C++多线程服务器

发布于:2025-08-11 ⋅ 阅读:(14) ⋅ 点赞:(0)

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);
}

流程说明:先运行服务器,等待客户端的连接,客户端连接后由客户端输入字符串,然后发送给服务器,服务器再把接受到的字符串发回给客户端,这样就是一个回声服务器了。

代码运行效果:

wechat_2025-08-10_160759_866
有关多线程网络编程的更多内容,大家可以参考《TCP/IP 网络编程》一书。