基于非阻塞socket的多线程服务器的实现------一个服务器如何与多个客户端进行通信?

时间:2023-01-02 23:59:32

      我们首先来看服务端(涉及非阻塞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). 


       好, 先这样, 睡觉去。