1. 程式人生 > >基於非阻塞socket的多執行緒伺服器的實現------一個伺服器如何與多個客戶端進行通訊?

基於非阻塞socket的多執行緒伺服器的實現------一個伺服器如何與多個客戶端進行通訊?

      我們首先來看服務端(涉及非阻塞socket和多執行緒):

#include <stdio.h>
#include <winsock2.h>   
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#define BUF_SIZE  100

sockaddr_in addrClient; // 為了讓通訊執行緒獲取ip

// 通訊執行緒
DWORD  WINAPI  CommThread(LPVOID  lp)  
{  
    SOCKET sClient = (SOCKET)(LPVOID)lp;  
 
	while(1)
	{    
		char buf[BUF_SIZE] = {0}; 
		int retVal = recv(sClient, buf, BUF_SIZE, 0);
		if(SOCKET_ERROR == retVal)   
		{   
			int err = WSAGetLastError();
			if(WSAEWOULDBLOCK == err) // 暫時沒有資料
			{
				Sleep(100);
				continue;
			}
		}   

		// 輸出客戶端連線資訊
		SYSTEMTIME st;
		GetLocalTime(&st);
		char sDateTime[100] = {0};
		sprintf(sDateTime, "%4d-%2d-%2d %2d:%2d:%2d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
		printf("%s, The client is [%s:%d]. Msg from client is : %s\n", sDateTime, inet_ntoa(addrClient.sin_addr), addrClient.sin_port, buf);   
		

		char msg[BUF_SIZE] = {0};  
		sprintf(msg, "Message received is : %s", buf); 
		while(1)
		{
			retVal = send(sClient, msg, strlen(msg), 0);  // 回顯
			if(SOCKET_ERROR == retVal)   
			{   
				int err = WSAGetLastError();
				if(err == WSAEWOULDBLOCK)
				{
					Sleep(500);
					continue;
				}
			}

			break;
		}	  	
	}

    closesocket(sClient);   
}


int main()
{
	WSADATA wsd;   
    WSAStartup(MAKEWORD(2, 2), &wsd);   
    SOCKET sServer = socket(AF_INET, SOCK_STREAM, 0);   
   
	// 設定套接字為非阻塞模式
	int iMode = 1;
	ioctlsocket(sServer, FIONBIO, (u_long FAR*) &iMode);

    // 設定伺服器套接字地址   
    SOCKADDR_IN addrServ;   
    addrServ.sin_family = AF_INET;   
    addrServ.sin_port = htons(8888);
    addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);    

    bind(sServer,(const struct sockaddr*)&addrServ, sizeof(SOCKADDR_IN));   
    
    listen(sServer, 10);   
      
	printf("Server start...\n");
	int addrClientlen = sizeof(addrClient);   
	while(1)
	{
		SOCKET sClient = accept(sServer, (sockaddr FAR*)&addrClient, &addrClientlen);   
		if(INVALID_SOCKET == sClient)   
		{   
			int err = WSAGetLastError();
			if(WSAEWOULDBLOCK == err)  // 無法立即完成非阻塞套接字上的操作
			{
				Sleep(100);
				continue;
			}
		} 

		// 建立通訊執行緒
		CreateThread(NULL, NULL, CommThread, (LPVOID)sClient, 0, NULL);
	}

    // 釋放套接字   
    closesocket(sServer);   
    WSACleanup();   

	getchar();
	return 0;
}
      不多解釋。 先把服務端執行起來吧。

      下面, 我們來看看客戶端:

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

int main()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(2, 2);
	
	WSAStartup( wVersionRequested, &wsaData );

	SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(8888);
	connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	send(sockClient, "hello world", strlen("hello world") + 1, 0);
	char recvBuf[100] = {0};
	recv(sockClient, recvBuf, 100, 0);
	printf("%s\n", recvBuf);
	
	while(1);

	closesocket(sockClient);
	WSACleanup();

	return 0;
}
       然後同時執行多個客戶端程序(請注意, 如果關掉某一個客戶端程序, 則會引起一些異常, 為了簡便起見, 本文就先不考慮這個情況了)。

       通過觀察服務端和客戶端的結果, 我們可以理解多執行緒伺服器(非阻塞socket). 

       好, 先這樣, 睡覺去。