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 256unsigned 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 20unsigned 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 網絡編程》一書。