首页 > 代码库 > 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));		//connect	int 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 serverstd::vector<PER_IO_OPERATION_DATA*> g_perIoDataPointerVec;//accept sockets connected to the server‘s listening socket in a recycle - whileDWORD 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 port	HANDLE 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 = http://www.mamicode.com/new PER_IO_OPERATION_DATA;>

Windows socket之最简单的socket程序