【问题标题】:C++ Sockets - Server doesn't accept multiple clients (linux)C++ 套接字 - 服务器不接受多个客户端 (linux)
【发布时间】:2015-10-01 01:09:11
【问题描述】:

我有工作的服务器和客户端代码。服务器和客户端可以正确连接和聊天。但是当我打开另一个客户端时,客户端说Awaiting confirmation from the server,没有别的。虽然服务器和客户端 #1 仍然可以聊天。

我搜索了多线程,但他们显示的示例或代码 sn-ps 是高级的。也许一点解释或一个例子会有很大帮助!

下面的代码正在运行。我有一个工作服务器,但它只接受一个连接。如何使服务器允许多个连接?这样我就可以让程序看起来像一个群聊。

client.cpp(当客户端 #2 连接时,代码在第 40 行冻结)

#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>

using namespace std;

int main()
{
    char a;
    int client;
    int portNum = 1500;
    int bufsize = 1024;
    char* buffer = new char[bufsize];
    bool isExit = false;
    char* ip = "127.0.0.1";

    struct sockaddr_in direc;

    if ((client = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        cout << "\nError creating socket..." << endl;
        exit(0);
    }

    cout << "\nSocket created successfully..." << endl;
    direc.sin_family = AF_INET;
    direc.sin_port = htons(portNum);
    inet_pton(AF_INET, ip, &direc.sin_addr);

    if (connect(client,(struct sockaddr *)&direc, sizeof(direc)) == 0)
        cout << "Connection to the server " << inet_ntoa(direc.sin_addr) << endl;

    cout << "Awaiting confirmation from the server..." << endl; //line 40
    recv(client, buffer, bufsize, 0);

    cout << "\n=> Enter # to terminate the connection\n" << endl;

    do {
        cout << "Client: ";
        do {
            cin >> buffer;
            send(client, buffer, bufsize, 0);
            if (*buffer == '#') {
                send(client, buffer, bufsize, 0);
                *buffer = '*';
                isExit = true;
            }
        } while (*buffer != 42);

        cout << "Server: ";
        do {
            recv(client, buffer, bufsize, 0);
            cout << buffer << " ";
            if (*buffer == '#') {
                *buffer = '*';
                isExit = true;
            }

        } while (*buffer != 42);
        cout << endl;

    } while (!isExit);
    cout << "=> Connection terminated.\nGoodbye";

    close(client);
    return 0;
}

服务器.cpp

#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>

using namespace std;

int main()
{
    int client, server;
    int bufsize = 1024;
    int portNum = 1500;
    bool isExit = false;
    char* buffer = new char[bufsize];

    struct sockaddr_in direc;
    socklen_t tamano;
    pid_t pid;

    if ((client = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        cout << "\nError establishing socket..." << endl;
        exit(1);
    }

    cout << "\nSocket server has been created..." << endl;

    direc.sin_family = AF_INET;
    direc.sin_addr.s_addr = htons(INADDR_ANY);
    direc.sin_port = htons(portNum);

    if ((bind(client, (struct sockaddr*)&direc,sizeof(direc))) < 0) {
        cout << "\nError binding connection..." << endl;
        return -1;
    }

    tamano = sizeof(direc);
    cout << "Looking for clients..." << endl;
    listen(client, 1);

    while ((server = accept(client,(struct sockaddr *)&direc,&tamano)) > 0) {
        strcpy(buffer, "Server connected...\n");
        send(server, buffer, bufsize, 0);
        cout << "Connected with the client, you are good to go..." << endl;
        cout << "Enter # to end the connection\n" << endl;

        cout << "Client: ";
        do {
            recv(server, buffer, bufsize, 0);
            cout << buffer << " ";
            if (*buffer == '#') {
                *buffer = '*';
                isExit = true;
            }
        } while (*buffer != '*');

        do {
            cout << "\nServer: ";
            do {
                cin >> buffer;
                send(server, buffer, bufsize, 0);
                if (*buffer == '#') {
                    send(server, buffer, bufsize, 0);
                    *buffer = '*';
                    isExit = true;
                }
            } while (*buffer != '*');

            cout << "Client: ";
            do {
                recv(server, buffer, bufsize, 0);
                cout << buffer << " ";
                if (*buffer == '#') {
                    *buffer == '*';
                    isExit = true;
                }
            } while (*buffer != '*');
        } while (!isExit);

        cout << "\n=> Connection terminated... " << inet_ntoa(direc.sin_addr);
        close(server);
        cout << "\nGoodbye..." << endl;
        isExit = false;
    }

    close(client);
    return 0;
}

如何让服务器接受多个连接?

谢谢!

【问题讨论】:

  • 服务器通过执行accept()接受新客户端。因此,它会在您调用它时立即接受第二个客户端(假设第二个客户端正在等待)。问题是您的服务器代码在再次调用接受之前等待第一个连接终止。您需要查看select()pselect()epoll()。这将允许您使用单个线程接受并与多个套接字通信。或者,您可以在选择之后为每个连接启动一个线程(但这只能扩展,因为线程很昂贵)。

标签: c++ linux sockets


【解决方案1】:

您将需要使用selectpoll 和状态机模式来执行您想要执行的操作。这意味着您将需要处理来自任何客户端发送的数据。查看here 的工作示例。

【讨论】:

    【解决方案2】:

    为了正确支持多个连接,您应该为每个传入连接启动一个新线程。每个新连接都由accept() 返回的其自己的唯一套接字描述符标识。一个简单的例子:

    while ((accepted = accept(client,(struct sockaddr *)&direc,&tamano)) > 0) {
        /*Create the thread and pass the socket descriptor*/
        if( pthread_create(new_thread, &thread_attributes, &handle_tcp_connection, (void *)accepted) != 0){
          perror("create thread");
          exit(EXIT_FAILURE);
        }
    }
    

    【讨论】:

    • 几个问题:1) C++ 有自己的线程模型(你可能应该使用它)。 2)线程不能很好地扩展(这可能适用于 10 个(如果幸运的话,可能是 100 个)连接。之后它不是一个好的模型。旨在使用 select() 或其继任者之一。
    • 让我不同意 select() 唯一的解决方案。如果可伸缩性是问题,最好的方法是使用线程和select() 的线程池。但首先我认为线程是最简单的解决方案,因为select() 使处理有点复杂。
    • @LokiAstari 您可以从 C++ 线程服务器中获得比仅仅一百个更高的并发级别。
    • @Manos:线程池没有问题,选择它就可以了。最好是 select() 替换之一。
    • @PSkocik:关于主题A Design Framework for Highly Concurrent Systems 的著名论文我将引导您查看第二部分中的吞吐量图。在理想的条件下(我的意思是理想的),100 处的吞吐量峰值通常要低得多,但这取决于具体的工作量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    • 2014-08-18
    • 2017-05-26
    • 2014-03-10
    • 1970-01-01
    • 2016-10-05
    • 2023-03-10
    相关资源
    最近更新 更多