Windows socket of the most simple socket program

The most simple socket server program flow is as follows (TCP connection-oriented connection):

1. WSAStartup (); library used to initialize the network.

2. socket (); obtaining a socket.

3. bind (); bind the socket to obtain a ip and port. Now as a server, ip typically Local IP127.0.0.1.

4. listen (); listening socket is already bound specified port.

5. accept (); accepts connections from a client.


accept () returns a new socket, the socket represents the local server and one client connection over the end of the link. As a parameter to the socket, you can call the send function to send data to the client, you can also call the recv function takes over the function of the client sends.


Finally, the server program ends when the call closesocket () close the socket, WSACleanup () library using a network termination, cleaning up resources.



The most simple socket client program flow is as follows (the same connection-oriented TCP connections):

1. WSAStartup (); library used to initialize the network.

2. socket (); obtaining a socket.

3. connect (); connect to a server.


You can send and receive data after the connection is successful. After the completion of the transceiver call closesocket () close the socket, call WSACleanup before the end of the last program () to clean up resources.


The following directly on the code

It must contain the following header files and definitions

#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;




Client code:

	//----------------------------       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;



After the client connects to the server, each piece of content transmitted to the server, the server in the foregoing content plus server: re-sent to the client.

When the content client is transmitted exit, the client program out of the loop, the socket is closed to disconnect. Server found a client disconnects also close the socket end of the program.


Of course, the above procedure only to demonstrate the most simple network programming. There are several loopholes.

1. The server can accept a client connection. Of course, you can add a loop to go repeatedly to accept the client's connection, but still handle only one client connection.

2.accept, connect, send, recv function are default blocking function. When there is no client connected to the server, the server blocked in accept function, can not exit the program. When the server receives data at the client, if the client does not send data, do not disconnect, the server blocked recv function, you can not exit the program.




Improve the program so that the server can stop the service at any time to exit the program, regardless of how many users are already connected.

For multiple clients can connect, and easiest to understand is the use of multi-threading. Each client is connected to its communication with a processing thread.

As can be taken out of service at any time to end, you can not call a function of the permanent blocking. Use the select function, can block the specified time, CPU does not account for the blocking period.


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

For compatibility with Berkeley sockets. It does not matter, just give a zero value on OK.

readfds

For checking whether there is a socket of a socket-readable collection. It can be empty.


writefds

Used to check if there is a socket set may write the socket. It can be empty.

exceptfds

For checking whether there is an error in the socket a socket set may be empty.

timeout

TIMEVAL structure, used for specifying the function blocks long.

When calling select, when readfds not empty, when a socket is ready readfds in any readable, or any of a socket is ready to write when writefds not empty and writefds in, or when exceptfds is not empty and any socket when the socket error occurs, select returns immediately. Otherwise, until after a specified timeout period elapsed before returning.


The return value, returns the number of ready socket. If it is 0, the function times out, and if more than 0, indicating that at least one socket is ready. If less than 0, indicating that the error occurred.


fd_set a collection type.

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

Record array, and the number inside the socket a socket.


struct timeval structure is a waiting time representation.

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


tv_sec indicates how many seconds, tv_usec indicates how many milliseconds.


For fd_set type, used in several macro-defined functions.

FD_ZERO (fd_set *), a collection of empty fd_set

FD_SET (SOCKET, fd_set *), the socket join fd_set collection.

FD_ISSET (SOCKET, fd_set *), determine whether the collection fd_set socket, and the socket is ready.

FD_CLR (SOCKET, fd_set *), if there is fd_set the SOCKET, then remove it.


The following is the improved server code

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;
}


As with the previous code before, local changes that accept place. For a listening socket, the socket if readable instructions coming users are connected.

Global maintains a list of information records created threads, each thread has created a bool variable is whether the thread should continue execution of the loop identification. When bRunning becomes false, the thread function out of the loop, return.


When you need to stop the server is running, the server simply press any key and enter, will notify the thread exits, and call WaitForSingleObject (), to confirm the thread has exited. There utilizes EnterCriticalSection () and LeaveCriticalSection () function is a critical area to ensure that only one thread in the operation of the global list.




Use multiple threads to consume certain resources. For fd_set, default Socket 64 can accommodate up. So may be a thread to handle 64 connected to the client. Each client without having to create a thread.

code show as below:

typedef struct _AcceptThreadParam
{
	bool bRunning;
	SOCKET listeningSocket;
}AcceptThreadParam;


#define SOCKET_ARRAY_SIZE 64

SOCKET 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;
}

As completion of the function. Only one thread can handle multiple clients up.




Asynchronous IO can also be used to implement the server, the following is a complete port server to achieve the same function.

typedef struct _RepeatAcceptingThreadParam
{
	SOCKET listeningSocket;
	bool* pBRunning;
}RepeatAcceptingThreadParam;



typedef struct _CompletionPortWorkerThreadParam
{
	HANDLE hCompletionPort;
	bool* pBRunning;
}CompletionPortWorkerThreadParam;


#define MESSAGE_BUF_SIZE 1024

enum 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 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 = 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 received
				pIoData->message[dwBytesTransfered] = '\0';
				printf("the client %d:%s \n",pIoData->sock,pIoData->message);

				//send back the data received add a "server:" in the front
				char 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;
}


Published 15 original articles · won praise 2 · views 50000 +

Guess you like

Origin blog.csdn.net/imhikaru/article/details/7231588