Windows socket之最简单的socket程序

Windows socket之最简单的socket程序
原文:Windows socket之最简单的socket程序

最简单的服务器的socket程序流程如下(面向连接的TCP连接 ):

1. WSAStartup(); 初始化网络库的使用。

2. socket(); 获得一个socket。

3. bind(); 把获得的socket绑定到一个ip 和端口。既然作为服务器, ip通常为本地IP127.0.0.1。

4. listen(); 监听已经绑定了指定端口的socket。

5. accept(); 接受一个来自客户端的连接。


accept()返回一个新的socket,该socket代表着本地服务器与某一个连接过来的客户端的链接。以该socket为参数,可以调用send函数往客户端发送数据,也可以调用recv函数接受客户端发送过来的函数。


最后服务器程序结束的时候调用closesocket()关闭socket, WSACleanup()终止网络库的使用,清理资源。



最简单的客户端的socket程序流程如下(同样是面向连接的TCP连接):

1. WSAStartup();初始化网络库的使用。

2. socket(); 获得一个socket。

3. connect(); 连接到一个 服务器。


连接成功后就可以收发数据了。收发完毕后调用closesocket()关闭socket,最后程序结束前调用 WSACleanup()清理资源。


下面直接上代码

需包含以下头文件和定义

#include <stdlib.h>
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

#define SERVE_ADDRESS "127.0.0.1"
#define SERVE_PORT    7001



	// ----------------------------       WSAStartup()         ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");goto Main_End;}//------------------------------------------------------------------------------//// ----------------------------         socket()         ----------------------------//SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == serverSocket){printf("failed to invoke socket, the socket returned is invalid!\n");goto Main_End;}// ------------------------------------------------------------------------------------////----------------------------           bind()          ----------------------------//// 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义SOCKADDR_IN localAddr;localAddr.sin_family = AF_INET;localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);	localAddr.sin_port = htons(SERVE_PORT);	memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));// int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));if(0 != resBind){printf("failed to bind ! \n");goto Main_End;}//------------------------------------------------------------------------------------////----------------------------          listen()         ----------------------------//int resListen = listen(serverSocket,5);if(0 != resListen){printf("failed to listen! \n");goto Main_End;}printf("the server is listening now!\n");//------------------------------------------------------------------------------------////----------------------------        accept()         ----------------------------//SOCKADDR_IN clientAddr;int addrLen = sizeof(clientAddr);SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);if(INVALID_SOCKET == acceptedSocket){printf("accept error!\n");goto Main_End;}printf("a client has connected to the server!\n");//------------------------------------------------------------------------------------//char recvBuffer[256];char sendBuffer[256];strcpy(sendBuffer,"server:Welcome to connect !");int sendBufLen = strlen(sendBuffer);int resSend = send(acceptedSocket,sendBuffer,sendBufLen,0);while(true){if(resSend != sendBufLen)	//发送的长度与需要发送的长度不等{printf("send data error!!\n");break;}int recvLen = recv(acceptedSocket,recvBuffer,sizeof(recvBuffer),0);if(0 == recvLen){printf("a client close the socket!\n");break;}else if(recvLen < 0){printf("an error has happen when receiving\n");break;}recvBuffer[recvLen] = '\0';printf("client:%s\n",recvBuffer);//在客户发过来的数据前面加上server:再发回给客户端strcpy(sendBuffer,"server:");strcat(sendBuffer,recvBuffer);sendBufLen = strlen(sendBuffer);resSend = send(acceptedSocket,sendBuffer,sendBufLen,0);}closesocket(acceptedSocket);closesocket(serverSocket);Main_End:WSACleanup();system("pause");return 0;




客户端代码:

	//----------------------------       WSAStartup()        ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");goto Main_End;}//------------------------------------------------------------------------------------////----------------------------       socket()           ----------------------------//SOCKET connSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == connSocket){printf("the socket returned is invalid!\n");goto Main_End;}//------------------------------------------------------------------------------------////----------------------------       connect()         ----------------------------////初始化struct sockaddr 结构体 SOCKADDR_IN serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);serverAddr.sin_port = htons(SERVE_PORT);memset(serverAddr.sin_zero,0x0,sizeof(serverAddr.sin_zero));//connectint resConn = connect(connSocket,(sockaddr*)&serverAddr,sizeof(serverAddr));if(0 != resConn){printf("failed to connect to server!!\n");goto Main_End;}//------------------------------------------------------------------------------------//char sendBuffer[256];char recvBuffer[256];while(true){int recvLen = recv(connSocket,recvBuffer,256,0);if(recvLen < 0){printf("receive error!!\n");break;}else if(0 == recvLen){printf("the server close the socket!\n");}recvBuffer[recvLen] = '\0';printf("the data recv:%s\n\n\n",recvBuffer);printf("please input what you want to send:\n");gets(sendBuffer);if(0 == strcmp(sendBuffer,"exit")){break;}int sendDataLen = strlen(sendBuffer);int nDataSent = send(connSocket,sendBuffer,sendDataLen,0);if(nDataSent != sendDataLen){printf("failed to send data!!\n");break;}}closesocket(connSocket);printf("the connection is closed!\n");Main_End:WSACleanup();system("pause");return 0;



客户端连接到服务端后,每次给服务端发送一段内容,服务器在内容前面加上server:再发送给客户端。

当客户端发送的内容是exit时,客户端程序跳出循环,关闭socket断开连接。服务端发现客户端断开连接后也关闭套接字结束程序。


当然上面程序只为了演示最简单的网络编程。有若干漏洞。

1. 服务器只能接受一个客户端连接。当然加一个循环语句进去可以重复地接受客户端的连接,但是仍然是每次只处理一个客户端连接。

2.accept, connect,send,recv函数默认均是阻塞函数。当没有客户连接到服务端时,服务端阻塞在accept函数,无法退出程序。当服务器在接受客户端的数据时,如果客户端不发送数据,也不断开连接,那么服务端阻塞在recv函数,无法退出程序。




改进该程序,使得服务端随时都可以停止服务退出程序,无论有多少个用户已经在连接。

为了多个客户端可以同时连接,最容易理解的便是利用多线程。每一个连接的客户端都用一个线程去处理它的通信。

至于为了随时可以退出服务端,不能再调用永久阻塞的函数了。利用select函数,可以阻塞指定的时间,阻塞期间不占CPU。


int select( __in int nfds, __in_out fd_set*readfds, __in_out fd_set*writefds, __in_out fd_set*exceptfds, __in const struct timeval*timeout);

nfds

用于兼容Berkeley sockets.不用理会,随便给个0值就OK。

readfds

用于检查是否存在可读socket的的一个socket集合。可为空。


writefds

用于检查是否存在可写socket的一个socket集合。可为空。

exceptfds

用于检查是否存在有错误的socket的一个 socket集合,可为空。

timeout

TIMEVAL结构体,用于指定该函数阻塞多长时间。

在 调用select时,当readfds不为空时,当readfds中任何一个socket就绪可读时,或者当writefds不为空且writefds中任何一个socket准备就绪可写,或者当exceptfds不为空且任何一个socket发生socket错误时,select就立即返回。否则,直到timeout指定的时间过去以后才返回。


返回值,返回准备就绪的socket的个数。如果为0,说明该函数超时了,如果大于0,说明至少有一个socket就绪。如果小于0,说明发生错误了。


fd_set 是一种集合类型。

typedef struct fd_set {
        u_int fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

记录着一个socket数组,以及里面的socket个数。


struct timeval是一个表示等待时间的结构体。

struct timeval {
        long    tv_sec;         /* seconds */
        long    tv_usec;        /* and microseconds */
};


tv_sec表示有多少秒,tv_usec表示有多少毫秒。


对于fd_set类型,用到几个宏定义函数。

FD_ZERO(fd_set*), 清空fd_set集合

FD_SET(SOCKET,fd_set*),把socket加入fd_set集合。

FD_ISSET(SOCKET,fd_set*),判断socket是否在集合fd_set中,并且socket准备就绪。

FD_CLR(SOCKET,fd_set*),如果fd_set存在该SOCKET,则移除它。


下面是改进后的服务端代码

typedef struct _ThreadInfo
{HANDLE hThread;bool bRunning;SOCKET sock;
}ThreadInfo;typedef struct _AcceptThreadParam
{bool bRunning;SOCKET listeningSocket;
}AcceptThreadParam;std::list<ThreadInfo*> g_threadInfoList;
CRITICAL_SECTION g_csForList;DWORD WINAPI ListeningThread(LPVOID lpParameter);
DWORD WINAPI CommunicationThread(LPVOID lpParameter);int _tmain(int argc, _TCHAR* argv[])
{_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// ----------------------------       WSAStartup()         ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");return -1;}//------------------------------------------------------------------------------//InitializeCriticalSection(&g_csForList);// ----------------------------         socket()         ----------------------------//SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == serverSocket){printf("failed to invoke socket, the socket returned is invalid!\n");goto Main_End;}// ------------------------------------------------------------------------------------////----------------------------           bind()          ----------------------------//// 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义SOCKADDR_IN localAddr;localAddr.sin_family = AF_INET;localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);	localAddr.sin_port = htons(SERVE_PORT);	memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));// int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));if(0 != resBind){printf("failed to bind ! \n");goto Main_End;}//------------------------------------------------------------------------------------////----------------------------          listen()         ----------------------------//int resListen = listen(serverSocket,5);if(0 != resListen){printf("failed to listen! \n");goto Main_End;}//------------------------------------------------------------------------------------//AcceptThreadParam threadParam;threadParam.bRunning = true;threadParam.listeningSocket = serverSocket;HANDLE hListeningThread = CreateThread(0,0,ListeningThread,&threadParam,0,0);if(0 == hListeningThread){printf("failed to create the listening thread!\n");goto Main_End;}else{printf("the server is listening now!pass any key to close the server!\n");}while(true){char ch = getchar();threadParam.bRunning = false;DWORD resWait = WaitForSingleObject(hListeningThread,3000);if(WAIT_TIMEOUT == resWait){printf("failed to wait for the listening thread exiting!\n");}else{printf("the listening thread has exited!\n");}break;}Main_End:if(INVALID_SOCKET != serverSocket){closesocket(serverSocket);serverSocket = INVALID_SOCKET;}WSACleanup();DeleteCriticalSection(&g_csForList);system("pause");return 0;
}DWORD WINAPI ListeningThread(LPVOID lpParameter)
{AcceptThreadParam* pAcceptThreadParam = (AcceptThreadParam*)lpParameter;SOCKET serverSocket = pAcceptThreadParam->listeningSocket;while(pAcceptThreadParam->bRunning){//----------------------------        accept()         ----------------------------//fd_set fdAccept;FD_ZERO(&fdAccept);FD_SET(serverSocket,&fdAccept);TIMEVAL acceptTimeVal;acceptTimeVal.tv_sec = 1;acceptTimeVal.tv_usec = 0;int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);if(selRes > 0){SOCKADDR_IN clientAddr;int addrLen = sizeof(clientAddr);SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);if(INVALID_SOCKET == acceptedSocket){printf("accept error!\n");break;}printf("a client has connected to the server!\n");ThreadInfo* pTI = new ThreadInfo;pTI->bRunning = true;pTI->sock = acceptedSocket;pTI->hThread = CreateThread(0,0,CommunicationThread,(LPVOID)pTI,0,0);if(0 == pTI->hThread){printf("failed to create a thread!\n");delete pTI;pTI = 0;}else{EnterCriticalSection(&g_csForList);g_threadInfoList.push_back(pTI);LeaveCriticalSection(&g_csForList);}}else if(selRes < 0){printf("an error has occured when listening !\n");break;}}std::list<ThreadInfo*> tempList;EnterCriticalSection(&g_csForList);std::list<ThreadInfo*>::iterator listIter;for(listIter = g_threadInfoList.begin(); listIter != g_threadInfoList.end(); listIter++){(*listIter)->bRunning = false;tempList.push_back(*listIter);}g_threadInfoList.clear();LeaveCriticalSection(&g_csForList);int nSuccessfullyExit = 0;for(listIter = tempList.begin(); listIter != tempList.end(); listIter++){DWORD resWait = WaitForSingleObject((*listIter)->hThread,2000);if(WAIT_TIMEOUT == resWait){printf("failed to wait for a communication thread exiting!\n");}else{nSuccessfullyExit++;}delete (*listIter);}printf("succeed waiting for %d thread exiting!\n",nSuccessfullyExit);tempList.clear();printf("listening thread is exiting!\n");return 0;
}DWORD WINAPI CommunicationThread(LPVOID lpParameter)
{ThreadInfo* pThreadInfo = (ThreadInfo*)lpParameter;SOCKET clientSocket = pThreadInfo->sock;fd_set fdRead,fdWrite;FD_ZERO(&fdRead);FD_ZERO(&fdWrite);FD_SET(clientSocket,&fdRead);FD_SET(clientSocket,&fdWrite);TIMEVAL sendTimeVal;sendTimeVal.tv_sec = 0;sendTimeVal.tv_usec = 500;int selRes = select(0,0,&fdWrite,0,&sendTimeVal);if(selRes <= 0){goto ThreadOver;}char recvBuffer[256];char sendBuffer[256];strcpy(sendBuffer,"server:Welcome to connect !");int sendBufLen = strlen(sendBuffer);int resSend = send(clientSocket,sendBuffer,sendBufLen,0);if(resSend != sendBufLen){printf("there are %d bytes to send, but it just succeeded sending %d bytes!\n",sendBufLen,resSend);goto ThreadOver;}while(pThreadInfo->bRunning){	FD_ZERO(&fdRead);FD_SET(pThreadInfo->sock,&fdRead);TIMEVAL recvTimeVal;recvTimeVal.tv_sec = 0;recvTimeVal.tv_usec = 500;int recvSelRes = select(0,&fdRead,0,0,&recvTimeVal);if(recvSelRes < 0){printf("socket error when receiving!\n");break;}else if(recvSelRes > 0){int recvLen = recv(clientSocket,recvBuffer,sizeof(recvBuffer),0);if(0 == recvLen){printf("a client close the socket!\n");break;}else if(recvLen < 0){printf("an error has happen when recving\n");break;}else{recvBuffer[recvLen] = '\0';printf("a client:%s\n",recvBuffer);strcpy(sendBuffer,"server:");strcat(sendBuffer,recvBuffer);sendBufLen = strlen(sendBuffer);FD_ZERO(&fdWrite);FD_SET(pThreadInfo->sock,&fdWrite);sendTimeVal.tv_sec = 0;sendTimeVal.tv_usec = 500;int sendSelRes = select(0,0,&fdWrite,0,&sendTimeVal);if(sendSelRes > 0){int bytesSent = send(clientSocket,sendBuffer,sendBufLen,0);if(bytesSent != sendBufLen){printf("there are %d bytes to be sent,but only %d bytes are sent!\n",sendBufLen,bytesSent);break;}}else{printf("failed to send in 500 ms!\n");break;}}}}ThreadOver:closesocket(pThreadInfo->sock);bool bMainThreadWaiting = true;EnterCriticalSection(&g_csForList);std::list<ThreadInfo*>::iterator listIter;for(listIter = g_threadInfoList.begin(); listIter != g_threadInfoList.end(); listIter++){if(pThreadInfo == (*listIter)){bMainThreadWaiting = false;g_threadInfoList.erase(listIter);break;}}LeaveCriticalSection(&g_csForList);if(false == bMainThreadWaiting){CloseHandle(pThreadInfo->hThread);delete pThreadInfo;pThreadInfo = 0;}return 0;
}


前面的代码与之前的一样,改变的地方在于accept的地方。对于一个监听的socket,如果该socket可读,说明有用户连接过来了。

全局维护了一个纪录创建的线程的信息的链表,每创建一个线程都有一个标识该线程是否应该继续循环执行的bool变量。当bRunning变为false的时候,线程函数跳出循环,返回。


当需要停止服务端运行时,服务端只需要按任何一个键和回车,就会通知线程退出,并且调用WaitForSingleObject(),来确认线程已退出。还有利用了 EnterCriticalSection()和LeaveCriticalSection()临界区函数来保证只有一个线程在操作全局的链表。




使用多线程要消耗一定的资源。对于fd_set,默认最多可以容纳64个socket.所以可以用1个线程去处理64个客户端的连接。而不必每个客户端都创建一个线程。

代码如下:

typedef struct _AcceptThreadParam
{bool bRunning;SOCKET listeningSocket;
}AcceptThreadParam;#define SOCKET_ARRAY_SIZE 64SOCKET g_socketArray[SOCKET_ARRAY_SIZE];
int g_socketCount = 0;
CRITICAL_SECTION g_csForSocketArray;DWORD WINAPI ListeningThread(LPVOID lpParameter);
DWORD WINAPI CommunicationThread(LPVOID lpParameter);int _tmain(int argc, _TCHAR* argv[])
{_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// ----------------------------       WSAStartup()         ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");return -1;}//------------------------------------------------------------------------------//InitializeCriticalSection(&g_csForSocketArray);g_socketCount = 0;// ----------------------------         socket()         ----------------------------//SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == serverSocket){printf("failed to invoke socket, the socket returned is invalid!\n");goto Main_End;}// ------------------------------------------------------------------------------------////----------------------------           bind()          ----------------------------//// 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义SOCKADDR_IN localAddr;localAddr.sin_family = AF_INET;localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);	localAddr.sin_port = htons(SERVE_PORT);	memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));// int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));if(0 != resBind){printf("failed to bind ! \n");goto Main_End;}//------------------------------------------------------------------------------------////----------------------------          listen()         ----------------------------//int resListen = listen(serverSocket,5);if(0 != resListen){printf("failed to listen! \n");goto Main_End;}//------------------------------------------------------------------------------------//AcceptThreadParam threadParam;threadParam.bRunning = true;threadParam.listeningSocket = serverSocket;bool bCommunicationThreadRunning = true;HANDLE hListeningThread = CreateThread(0,0,ListeningThread,&threadParam,0,0);HANDLE hCommunicationThread = CreateThread(0,0,CommunicationThread,&bCommunicationThreadRunning,0,0);if(0 == hListeningThread || 0 == hCommunicationThread){printf("failed to create a thread!\n");if(0 != hListeningThread){threadParam.bRunning = false;WaitForSingleObject(hListeningThread,2000);CloseHandle(hListeningThread);}if(0 != hCommunicationThread){bCommunicationThreadRunning = false;WaitForSingleObject(hCommunicationThread,2000);CloseHandle(hCommunicationThread);}goto Main_End;}else{printf("the server is listening now!pass any key to close the server!\n");}while(true){char ch = getchar();threadParam.bRunning = false;bCommunicationThreadRunning = false;DWORD resWait = WaitForSingleObject(hListeningThread,3000);if(WAIT_TIMEOUT == resWait){printf("failed to wait for the listening thread exiting!\n");}else{printf("the listening thread has exited!\n");}CloseHandle(hListeningThread);resWait = WaitForSingleObject(hCommunicationThread,3000);if(WAIT_TIMEOUT == resWait){printf("failed to wait for the communication thread exiting!\n");}else{printf("the communication thread has exited!\n");}CloseHandle(hCommunicationThread);break;}Main_End:if(INVALID_SOCKET != serverSocket){closesocket(serverSocket);serverSocket = INVALID_SOCKET;}WSACleanup();DeleteCriticalSection(&g_csForSocketArray);system("pause");return 0;
}DWORD WINAPI ListeningThread(LPVOID lpParameter)
{AcceptThreadParam* pAcceptThreadParam = (AcceptThreadParam*)lpParameter;SOCKET serverSocket = pAcceptThreadParam->listeningSocket;while(pAcceptThreadParam->bRunning){//----------------------------        accept()         ----------------------------//fd_set fdAccept;FD_ZERO(&fdAccept);FD_SET(serverSocket,&fdAccept);TIMEVAL acceptTimeVal;acceptTimeVal.tv_sec = 1;acceptTimeVal.tv_usec = 0;int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);if(selRes > 0){SOCKADDR_IN clientAddr;int addrLen = sizeof(clientAddr);SOCKET acceptedSocket = accept(serverSocket,(sockaddr*)&clientAddr,&addrLen);if(INVALID_SOCKET == acceptedSocket){printf("accept error!\n");break;}printf("a client has connected to the server!\n");fd_set fdWrite;FD_ZERO(&fdWrite);FD_SET(acceptedSocket,&fdWrite);TIMEVAL writeTimeVal;writeTimeVal.tv_sec = 0;writeTimeVal.tv_usec = 500;int writeSelRes = select(0,0,&fdWrite,0,&writeTimeVal);if(writeSelRes > 0){int sendBufferLen = strlen("server:Welcome to connect!");int bytesSent = send(acceptedSocket,"server:Welcome to connect!",sendBufferLen,0);if(bytesSent == sendBufferLen){EnterCriticalSection(&g_csForSocketArray);if(g_socketCount < 64){g_socketArray[g_socketCount] = acceptedSocket;g_socketCount++;}else{printf("the server has accepted more than 64 clients!\n");closesocket(acceptedSocket);}LeaveCriticalSection(&g_csForSocketArray);}else{printf("send error, there are %d bytes to be sent, but only %d bytes are sent!\n",sendBufferLen,bytesSent);closesocket(acceptedSocket);}}else{printf("select error of can not wait for sending data when select!\n");closesocket(acceptedSocket);}}else if(selRes < 0){printf("an error has occured when listening !\n");break;}}printf("listening thread is exiting!\n");return 0;
}DWORD WINAPI CommunicationThread(LPVOID lpParameter)
{bool* pBRunning = (bool*)lpParameter;char recvBuffer[256];char tempBuffer[256];while(true == *pBRunning){int currentSocketCount = 0;EnterCriticalSection(&g_csForSocketArray);if(0 == g_socketCount){LeaveCriticalSection(&g_csForSocketArray);Sleep(200);continue;}currentSocketCount = g_socketCount;LeaveCriticalSection(&g_csForSocketArray);fd_set fdRead;FD_ZERO(&fdRead);for(int i = 0; i < currentSocketCount; i++){FD_SET(g_socketArray[i],&fdRead);}TIMEVAL readTimeVal;readTimeVal.tv_sec = 1;readTimeVal.tv_usec = 0;int selRes = select(0,&fdRead,0,0,&readTimeVal);if(selRes > 0){for(int i = 0; i < currentSocketCount; i++){if(FD_ISSET(g_socketArray[i],&fdRead) != 0){int bytesRecv = recv(g_socketArray[i],recvBuffer,sizeof(recvBuffer),0);if(bytesRecv > 0){recvBuffer[bytesRecv] = '\0';printf("the %d client: %s\n",i + 1,recvBuffer);sprintf(tempBuffer,"the server:%s",recvBuffer);fd_set fdWrite;FD_ZERO(&fdWrite);FD_SET(g_socketArray[i],&fdWrite);TIMEVAL writeTimeVal;writeTimeVal.tv_sec = 0;writeTimeVal.tv_usec = 500;int writeSelRes = select(g_socketArray[i],0,&fdWrite,0,&writeTimeVal);if(writeSelRes > 0){int sendBufLen = strlen(tempBuffer);int bytesSent = send(g_socketArray[i],tempBuffer,sendBufLen,0);if(bytesSent == sendBufLen){break;}else{printf("there are %d bytes to be sent,but only %d bytes are sent!\n",sendBufLen,bytesSent);}}else{printf("select error!\n");}}else if(0 == bytesRecv){printf("the %d client has closed the socket!\n",i + 1);}else{printf("recv error!\n");}closesocket(g_socketArray[i]);EnterCriticalSection(&g_csForSocketArray);g_socketArray[i] = g_socketArray[g_socketCount - 1];g_socketCount--;LeaveCriticalSection(&g_csForSocketArray);}}}else if(selRes < 0){printf("select error in communication thread!\n");}}EnterCriticalSection(&g_csForSocketArray);for(int i = 0; i < g_socketCount; i++){closesocket(g_socketArray[i]);}LeaveCriticalSection(&g_csForSocketArray);printf("the communication thread is exiting!\n");return 0;
}

完成的功能一样。只需要一个线程就可以处理多个客户端了。




还可以用异步IO来实现该服务器,以下是用完成端口来实现同样功能的服务器。

typedef struct _RepeatAcceptingThreadParam
{SOCKET listeningSocket;bool* pBRunning;
}RepeatAcceptingThreadParam;typedef struct _CompletionPortWorkerThreadParam
{HANDLE hCompletionPort;bool* pBRunning;
}CompletionPortWorkerThreadParam;#define MESSAGE_BUF_SIZE 1024enum OPERATION_TYPE
{OPERATION_SEND,OPERATION_RECV
};typedef struct
{SOCKET sock;WSAOVERLAPPED overlap;WSABUF wsaBuf;char message[1024];DWORD bytesRecv;DWORD flags;OPERATION_TYPE operationType;
}PER_IO_OPERATION_DATA;//global vector, which saves the information of the client sockets connected to the server
std::vector<PER_IO_OPERATION_DATA*> g_perIoDataPointerVec;//accept sockets connected to the server's listening socket in a recycle - while
DWORD WINAPI RepeatAcceptingThread(LPVOID lpParameter);	//the worker thread that deal with the communications between the server and the clients.
DWORD WINAPI CompletionPortWorkerThread(LPVOID lpParameter);int _tmain(int argc,_TCHAR* argv[])
{_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// ----------------------------       WSAStartup()         ----------------------------//WSADATA wsd;int resStartup = WSAStartup(MAKEWORD(2,2),&wsd);if(0 != resStartup){printf("failed to WSAStartup!\n");return -1;}//------------------------------------------------------------------------------//// ----------------------------         socket()         ----------------------------//SOCKET serverSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(INVALID_SOCKET == serverSocket){printf("failed to invoke socket, the socket returned is invalid!\n");return -1;}// ------------------------------------------------------------------------------------////----------------------------           bind()          ----------------------------//// 初始化 struct sockaddr 结构体, SOCKADDR_IN就是 struct sockaddr的宏定义SOCKADDR_IN localAddr;localAddr.sin_family = AF_INET;localAddr.sin_addr.S_un.S_addr = inet_addr(SERVE_ADDRESS);	localAddr.sin_port = htons(SERVE_PORT);	memset(localAddr.sin_zero,0x0,sizeof(localAddr.sin_zero));// int resBind = bind(serverSocket,(sockaddr*)&localAddr,sizeof(SOCKADDR_IN));if(0 != resBind){printf("failed to bind ! \n");closesocket(serverSocket);return -1;}//------------------------------------------------------------------------------------////----------------------------          listen()         ----------------------------//int resListen = listen(serverSocket,5);if(0 != resListen){printf("failed to listen! \n");closesocket(serverSocket);return -1;}//------------------------------------------------------------------------------------//bool bRepeatAcceptingThreadRunning = true;	// a bool variable that take control of terminating the RepeatAcceptingThread.//init the parameter for the RepeatAcceptingThread.RepeatAcceptingThreadParam rtp;rtp.listeningSocket = serverSocket;rtp.pBRunning = &bRepeatAcceptingThreadRunning;HANDLE hRepeatAcceptingThread = CreateThread(0,0,RepeatAcceptingThread,&rtp,0,0);if(0 == hRepeatAcceptingThread){printf("failed to create the repeat-accepting thread!\n");closesocket(serverSocket);return -1;}printf("the repeat-accepting thread has run!\n");while(true){// pass any key char ch = getchar();bRepeatAcceptingThreadRunning = false;//to notify the RepeatAcceptingThread to exit safely DWORD waitRes = WaitForSingleObject(hRepeatAcceptingThread,3000);if(WAIT_TIMEOUT == waitRes){printf("failed to wait for the repeatAcceptingThread exiting!\n");}else{printf("the repeat accepting thread has exited!\n");}CloseHandle(hRepeatAcceptingThread);break;}system("pause");return 0;
}DWORD WINAPI RepeatAcceptingThread(LPVOID lpParameter)
{//get the parameters passed by the creator of the thread.RepeatAcceptingThreadParam* pParam = (RepeatAcceptingThreadParam*)lpParameter;SOCKET listeningSocket = pParam->listeningSocket;bool* pStillRun = pParam->pBRunning;// create a completion portHANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);if(0 == hCompletionPort){printf("failed to CreateIoCompletionPort!\n");return -1;}// a bool variable for notifying the worker threads of exiting.bool bWorkThreadRunning = true;// a vector of HANDLEs,which will be used for synchronization of waiting the worker threads to exit.std::vector<HANDLE> threadHandlesVec;SYSTEM_INFO systemInfo;GetSystemInfo(&systemInfo);//the parameter to be passed to the worker thread.CompletionPortWorkerThreadParam cpwtp;cpwtp.pBRunning = &bWorkThreadRunning;cpwtp.hCompletionPort = hCompletionPort;for(int i = 0; i < systemInfo.dwNumberOfProcessors; i++){HANDLE hThread = CreateThread(0,0,CompletionPortWorkerThread,&cpwtp,0,0);if(0 == hThread)	{printf("failed to create a completion port worker thread!\n");bWorkThreadRunning = false;// terminate all threads created safely.std::vector<HANDLE>::iterator vecIter;for(vecIter = threadHandlesVec.begin(); vecIter != threadHandlesVec.end(); vecIter++){DWORD waitRes = WaitForSingleObject(*vecIter,2000);if(WAIT_TIMEOUT == waitRes){printf("failed the wait for the completion port worker thread!\n");}CloseHandle(*vecIter);}threadHandlesVec.clear();CloseHandle(hCompletionPort);return -1;}else{threadHandlesVec.push_back(hThread);	//add the handle to the vector}}printf("succeed creating completion port worker threads!\n");while(true == *pStillRun){fd_set fdAccept;FD_ZERO(&fdAccept);FD_SET(listeningSocket,&fdAccept);TIMEVAL acceptTimeVal;acceptTimeVal.tv_sec = 1;acceptTimeVal.tv_usec = 0;int selRes = select(0,&fdAccept,0,0,&acceptTimeVal);if(selRes > 0)	// a client connected{SOCKADDR_IN clientAddr;int addrLen = sizeof(clientAddr);SOCKET acceptedSocket = WSAAccept(listeningSocket,(struct sockaddr*)&clientAddr,&addrLen,0,0);if(0 == acceptedSocket){printf("failed to accept a connection!\n");}else{printf("a clent %s:%d has connected!\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));PER_IO_OPERATION_DATA* perIoData = new PER_IO_OPERATION_DATA;if(0 == perIoData){closesocket(acceptedSocket);printf("failed to new a struct! there is not enough memory!\n\n");}else{//associate the newly connected client socket with the completion port.if(0 == CreateIoCompletionPort((HANDLE)acceptedSocket,hCompletionPort,(ULONG_PTR)perIoData,0)){printf("failed to associate the newly connected client socket with the completion port!\n");closesocket(acceptedSocket);delete perIoData;perIoData = 0;}else{//associated successfully, Set the information of the client socket in A PER_IO_OPERATION_DATA struct.//when a IO operation is completed, we can get notified with the struct to be one of the parameters.perIoData->sock = acceptedSocket;perIoData->operationType = OPERATION_SEND;perIoData->wsaBuf.buf = perIoData->message;perIoData->overlap.hEvent = INVALID_HANDLE_VALUE;strcpy(perIoData->message,"Welcome to connect to the server!");perIoData->wsaBuf.len = strlen(perIoData->message);int sendRes = WSASend(acceptedSocket,&(perIoData->wsaBuf),1,&(perIoData->bytesRecv),0,0,0);if(0 == sendRes)	//finished immediately{// asynchronously invoke a receive operation. When the reception finished,we can get its information by// invoking GetQueuedCompletionStatus()perIoData->wsaBuf.buf = perIoData->message;perIoData->wsaBuf.len = MESSAGE_BUF_SIZE;perIoData->flags = 0;perIoData->operationType = OPERATION_RECV;ZeroMemory(&perIoData->overlap,sizeof(perIoData->overlap));int recvRes = WSARecv(acceptedSocket,&perIoData->wsaBuf,1,&perIoData->bytesRecv,&perIoData->flags,&perIoData->overlap,0);if(0 == recvRes)	//the receiving operation finished immediately , the information of the operation has been queued.{g_perIoDataPointerVec.push_back(perIoData);}else if(SOCKET_ERROR == recvRes && WSA_IO_PENDING == WSAGetLastError())	//the receiving operation will finish later{g_perIoDataPointerVec.push_back(perIoData);}else{printf("failed to WSARecv!\n");closesocket(acceptedSocket);delete perIoData;perIoData = 0;}}else if(SOCKET_ERROR == sendRes && WSA_IO_PENDING == WSAGetLastError())		//the sending operation will finish later{g_perIoDataPointerVec.push_back(perIoData);}else{//int lastErr = WSAGetLastError();printf("send data error!\n");closesocket(acceptedSocket);delete perIoData;perIoData = 0;}}}}}else if(selRes < 0){printf("select error!\n");}}bWorkThreadRunning = false;	//notifies the worker threads of exiting// terminate all threads created safely.std::vector<HANDLE>::iterator vecIter;for(vecIter = threadHandlesVec.begin(); vecIter != threadHandlesVec.end(); vecIter++){DWORD waitRes = WaitForSingleObject(*vecIter,2000);if(WAIT_TIMEOUT == waitRes){printf("failed the wait for the completion port worker thread!\n");}CloseHandle(*vecIter);}threadHandlesVec.clear();CloseHandle(hCompletionPort);//delete the structs of PER_IO_OPERATION_DATA newed for clients connected.std::vector<PER_IO_OPERATION_DATA*>::iterator pIoDataPointerIter;for(pIoDataPointerIter = g_perIoDataPointerVec.begin(); pIoDataPointerIter != g_perIoDataPointerVec.end(); pIoDataPointerIter++){closesocket((*pIoDataPointerIter)->sock);delete (*pIoDataPointerIter);*pIoDataPointerIter = 0;}g_perIoDataPointerVec.clear();printf(" the repeat accepting thread is exiting!\n");return 0;
}bool ReleaseIOOperationData(PER_IO_OPERATION_DATA* & pDataToBeDeleted)
{bool retVal = false;std::vector<PER_IO_OPERATION_DATA*>::iterator vecIter;for(vecIter = g_perIoDataPointerVec.begin(); vecIter != g_perIoDataPointerVec.end(); vecIter++){if(pDataToBeDeleted == (*vecIter)){g_perIoDataPointerVec.erase(vecIter);closesocket(pDataToBeDeleted->sock);delete pDataToBeDeleted;pDataToBeDeleted = 0;retVal = true;break;}}return retVal;
}DWORD WINAPI CompletionPortWorkerThread(LPVOID lpParameter)
{CompletionPortWorkerThreadParam* pParam = (CompletionPortWorkerThreadParam*)lpParameter;bool* pStillRun = pParam->pBRunning;HANDLE hCompletionPort = pParam->hCompletionPort;DWORD dwBytesTransfered;PER_IO_OPERATION_DATA* pIoData;WSAOVERLAPPED* pOverlap;while(true == *pStillRun){dwBytesTransfered = 0;pIoData = 0;pOverlap = 0;BOOL bGetStatus = GetQueuedCompletionStatus(hCompletionPort,&dwBytesTransfered,(PULONG_PTR)&pIoData,&pOverlap,500);if(FALSE == bGetStatus){if(0 == pOverlap)	//did not get a packet from the queue.{continue;	}else{//get a packet for a failed I/O operation.}}if(OPERATION_SEND == pIoData->operationType){if(0 == dwBytesTransfered)	//a packet for a failed I/O operation.{printf("the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}else{// receive operation.pIoData->operationType = OPERATION_RECV;pIoData->wsaBuf.buf = pIoData->message;pIoData->wsaBuf.len = MESSAGE_BUF_SIZE;pIoData->flags = 0;ZeroMemory(&pIoData->overlap,sizeof(pIoData->overlap));int recvRes = WSARecv(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,&pIoData->flags,&pIoData->overlap,0);if(0 != recvRes && WSA_IO_PENDING != WSAGetLastError()){printf("recv error, may be the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}}}else if(OPERATION_RECV == pIoData->operationType){if(0 == dwBytesTransfered)	//a packet for a failed I/O operation.{printf("the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}else{// show the data receivedpIoData->message[dwBytesTransfered] = '\0';printf("the client %d:%s \n",pIoData->sock,pIoData->message);//send back the data received add a "server:" in the frontchar tempBuf[MESSAGE_BUF_SIZE];sprintf(tempBuf,"server:%s",pIoData->message);strcpy(pIoData->message,tempBuf);pIoData->operationType = OPERATION_SEND;pIoData->wsaBuf.buf = pIoData->message;pIoData->wsaBuf.len = strlen(pIoData->message);int sendRes = WSASend(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,0,0,0);if(0 == sendRes){pIoData->operationType = OPERATION_RECV;pIoData->wsaBuf.buf = pIoData->message;pIoData->wsaBuf.len = MESSAGE_BUF_SIZE;pIoData->flags = 0;ZeroMemory(&pIoData->overlap,sizeof(pIoData->overlap));int recvRes = WSARecv(pIoData->sock,&pIoData->wsaBuf,1,&pIoData->bytesRecv,&pIoData->flags,&pIoData->overlap,0);if(0 != recvRes && WSA_IO_PENDING != WSAGetLastError()){printf("recv error, may be the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}}else if(SOCKET_ERROR == sendRes && WSA_IO_PENDING == WSAGetLastError()){}else{printf("send error, maybe the client %d has close the socket!\n",pIoData->sock);ReleaseIOOperationData(pIoData);}}}}printf("a completion port thread is exiting!\n");return 0;
}


posted on 2014-12-16 08:17 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/4166277.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/292269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开源与自由 | 商业自由:从边缘到核心贡献

文 | 肖滢策划 | h4cd出品 | OSC开源社区&#xff08;ID&#xff1a;oschina2013&#xff09;当我们谈论开源时&#xff0c;很少谈论自由&#xff0c;尽管开源与自由同行。从 1998 年开源兴起时&#xff0c;我们就无法把开源和自由分割开来。因为它孕育于自由软件运动&#xff…

表面上在帮女朋友拍照,其实镜头瞄准的是......

1 哈哈哈哈哈&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼2 永远喝不完的牛奶&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼3 我的电动车不见了&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼4 这次的事就拜托了&#xff08;素材来源网络&…

用递归实现字符数组的反转

用递归实现字符数组的反转 比如 char a[10] {a, b, c}; 用递归实现后打印为 ‘c’, b, a 代码实现&#xff1a; #include<stdio.h> #include<string.h>void reverse(int length, char a[]);int main(){char a[10] {a, b, c, d, e};int length strlen(a);reve…

pandas添加一行数据_恨晚,Python探索性数据分析神器pandas-profiling,一行代码搞定...

我们使用Pandas进行数据分析时&#xff0c;首先要先对数据集进行探索性数据分析(Exploratory data analysis)&#xff0c;以便有一个大体的了解&#xff0c;明确后续数据处理、分析方向&#xff0c;数据EDA大致包含如下内容&#xff1a;感知数据的直观表现 挖掘潜在的结构 提取…

建立简单的套接字

最近发现学计算机的真的要好好做笔记啊。。。前两天有个学长的聊天工具我是知道没有初始化套接字&#xff0c;可是一时竟然忘记了加载版本库的那几行代码&#xff0c;真是汗。。。硬是回来看自己的程序才知道。。。 今晚复习了一下套接字的建立 有服务端和客服端两个部分 服务端…

对KVC和KVO的理解

对KVC和KVO的理解 对KVC和KVO的理解 kvc kvo KVC KVC是KeyValueCoding的简称&#xff0c;它是一种可以直接通过字符串的名字(key)来访问类属性的机制。而不是通过调用Setter、Getter方法访问。 KVC实例 一个对象拥有某些属性。比如说&#xff0c;一个Person对象有一个name和一个…

Natasha 4.0 探索之路系列(一) 概况

简介Natasha 是一个基于 Roslyn 的动态编译类库&#xff0c;它以极简的 API 完成了动态编译的大部分功能&#xff0c;使用它可以在程序运行时编译出新的程序集。Natasha 允许开发人员直接使用 C# 代码即可编写运行时的功能&#xff0c;避免了 Emit 的学习、开发、维护的成本。N…

相信应该有百分九十的男生看见这个东西是这个状态吧?

1 8400亿人民币是什么概念&#xff1f;&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼2 神奇的翻译&#xff08;素材来源豆瓣&#xff0c;侵删&#xff09;▼3 医生为什么喜欢把手背在后面呢&#xff1f;&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼4 …

postman安装_Postman插件的应用与实战(二)

在postman插件的应用与实战(一)中&#xff0c;介绍了postman插件的安装&#xff0c;使用&#xff0c;collestion的创建以及应用&#xff0c;本小节中&#xff0c;我们来介绍postman结合newman和jenkins持续构建工具&#xff0c;来对postman中的接口测试进行统一的管理。关于new…

struts2:JSP页面及Action中获取HTTP参数(parameter)的几种方式

本文演示了JSP中获取HTTP参数的几种方式&#xff0c;还有action中获取HTTP参数的几种方式。 1. 创建JSP页面&#xff08;testParam.jsp&#xff09; <% page language"java" import"java.util.*" pageEncoding"utf-8"%> <% page isELIg…

HTTP—缓存

1. ETag HTTP 1.1中引入了ETag来解决缓存的问题。ETag全称是Entity Tag&#xff0c;由服务端生成&#xff0c;服务端可以决定它的生成规则。如果根据文件内容生成散列值。那么条件请求将不会受到时间戳的改动造成带宽浪费。下面是根据内容生成散列值的方法&#xff1a; 1 var g…

python3随记——字符编码

1.1什么是字节 字节&#xff08;Byte&#xff09;是计算机信息技术用于计量存储容量的一种计量单位&#xff0c;也表示一些计算机编程语言中的数据类型和语言字符。 比特&#xff08;bit&#xff09;在计算机中最小的单位&#xff0c;在二进制位的电脑的系统中&#xff0c;每一…

数据结构之线性查找和折半查找

1、线性查找 比如字符串 char s[] = "chenyu"; 如果我们是线性查找的话,就是从字符‘c’依次到字符串结尾‘u’查找 2、折半查找 注意查找之前必须是有序的 比如整形数组 int a[10] = {1, 2, 7, 9, 10}; 查找数字2 我们可以定义 首和尾巴,拿需要查找的数据和…

盖茨被逐出微软董事会真相曝光:长期跟员工搞地下情,27年婚姻中出轨不断,人设已崩...

全世界只有3.14 % 的人关注了爆炸吧知识转自&#xff1a;量子位作者&#xff1a;梦晨 鱼羊2020年3月&#xff0c;在比尔盖茨辞去微软董事会职务的时候&#xff0c;人们都在感慨一代互联网大拿&#xff0c;纷纷都到了交接班的时候。万万没想到&#xff0c;一年多之后&#xff0c…

Natasha 4.0 探索之路系列(二) 「域」与插件

域与ALC在 Natasha 发布之后有不少小伙伴跑过来问域相关的问题&#xff0c;能不能兼容 AppDomain、如何使用 AppDomain、为什么 CoreAPI 阉割了 AppDomain 等一系列的问题。今天答复一下&#xff1a;首先 AppDomain 作为程序集隔离容器的存在&#xff0c;是风靡了 .NET Framewo…

cake fork什么意思_Java7任务并行执行神器:Forkamp;Join框架

Fork/Join是什么&#xff1f;Fork/Join框架是Java7提供的并行执行任务框架&#xff0c;思想是将大任务分解成小任务&#xff0c;然后小任务又可以继续分解&#xff0c;然后每个小任务分别计算出结果再合并起来&#xff0c;最后将汇总的结果作为大任务结果。其思想和MapReduce的…

php xml常用函数的集合及四种方法

1、DOM 函数 a、DOMDocument->load()作用&#xff1a;加载xml文件用法&#xff1a;DOMDocument->load( string filename )参数&#xff1a;filename&#xff0c;xml文件&#xff1b;返回&#xff1a;如果成功则返回 TRUE&#xff0c;失败则返回 FALSE。 b、DOMDocument-&…

linux C语言之called object ‘maze’ is not a function or function pointer printf(“%d\t“, maze(i, j))

今天写广度优先搜索的时候出现了这个问题&#xff0c; 解决办法&#xff1a; 特么我傻逼了&#xff0c;明显是数组&#xff0c;我写成了mate(i, j),然后我改了写成了mate[i, j]; 特么我又傻逼了 改成mate[i][j] 就可以了

drozer

安装drozer 1. 准备环境 1&#xff09;JRE / JDK 2) Android SDK 3) Python 2.7 Path, abd和java的环境变量 2. windows 下 安装drozer drozer-installer-2.3.3.zip adb install agent.apk //drozer agent 启动服务端口 adb forward tcp:31415 tcp:31415drozer.bat co…