前记:select模型主要用于解决tcp通信中,每次处理一个独立的客户都要单独的开线程,这样会导致客户连接数很大时,线程数也会很多。而使用select就会将线程缩减至2个,一个主线程用于接收Accept客户端的连接,另外一个子线程用于监听已经连接上来的客户端是否有信号状态,如果有信号则进行相应的消息处理。
服务端:
#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>//必须放在windows.h前面
#include <Windows.h>
#include <stdlib.h>
#pragma comment(lib, "ws2_32.lib")
fd_set g_fdClientSock;
int clientNum = 0;
DWORD WINAPI ListenThreadProc(LPARAM lparam)
{
fd_set fdRead;
FD_ZERO(&fdRead);
int nRet = 0;
char *recvBuffer = (char*)malloc(1024);
if (!recvBuffer)
{
return -1;
}
memset(recvBuffer, 0, 1024);
while (TRUE)
{
fdRead = g_fdClientSock;
timeval vt;
vt.tv_sec = 0;
vt.tv_usec = 0;
nRet = select(0, &fdRead, 0,0,&vt);//会阻塞检查集合中所有socket是否有信号
if (nRet != SOCKET_ERROR)
{
for (int i=0; i<g_fdClientSock.fd_count; i++)
{
if (FD_ISSET(g_fdClientSock.fd_array[i], &fdRead))
{
memset(recvBuffer, 0, 1024);
nRet = recv(g_fdClientSock.fd_array[i], recvBuffer, 1024, 0);
if (nRet >0)
{
//todo:
printf("接收到数据:%s", recvBuffer);
send(g_fdClientSock.fd_array[i], recvBuffer, strlen(recvBuffer), 0);
}
else//如果接收失败,则从集合中清除响应socket,并把客户端数量减1
{
closesocket(g_fdClientSock.fd_array[i]);
clientNum--;
FD_CLR(g_fdClientSock.fd_array[i], &g_fdClientSock);
}
}
}
}
}
if (recvBuffer)
{
free(recvBuffer);
recvBuffer=nullptr;
}
return 0;
}
void main()
{
int port = 5099;
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Failed to load Winsock");
return;
}
//创建用于监听的套接字 AF_INET:IPV4版本
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
if (sockSrv == INVALID_SOCKET)
{
return;
}
//地址绑定-告诉操作系统是在哪一个地址及端口
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(port); //1024以上的端口号,htons本地转换为网络数据
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//电脑上所有的网络ip
int retVal = bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));
if(retVal == SOCKET_ERROR)
{
printf("绑定bind失败:%d\n", WSAGetLastError());
return;
}
if(listen(sockSrv,5/*SOMAXCONN*/) ==SOCKET_ERROR)
{
printf("监听listen失败:%d", WSAGetLastError());
return;
}
SOCKADDR_IN addrClient;//用于获取连接上来的人的地址信息
int len = sizeof(SOCKADDR);
CreateThread(NULL,NULL, (LPTHREAD_START_ROUTINE)ListenThreadProc, NULL, NULL, NULL);
while(clientNum < FD_SETSIZE)
{
//等待客户请求到来
SOCKET clientSock = accept(sockSrv, (SOCKADDR *) &addrClient, &len);
if(clientSock == SOCKET_ERROR)
{
printf("接收Accept失败:%d", WSAGetLastError());
break;
}
else
{
printf("接收Accept到客户端IP:[%s]\n", inet_ntoa(addrClient.sin_addr));
}
FD_SET(clientSock, &g_fdClientSock);//添加到集合中去
clientNum++;//每次接收到一个人就+1,目前最多接收64个客户
}
closesocket(sockSrv);
WSACleanup();
system("pause");
}
客户端:
// Tcp_client.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
void main()
{
//加载套接字
WSADATA wsaData;
char buff[1024];
memset(buff, 0, sizeof(buff));
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Failed to load Winsock");
return;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(5099);//http默认端口
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//创建套接字
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
if(SOCKET_ERROR == sockClient){
printf("Socket() error:%d", WSAGetLastError());
return;
}
//向服务器发出连接请求
if(connect(sockClient, (struct sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET){
printf("Connect failed:%d", WSAGetLastError());
return;
}
int iRecvLen = 0;
//发送数据
char* buffSend = "hello, this is a Client....";
iRecvLen = send(sockClient, buffSend, strlen(buffSend), 0);
//接收数据
iRecvLen = recv(sockClient, buff, sizeof(buff), 0);
printf("%s\n", buff);
//关闭套接字
closesocket(sockClient);
WSACleanup();
system("pause");
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)