多线程在 C++ 中的 Windows 中将多个客户端连接到单个服务器

Posted

技术标签:

【中文标题】多线程在 C++ 中的 Windows 中将多个客户端连接到单个服务器【英文标题】:MultiThreading in connecting multiple clients to single server in windows in C++ 【发布时间】:2017-03-08 05:22:42 【问题描述】:

我用 C++ 为客户端和服务器编写了可以处理多个客户端的程序。现在问题是这样的:

我在服务器的 main() 函数中启动了两个线程,一个线程监听(打开)与客户端的连接,另一个线程接受连接并将数据发送到连接的客户端。两个线程都定期运行(即连续)。

现在,当我将客户端 1 连接到服务器时,客户端 1 接收到信息,同时当另一个客户端 2 连接到服务器时,客户端 1 将进入 等待状态,客户端 2获取数据。现在,当我关闭客户端 2 时,服务器显示两个客户端都已断开连接,但客户端 1 正在运行,因为它处于无限循环中,并且由于线程将定期运行,服务器开始再次侦听并再次接受连接.这将与 client1 建立新连接,并且 client 1 正在接收数据。

谁能帮我解决这个问题。我需要同时将数据发送到连接到服务器的客户端数量以及任何客户端断开连接时不应影响其他客户端。一个可以处理多个客户端的简单服务器。

我的服务器程序:

#include <iostream>
#include <winsock2.h>
#include <Windows.h>
#include <process.h>
#include <thread>
#pragma comment(lib,"ws2_32.lib")


static int flag = 0;
BOOL bOptVal = FALSE;
WSADATA WsaDat;
SOCKET Socket;  
std:: thread t1,t2,t3;
void client_disconnected(SOCKET Socket);
void startThreads();
void timer_start(std::function<void(void)> func, unsigned int interval);
void accept_connection();
void send_message();

void start_server_listening()


if(WSAStartup(MAKEWORD(2,2),&WsaDat)!=0)

    std::cout<<"WSA Initialization failed!\r\n";
    WSACleanup();
    system("PAUSE");



Socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);       

if (setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, (char *) &bOptVal, sizeof(bOptVal)) < 0) 
    perror("setsockopt(SO_REUSEADDR) failed");
    exit(EXIT_FAILURE);


if(Socket==INVALID_SOCKET)

    std::cout<<"Socket creation failed.\r\n";
    WSACleanup();
    system("PAUSE");



SOCKADDR_IN serverInf;
serverInf.sin_family=AF_INET;
serverInf.sin_addr.s_addr=INADDR_ANY;
serverInf.sin_port=htons(8888);

if(bind(Socket,(SOCKADDR*)(&serverInf),sizeof(serverInf))==SOCKET_ERROR)

    std::cout<<"Unable to bind socket!\r\n";
    WSACleanup();
    //system("PAUSE");


else   
listen(Socket,3);


accept_connection();
    


void accept_connection()


    SOCKET TempSock=SOCKET_ERROR;
    while(TempSock==SOCKET_ERROR)
    
        std::cout<<"\nWaiting for incoming connections\n";
        Sleep(2000);
        TempSock=accept(Socket,NULL,NULL);
    

    // If iMode!=0, non-blocking mode is enabled.
    u_long iMode=1;
    ioctlsocket(Socket,FIONBIO,&iMode);

    Socket=TempSock;
    std::cout<<"Client connected!\r\n\r\n";
    send_message();
    if(flag==1)
        Sleep(1000);
        //system("PAUSE");
    

void send_message()

    for(;;)
    
        int nError=WSAGetLastError();
        if(nError!=WSAEWOULDBLOCK&&nError!=0)
        
            flag=1;
            client_disconnected(Socket);
            break;

        
        else 
        char *szMessage="Welcome to the server!\r\n";
        send(Socket,szMessage,strlen(szMessage),0);
        Sleep(2000);            
        
    


void client_disconnected(SOCKET Socket)

    std::cout<<"Client disconnected!\r\n";

            // Shutdown our socket
            shutdown(Socket,SD_SEND);

            // Close our socket entirely
            closesocket(Socket);

            WSACleanup();




int main()

    // start a timer that executes threads periodically/continuously
    timer_start(startThreads, 200); //calling timer function
    while(true);
    return 0;


void timer_start(std::function<void(void)> func, unsigned int interval)

    std::thread([func, interval]()  // calling startThreads() function
        while (true)
        
            func();
            std::this_thread::sleep_for(std::chrono::milliseconds(interval));
        
    ).detach();

void startThreads()

      //starting two threads one starts listening and other accepts connections
      t1 = std::thread(start_server_listening);
      t2 = std:: thread(accept_connection);
      t1.join();
      t2.join();


我的客户计划:

#include <iostream>
#include <winsock2.h>
#include <Windows.h>
#include <process.h>
#include <thread>
#pragma comment(lib,"ws2_32.lib") 

void startThreads();
void timer_start(std::function<void(void)> func, unsigned int interval);
std:: thread t1;

 void  receiveToClient()

    WSADATA WsaDat;
    if(WSAStartup(MAKEWORD(2,2),&WsaDat)!=0)
    
        std::cout<<"Winsock error - Winsock initialization failed\r\n";
        WSACleanup();
        system("PAUSE");

    

    // Create our socket

    SOCKET Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(Socket==INVALID_SOCKET)
    
        std::cout<<"Winsock error - Socket creation Failed!\r\n";
        WSACleanup();
        system("PAUSE");

    

    // Resolve IP address for hostname
    struct hostent *host;
    if((host=gethostbyname("localhost"))==NULL)
    
        std::cout<<"Failed to resolve hostname.\r\n";
        WSACleanup();
        system("PAUSE");

    

    // Setup our socket address structure
    SOCKADDR_IN SockAddr;
    SockAddr.sin_port=htons(8888);
    SockAddr.sin_family=AF_INET;
    SockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // Attempt to connect to server
    if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr))!=0)
    
        std::cout<<"Failed to establish connection with server\r\n";
        WSACleanup();
        system("PAUSE");

    

    // If iMode!=0, non-blocking mode is enabled.
    u_long iMode=1;
    ioctlsocket(Socket,FIONBIO,&iMode);

    // Main loop
    for(;;)
    
        int nError=WSAGetLastError();
        if(nError!=WSAEWOULDBLOCK&&nError!=0)
                break;
        // Display message from server
        char buffer[1000];
        memset(buffer,0,999);
        int inDataLength=recv(Socket,buffer,1000,0);
        std::cout<<buffer;

        //end client when server is disconnected

        Sleep(2000);
    

    WSACleanup();




int main()

    timer_start(startThreads, 200);
    while(true);
    return 0;


void timer_start(std::function<void(void)> func, unsigned int interval)

    std::thread([func, interval]() 
        while (true)
        
            func();
            std::this_thread::sleep_for(std::chrono::milliseconds(interval));
        
    ).detach();

void startThreads()


    t1 = std::thread(receiveToClient);
    t1.join();

【问题讨论】:

while(true); main 中的循环仅保证您的程序将永远退出。对于连接处理,我会使用一个受互斥体保护的向量来存储每个连接,并编写一个函数来检查连接是否打开,如果没有则将其删除。 【参考方案1】:

我看到您的代码运行了很多线程。他们中的大多数人只是在等待,永远不会完成。此外,您的 Socket 对象被放置在全局空间中。但是对于每个使用它的线程来说,它应该是本地的。例如,您可以在全局空间中放置一个连接列表。必须从临界区中的线程访问此列表。

【讨论】:

以上是关于多线程在 C++ 中的 Windows 中将多个客户端连接到单个服务器的主要内容,如果未能解决你的问题,请参考以下文章

线程池

c++ windows中客户端服务器编程中的多线程

可移植的 C++ 多线程

C++ - 通过多线程同时播放多个哔声?

C++多线程编程

C++多线程同步技巧--- 互斥体