大家好,我是涛哥。
既然是多线程服务器,那么,这些线程肯定是有明确分工的。主线程来处理网络的连接,而通信线程来处理客户端与服务端的通信。
而且,主线程要负责多个客户端的连接请求,所以不能阻塞主线程哦,因此必须用非阻塞socket. 于是,我写的服务端程序如下:
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;
}
编译并运行程序,开启服务端,等待客户端的连接。
我们可以看到,上述服务端的程序需要有主线程和通信线程,而客户端的程序就相对简单了,主线程本身就是通信线程。程序如下:
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;
}
建议有兴趣的同学实际玩一下这个例子,加深对多线程服务器的理解,提高实战能力。网络编程就是这样,定好思路,多编程,多抓包,多调试,很舒!
今天先这样,咱们明天见。
荐 读
点个“赞”和“在看”鼓励下呗