C++多线程服务器
因为自己同时在看多本书,之前看过《TCP/IP 网络编程》一书,其中有一个自己编写一个多线程服务器的例子,于是就把代码直接抄了一变。
在学习网络编程前需要先了解网络的7层模型。
具体代码如下:
服务器端:
include <iostream>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <process.h>
#pragma comment(lib,"Ws2_32.lib")
using namespace std;
#define BUF_SIZE 100
#define MAX_CLNT 256
unsigned WINAPI HandleClient(void* pvoid);
void SendMeg(const char* pMsg,int nLen);
void ErrorHandle(const char* pMsg);
int nCountClient = 0;
SOCKET clntSocket[MAX_CLNT];
HANDLE hMutex;
int main(int argc,char* argv[])
{
WSADATA wsaData;
SOCKET hServSock, hClntSock;
SOCKADDR_IN servAdr, clntAdr;
int clntAdrSz;
HANDLE hTread;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
{
ErrorHandle("WSAStartup Error");
}
hMutex = CreateMutex(NULL,FALSE,NULL);
hServSock = socket(PF_INET,SOCK_STREAM,0);
memset(&servAdr,0,sizeof(servAdr));
servAdr.sin_family = AF_INET;
servAdr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
servAdr.sin_port = htons(9000);
if (bind(hServSock, (SOCKADDR*)&servAdr,sizeof(servAdr)) == SOCKET_ERROR)
{
ErrorHandle("bind Error");
}
if (listen(hServSock,5) == SOCKET_ERROR)
{
ErrorHandle("listen Error");
}
while (1)
{
clntAdrSz = sizeof(clntAdr);
cout << "[Server]:Wait for client to Connect..." << endl;
hClntSock = accept(hServSock,(sockaddr*)&clntAdr,&clntAdrSz);
::WaitForSingleObject(hMutex,INFINITE);
clntSocket[nCountClient++] = hClntSock;
ReleaseMutex(hMutex);
hTread = (HANDLE)_beginthreadex(NULL,0, HandleClient,(void*)&hClntSock,0,NULL);
printf("[Server]:Connenct Client IP:%s\n",inet_ntoa(clntAdr.sin_addr));
}
closesocket(hServSock);
WSACleanup();
return 0;
}
unsigned WINAPI HandleClient(void* pvoid)
{
SOCKET hClntSock = *((SOCKET*)(pvoid));
int strLen = 0;
int i;
char szBuff[BUF_SIZE];
memset(szBuff,0, sizeof(char)*BUF_SIZE);
while ((strLen = recv(hClntSock, szBuff, sizeof(szBuff), 0)) != 0)
{
printf("[Server]:recv %s from Client.\n",szBuff);
SendMeg(szBuff, strLen);
}
::WaitForSingleObject(hMutex, INFINITE);
for (i = 0;i< nCountClient;i++)
{
// 移除断开连接的套接字
if (hClntSock == clntSocket[i])
{
while (i++ < nCountClient - 1)
{
clntSocket[i] = clntSocket[i + 1];
}
break;
}
}
nCountClient--;
ReleaseMutex(hMutex);
closesocket(hClntSock);
return 0;
}
void SendMeg(const char* pMsg, int nLen)
{
int i;
::WaitForSingleObject(hMutex, INFINITE);
for (int i=0;i< nCountClient;i++)
{
send(clntSocket[i], pMsg, nLen,0);
printf("[Server]:send %s to Client.\n",pMsg);
}
ReleaseMutex(hMutex);
}
void ErrorHandle(const char* pMsg)
{
fputs(pMsg,stderr);
fputc('\n',stderr);
exit(1);
}
代码说明:其中修改了一下在控制台中输出的信息。
客户端代码:
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <process.h>
#pragma comment(lib,"Ws2_32.lib")
using namespace std;
#define BUF_SIZE 100
#define NAME_SIZE 20
unsigned WINAPI SendMsg(void* arg);
unsigned WINAPI RecvMsg(void* arg);
void ErrorHandle(const char* msg);
char name[NAME_SIZE] = "[DEFAULT]";
char msg[BUF_SIZE];
int main(int argc,char* argv[])
{
WSADATA wsaData;
SOCKET hSock;
SOCKADDR_IN servAdr;
HANDLE hSndThread, hRcvThread;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
ErrorHandle("WSAStartup Error()");
}
hSock = socket(PF_INET,SOCK_STREAM,0);
memset(&servAdr,0,sizeof(servAdr));
servAdr.sin_family = AF_INET;
servAdr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
servAdr.sin_port = htons(9000);
if (connect(hSock,(sockaddr*)&servAdr,sizeof(servAdr)) == SOCKET_ERROR)
{
ErrorHandle("Connect Error");
}
cout << "Connect To Server success" << endl;
hSndThread = (HANDLE)_beginthreadex(NULL,0,SendMsg,(void*)(&hSock),0,NULL);
hRcvThread = (HANDLE)_beginthreadex(NULL,0,RecvMsg, (void*)(&hSock), 0, NULL);
WaitForSingleObject(hSndThread,INFINITE);
WaitForSingleObject(hRcvThread,INFINITE);
closesocket(hSock);
WSACleanup();
return 0;
}
unsigned WINAPI SendMsg(void* arg)
{
SOCKET hSock = *((SOCKET*)arg);
char nameMsg[BUF_SIZE + NAME_SIZE];
while (1)
{
cout << "[Client]:Please enter your msg to send to Server..." << endl;
fgets(msg, BUF_SIZE,stdin);
if (strcmp(msg,"q\n") == 0||strcmp(msg,"Q\n") == 0)
{
closesocket(hSock);
exit(0);
}
send(hSock,msg,strlen(msg),0);
printf("[Client]:send %s to Server\n", msg);
}
return 0;
}
unsigned WINAPI RecvMsg(void* arg)
{
SOCKET hSock = *((SOCKET*)arg);
char nameMsg[BUF_SIZE + NAME_SIZE];
int strLen;
while (1)
{
strLen = recv(hSock, nameMsg, BUF_SIZE + NAME_SIZE-1,0);
if (strLen == -1)
return-1;
nameMsg[strLen] = 0;
printf("[Client]:recv %s From Server\n", nameMsg);
}
return 0;
}
void ErrorHandle(const char* msg)
{
fputs(msg, stderr);
fputc('\n', stderr);
exit(1);
}
流程说明:先运行服务器,等待客户端的连接,客户端连接后由客户端输入字符串,然后发送给服务器,服务器再把接受到的字符串发回给客户端,这样就是一个回声服务器了。
代码运行效果:
有关多线程网络编程的更多内容,大家可以参考《TCP/IP 网络编程》一书。