在 C++ 中处理单个服务器和多个客户端

Posted

技术标签:

【中文标题】在 C++ 中处理单个服务器和多个客户端【英文标题】:Handle single server and multiple clients in c++ 【发布时间】:2017-04-01 17:37:12 【问题描述】:

我对套接字编程完全陌生。我试图了解套接字编程和处理多个连接时的不同做法。 我已经实现了以下功能。

服务器连接到多个客户端。 c1,c2,c3......cn.

服务器向客户端发送一个时隙。

服务器向客户端发送消息以传输消息。(传输 限时消息)

客户端在限定时间内发送消息

一旦第一个连接的客户端超时,服务器将消息发送到下一个 用于传输消息等的客户端。

这是我的工作代码。

服务器.cpp

    #include "stdafx.h"
    #pragma comment(lib,"ws2_32.lib")
    #include <WinSock2.h>
    #include <string>
    #include <iostream>
    #include<conio.h>

    #define DEFAULT_NC_TIME  20
    #define MAX_CLIENTS 2
    SOCKET Connections[MAX_CLIENTS];
    int TotalConnections = 0;

    #include <vector>
    #include<time.h>


void wait ( int seconds )

    clock_t endwait;
    endwait = clock () + seconds * CLOCKS_PER_SEC ;
    while (clock() < endwait) 


void ClientHandlerThread(int index) //index = the index in the SOCKET Connections array
    
    int bufferlength; //Holds the length of the message a client sent
    while (true)
    
        recv(Connections[index], (char*)&bufferlength, sizeof(int), NULL); //get buffer length
        char * buffer = new char[bufferlength+1]; //Allocate buffer
        buffer[bufferlength] = '\0';
        recv(Connections[index], buffer, bufferlength, NULL); //get buffer message from client
        std::cout << buffer << std::endl;
        delete[] buffer; 
    
    

void emitMessageToClient(int indexOfClient, int ts)
        std::string buftest="You are ready to transmit Message.\n";
        int size = buftest.size(); //Get size of message in bytes and store it in int size
        send(Connections[indexOfClient], (char*)&ts, sizeof(int), NULL); //send Timeslot Duration to the client i
        send(Connections[indexOfClient], (char*)&size, sizeof(int), NULL); //send Size of message
        send(Connections[indexOfClient], buftest.c_str(), buftest.size(), NULL); //send Message
        


int main()
    
    //Winsock Startup
    WSAData wsaData;
    WORD DllVersion = MAKEWORD(2, 1);
    if (WSAStartup(DllVersion, &wsaData) != 0)
    
        MessageBoxA(NULL, "WinSock startup failed", "Error", MB_OK | MB_ICONERROR);
        return 0;
    

    SOCKADDR_IN addr;
    int addrlen = sizeof(addr);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    addr.sin_port = htons(1111); 
    addr.sin_family = AF_INET;

    SOCKET sListen = socket(AF_INET, SOCK_STREAM, NULL);
    bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
    listen(sListen, SOMAXCONN); 

    SOCKET newConnection; //Socket to hold the client's connection
    int ConnectionCounter = 0; //# of client connections
    for (int i = 0; i < MAX_CLIENTS; i++)
    

        newConnection = accept(sListen, (SOCKADDR*)&addr, &addrlen);
        if (newConnection == 0)
        
            std::cout << "Failed to accept the client's connection." << std::endl;
        
        else
        
            std::cout << "Client "<<i<<" Connected!" << std::endl;
            Connections[i] = newConnection; 
            TotalConnections += 1; //Increment total # of clients that have connected
        
    

    int ts = DEFAULT_NC_TIME / (TotalConnections + 1); // caculating time slot for each of connected node including server 
    std::cout<<"\nPress any key to start transmission: ";
    getche();
    for(int i= 0; i < TotalConnections; i++)

        emitMessageToClient(i, ts);
        CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandlerThread, (LPVOID)(i), NULL, NULL);
        wait(ts);
        if(i == (TotalConnections - 1))
           i = 0;
        
    
    system("pause");
    return 0;

客户端.cpp

        #include "stdafx.h"
    #pragma comment(lib,"ws2_32.lib") //Required for WinSock
    #include <WinSock2.h> //For win sockets
    #include <string> //For std::string
    #include <iostream> //For std::cout, std::endl, std::cin.getline
    #include <time.h>
    #include <vector>
    #include <conio.h>
    SOCKET Connection;//This client's connection to the server

    void wait ( int seconds )
    
        clock_t endwait;
        endwait = clock () + seconds * CLOCKS_PER_SEC ;
        while (clock() < endwait) 
    

    void ClientThread()
    
        int bufferlength; //Holds the length of the message we are receiving
        int ts; // holds timeslot duration for this client
        while (true)
        
            recv(Connection, (char*)&ts, sizeof(int), NULL); //receive timeslot
            recv(Connection, (char*)&bufferlength, sizeof(int), NULL); //receive bufferlength
            char * buffer = new char[bufferlength+1];
            buffer[bufferlength] = '\0'; 
            recv(Connection, buffer, bufferlength, NULL);
            std::cout << buffer << std::endl; //print out buffer
            std::string userinput; //holds the user's chat message

            for (int n= ts; n>0; n--)
           
            userinput = "First client sending message...";
            int bufferlength = userinput.size(); //Find buffer length
            send(Connection, (char*)&bufferlength, sizeof(int), NULL); //Send length of buffer
            send(Connection, userinput.c_str(), bufferlength, NULL); //Send buffer
            wait (1);
           
            delete[] buffer; //Deallocate buffer
        
    

    int main()
    
        //Winsock Startup
        WSAData wsaData;
        WORD DllVersion = MAKEWORD(2, 1);
        if (WSAStartup(DllVersion, &wsaData) != 0)
        
            MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
            return 0;
        

        SOCKADDR_IN addr; 
        int sizeofaddr = sizeof(addr);
        addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
        addr.sin_port = htons(1111);
        addr.sin_family = AF_INET;

        Connection = socket(AF_INET, SOCK_STREAM, NULL);
        if (connect(Connection, (SOCKADDR*)&addr, sizeofaddr) != 0)
        
            MessageBoxA(NULL, "Failed to Connect", "Error", MB_OK | MB_ICONERROR);
            return 0; //Failed to Connect
        

        std::cout << "Connected!" << std::endl;
        CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientThread, NULL, NULL, NULL);

        getche();
        return 0;
    

一旦服务器向所有连接的客户端发送消息。服务器想再次向他的第一个客户端发送消息以再次发送消息。(循环)

问题

如果服务器将他的所有客户端 c1、c2、c3 与 Cn 通信并再次与他的第一个客户端 C1 通信,我该如何实现。(循环)我已使用此检查

 if(i == (TotalConnections - 1))
       i = 0;
 

但它会使程序崩溃。

【问题讨论】:

尝试使用threads 使用带有作业队列的线程池。 @daemon7osh 上面的代码工作正常。当我使用 if 检查它会使程序崩溃。我认为在客户端 CreateThread 函数无法有效处理 @Richard Critten 你能给我举个例子吗? 为每个客户端服务器端创建线程 【参考方案1】:

通过将i 设置回0,您将创建一个无限循环,因为i 永远不会变得足够大以使循环结束。

for(int i= 0; i < TotalConnections; i++) 

    emitMessageToClient(i, ts);
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandlerThread, (LPVOID)(i), NULL, NULL);
    wait(ts);

    // This will create an infinite loop
    if(i == (TotalConnections - 1))
    
        i = 0;
    

你可以做的是等到循环完成重新访问第一个连接:

for(int i= 0; i < TotalConnections; i++) 

    emitMessageToClient(i, ts);
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandlerThread, (LPVOID)(i), NULL, NULL);
    wait(ts);


// now the loop is over, revisit the first connection 
// if it exists      
if(TotalConnections > 0) 

    emitMessageToClient(0, ts);
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandlerThread, (LPVOID)(0), NULL, NULL);
    wait(ts);

【讨论】:

听起来像是 OP 想要无限循环。我不认为这是问题所在。

以上是关于在 C++ 中处理单个服务器和多个客户端的主要内容,如果未能解决你的问题,请参考以下文章

多个客户端套接字到单个服务器 C++

c++ 网络编程TCP/IP linux 下多进程socket通信 多个客户端与单个服务端交互代码实现回声服务器

Websockets 在客户端发送多个事件和多个事件处理程序

渲染单个网页需要多少HTTP get()请求?一个还是多个?

使用azure服务总线,如何将单个消息发布到多个队列?

使用单个事件处理程序 asp.net 从网格中处理多个删除事件