【问题标题】:Multithreaded Socket programming server is getting messages from only 1 client thread多线程套接字编程服务器仅从 1 个客户端线程获取消息
【发布时间】:2018-08-23 10:25:07
【问题描述】:

对于一个基准测试任务,我正在创建多个具有相同套接字描述符的服务器工作者和具有不同连接的客户端工作者。

尽管客户端线程正在为每个线程发送相同消息的副本。服务器显示它只收到每条消息的一个副本,而不是所有副本,由所有客户端线程发送。

对于 4 个线程和 2 条消息 {0,1} ,客户端输出

client 3Wrote 0
 client 1Wrote 1
 client 0Wrote 0
 client 1Wrote 0
 client 2Wrote 0
 client 3Wrote 1
 client 2Wrote 1

作为服务器输出的位置

Server 0 Received - 0
Server 2 Received - 1

为什么没有显示其余的消息?或者他们从未真正由客户发送?

void *clientWorker(void *threadarg) {
    struct workDetails *thisWork;
    thisWork = (struct workDetails *) threadarg;
    int threadcount = thisWork->threadcount;
    int chunkSize = thisWork->chunkSize;
    char *serverIp = thisWork->serverIp;
    char *dataStore = thisWork->dataStore;

    int sockfd;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    char *buffer = (char *) calloc(chunkSize, sizeof(char));
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) error("ERROR opening client socket");
    server = gethostbyname(serverIp);
    if (server == NULL) error("Could not detect server by node name");

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *) server->h_addr,
          (char *) &serv_addr.sin_addr.s_addr,
          server->h_length);
    serv_addr.sin_port = htons(PORTNO);
    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        error("Server is not up.");
    long totalPackets = oneGBtoByte / (chunkSize * threadcount);
    long i;
    for (i = 0; i < totalPackets; ++i) {
        sprintf(buffer, "%ld", i);
        if (write(sockfd, buffer, chunkSize) < 0)
            error("ERROR writing to socket");
        else {
            lock_guard<mutex> guard(cout_mutex);
            cout << " client " << thisWork->threadid << "Wrote " << i << endl;
        }
    }
    cout << " client " << thisWork->threadid << "Exit";
    pthread_exit((void *) NULL);
}

void *serverWorker(void *threadarg) {
    struct workDetails *thisWork;
    thisWork = (struct workDetails *) threadarg;
    int threadcount = thisWork->threadcount;
    int chunkSize = thisWork->chunkSize;
    char *dataStore = thisWork->dataStore;
    int sockfd = thisWork->sockfd;
    char *buffer = new char[chunkSize];

    while (1) {

        if(read(sockfd, buffer, chunkSize) > 0){
            lock_guard<mutex> guard(cout_mutex);
            cout << "Server " << thisWork->threadid << " Received - " << buffer << endl;
        }
    }
    pthread_exit((void *) NULL);
}

【问题讨论】:

  • 跨线程共享相同的套接字描述符通常是一个坏主意,如果它是一个 TCP 套接字,它总是一个坏主意(因为您无法控制 TCP 流中的哪些字节将进入哪个线程,并且在不可避免的情况下,线程 A 以传入 TCP 消息的一半结束而线程 B 以另一半结束,这两个线程都无法正确处理消息)
  • 我猜整个消息块只发送到一个特定的线程。 read(sockfd, buffer, chunkSize) 是线程安全的。
  • 您的猜测充其量是过于乐观了。关键是read(sockfd, buffer, chunkSize)保证获得chunkSize 字节。特别是,如果您有多个线程共享一个套接字,它可能只接收一个对等方消息的 1 个字节。或者它可能会收到散布的几个对等方消息的字节。代码既可以是线程安全的,也可以是不连贯的。
  • 详细说明 Gil 上面提到的——TCP 是一种基于流的协议,而不是基于消息的协议;这意味着 TCP 流只保证它传输的字节的传递和排序,它不提供任何消息帧功能。因此,通过对 send() 的单个调用传递给 TCP 层的数据可能会在另一端通过对 recv() 的多次调用被接收,或者相反,通过对 send() 的多次调用传入的数据可能会通过单个 recv() 调用。您的接收代码需要考虑到这一点——如果涉及多个线程,这很难做到。
  • @sapy:不,他们不是这么说的。您可以有 1 个线程从一个套接字读取,而另一个线程写入同一个套接字。但是你不能有多个线程同时从同一个套接字读取,或者多个线程同时写入同一个套接字,除非你序列化对套接字的访问以避免并发问题和数据损坏。否则,为每个套接字使用单独的线程,不要跨多个线程共享套接字。

标签: c++ multithreading sockets pthreads client-server


【解决方案1】:

问题是accept connection code 在主线程中(在代码中不可见)。因此,尽管客户端尝试创建 4 个连接,但服务器只接受了一个连接。并且输出没有揭示这个问题,因为代码没有来回发送确认。 (没有乒乓球。理想情况下,大多数接收之后是发送确认,并且每次发送之后都是接收确认)。

当我将该接受连接代码移至线程工作者时。它按预期工作。

void *serverWorker(void *threadarg) {
    struct workDetails *thisWork;
    thisWork = (struct workDetails *) threadarg;
    int threadcount = thisWork->threadcount;
    int chunkSize = thisWork->chunkSize;
    char *dataStore = thisWork->dataStore;
    int sockfd = thisWork->sockfd;
    struct sockaddr_in cli_addr; // = thisWork->cli_addr;
    char *buffer = new char[chunkSize];


    listen(sockfd, 5);
    socklen_t clilen = sizeof(cli_addr);
    sockfd = ::accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
    if (sockfd < 0)
        error("ERROR on accept");

    while (1) {
        if (recv(sockfd, buffer, chunkSize, 0) > 0) {
            //lock_guard<mutex> guard(cout_mutex);
            //cout << "Server " << thisWork->threadid << " Received - " << buffer << endl;
            buffer[chunkSize] = NULL;
            strncat(dataStore, buffer, strlen(buffer));
            if (strlen(dataStore) == oneGBtoByte) {
                cout << "Done - " << endl;
                storeInfile(created, dataStore);
                break;
            }
        }
    }

    //End
    delete[] buffer;
    pthread_exit((void *) NULL);

}

@jeremy-friesner 或 @gil-hamilton 所说的都是真的。但是这些问题在这里并不重要,因为我只对收到的总字节感兴趣,而不是谁收到了哪个块。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-24
    相关资源
    最近更新 更多