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

Posted

技术标签:

【中文标题】多个客户端套接字到单个服务器 C++【英文标题】:Multiple client sockets to a single server C++ 【发布时间】:2016-07-11 00:58:56 【问题描述】:

基本上,我想做的是使用 C++ 中的套接字将多线程客户端之间的消息交换到单线程协调器。客户端将请求授予访问文件的权限,协调器将它们放入队列中,一次授予一个。我认为这不是最好的解决方案,但我考虑在端口 2000 中为协调器创建一个套接字,以便客户端在那里发送所有消息。每个客户端都将使用其标识符(端口 2001、2002、2003...)创建一个套接字,以便协调者可以在需要时通过这些端口回答它们。我遇到的问题是客户端被困在他们自己的套接字的accept() 函数上,甚至是协调器授予的第一个套接字。下面是我的代码(cout“我在这里”从未在客户端中显示,这就是它卡住的地方 - 第 53 行):

协调器.cpp:

#include <iostream>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

using namespace std;

int main (int argc, char** argv) 

    const int REQUEST = 1;
    const int GRANT = 2;
    const int RELEASE = 3;
    const char* portno = "2000";
    int queue[128] = 0;
    int last = 0;

    int socket_receive, newsocket_receive, socket_send, port_send;
    string msg, msg_send;

    char buffer[256];
    memset(buffer, '|', 256);
    struct addrinfo hints, *res;
    struct addrinfo hints_send, *res_send;
    struct sockaddr_in client_address;

    bzero((char*) &hints, sizeof(hints));

    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    getaddrinfo(NULL,portno,&hints,&res);
    socket_receive = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
    bind(socket_receive, res->ai_addr, res->ai_addrlen);
    listen(socket_receive,5);
    socklen_t client_lenght = sizeof(client_address);

    while (1)
        newsocket_receive = accept(socket_receive,(struct sockaddr*)&client_address,&client_lenght);

        int n = recv(newsocket_receive,buffer,sizeof(buffer),0);
        msg.append(buffer, buffer+n);

        copy(buffer+n, buffer+256, buffer);
        char subarray[n];
        memset(subarray, '|', n);
        copy(subarray, subarray+n, buffer+256-n);

        int elems[2];
        int i = 0;
        while (i < sizeof(msg)-1)
            if (msg[i] == ':')
                break;
            
            i++;
        
        if (i < 4)
            const char* char_msg0 = msg.substr(0,i).c_str();
            const char* char_msg1 = msg.substr(i+1,sizeof(msg)).c_str();
            elems[0] = atoi(char_msg0);
            elems[1] = atoi(char_msg1);

            if (elems[1] == REQUEST)
                if (last == 0)

                    port_send = 2000 + elems[0];
                    string str_port_send = to_string(port_send);
                    char const* char_port_send = str_port_send.c_str();

                    getaddrinfo("localhost",char_port_send,&hints_send,&res_send);
                    socket_send = socket(res_send->ai_family,res_send->ai_socktype,res_send->ai_protocol);
                    connect(socket_send,res_send->ai_addr,res_send->ai_addrlen);
                    msg_send = to_string(GRANT);
                    cout << "Coordinator: sending GRANT message to consumer " << elems[0] << endl;
                    send(socket_send, msg_send.data(),msg_send.size(),0);
                    close(socket_send);

                    queue[last] = elems[0];
                    last++;
                
                else 
                    queue[last] = elems[0];
                    last++;
                
            
            else if (elems[1] == RELEASE)
                //SHIFT QUEUE
                last--;
            
            msg = "";
        
     

    close(socket_receive);

    return 0;

client.cpp:

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <algorithm>

using namespace std;

void *request (void *arg)

    const int REQUEST = 1;
    const int GRANT = 2;
    const int RELEASE = 3;

    int id = *((int *) arg);

    string msg_send, msg_receive;
    const char* portno = "2000";
    const char* port_receive = to_string(2000+id).c_str();
    int socket_send, socket_receive, newsocket_receive;
    char buffer [256];
    struct addrinfo hints, *res;
    struct addrinfo hints_receive, *res_receive;
    struct sockaddr_in client_address;


    bzero((char*) &hints, sizeof(hints));

    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    getaddrinfo("localhost",portno,&hints,&res);
    socket_send = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
    connect(socket_send,res->ai_addr,res->ai_addrlen);
    msg_send = to_string(id) + ":" + to_string(REQUEST);
    cout << "Client: sending REQUEST message to coordinator." << endl;
    send(socket_send,msg_send.data(),msg_send.size(),0);
    close(socket_send);

    getaddrinfo(NULL,port_receive,&hints_receive,&res_receive);
    socket_receive = socket(res_receive->ai_family,res_receive->ai_socktype,res_receive->ai_protocol);
    bind(socket_receive, res_receive->ai_addr, res_receive->ai_addrlen);
    listen(socket_receive,5);
    socklen_t client_lenght = sizeof(client_address);

    newsocket_receive = accept(socket_receive,(struct sockaddr*)&client_address,&client_lenght);
    cout << "I'm here" << endl;
    int n = recv(newsocket_receive,buffer,sizeof(buffer),0);
    msg_receive.append(buffer, buffer+n);
    cout << msg_receive << endl;
    copy(buffer+n, buffer+256, buffer);
    char subarray[n];
    memset(subarray, '|', n);
    copy(subarray, subarray+n, buffer+256-n);
    close(socket_receive);



int main (int argc, char** argv) 

    int n_threads = atoi(argv[1]);
    pthread_t threads[n_threads];

    for (long i=1; i<=n_threads; i++)
        int *arg = (int *) malloc(sizeof(*arg));
        *arg = i;
        pthread_create(&threads[i], NULL, request, arg);
    

    pthread_exit(NULL);
    return 0;

【问题讨论】:

【参考方案1】:

您花费大量时间在服务器和客户端之间建立多个连接。我会尝试重新使用客户端与connect() 建立的第一个套接字连接,因为套接字是双向的。

就您的连接机制而言,我看到的第一个问题是您在循环内调用listen()。您应该只为服务器调用一次。

【讨论】:

我想过这样做,但是如何存储我以后要使用的套接字以及如何识别队列中的每个套接字? 听的东西也不是问题,但感谢您的注意。我把它排除在循环之外。 @EduardoDeMelloCastanho 这只是一个数据结构问题。你确定可以选择和设计数据结构吗?

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

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

具有多个连接的 C++ 服务器套接字

将 C++ 服务器移植到云端

单个服务器进程可以监听多个端口吗?

通过 Socket C++ 发送长字符串

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