处理多个客户端的单个 TCP/IP 服务器(在 C++ 中)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25091148/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:14:44  来源:igfitidea点击:

Single TCP/IP server that handles multiple clients (in C++)?

c++multithreadingsocketsnetworkingtcp

提问by adam10603

I want to write a TCP/IP server in C++ (using bind(), accept()etc.) that can deal with multiple clients connecting to it at the same time. I have read a few topics about this, and everyone is suggesting the following (dirty pseudocode coming up):

我想写C ++中的TCP / IP服务器(使用bind()accept()等),可以处理多个客户端连接到它在同一时间。我已经阅读了一些关于此的主题,每个人都提出以下建议(即将出现脏伪代码):

set up server, bind it

while (1) {
    accept connection
    launch new thread to handle it
}

Which would work totally fine on a machine that has multiple threads. But my target system is a single core machine without any hardware threads. For a little test, I tried launching multiple threads via std::threadon the system but they were executed one after the other. No parallel goodness :(

在具有多个线程的机器上完全可以正常工作。但是我的目标系统是没有任何硬件线程的单核机器。为了进行一个小测试,我尝试std::thread在系统上启动多个线程,但它们一个接一个地执行。没有平行的善良:(

That makes it impossible to implement the algorithm above. I mean, I'm sure it can be done, I just don't know how, so I would really appreciate any help.

这使得无法实现上述算法。我的意思是,我确定它可以完成,我只是不知道如何,所以我真的很感激任何帮助。

采纳答案by John Zwinck

You don't need threads, you need asynchronous or "event-driven" programming. This can be done with select() if you want cross-platform, or epoll() if you're on Linux and want to support thousands of clients at once.

您不需要线程,您需要异步或“事件驱动”编程。如果您想要跨平台,可以使用 select() 或 epoll() 如果您在 Linux 上并希望一次支持数千个客户端,则可以完成此操作。

But you don't need to implement this all from scratch--you can use Boost ASIO(some of which may become part of C++17) or a C library like libeventor libevor libuv. Those will handle a bunch of subtle details for you, and help you get more quickly to the real business of your application.

但是您不需要从头开始实现这一切——您可以使用Boost ASIO(其中一些可能成为 C++17 的一部分)或像libeventlibevlibuv这样的 C 库。这些将为您处理许多微妙的细节,并帮助您更快地了解应用程序的实际业务。

回答by jxh

I am going to take a guess about what your actual code looks like.

我将猜测您的实际代码是什么样的。

void new_connection (int sock) {
    //...handle new connection
}

void accept_loop (int listen_sock) {
    int new_sock;

    while ((new_sock = accept(listen_sock, 0, 0)) != -1) {
        std::thread t(new_connection, new_sock);
    }
}

The problem with this code is that the thread's destructor is called when the loop reiterates. This will cause an exception to be thrown since the destructor will detect the thread context is still active.

这段代码的问题是在循环重复时调用了线程的析构函数。这将导致抛出异常,因为析构函数将检测到线程上下文仍处于活动状态。

To avoid that problem, you can detach the thread object from the active context.

为避免该问题,您可以从活动上下文中分离线程对象。

    while ((new_sock = accept(listen_sock, 0, 0)) != -1) {
        std::thread t(new_connection, new_sock);
        t.detach();
    }

What follows is a mostly complete example (without error checking). This routine creates an accepting socket for a server specification (which is "host:port" for a particular interface, or ":port" for any interface).

下面是一个大致完整的示例(没有错误检查)。该例程为服务器规范创建一个接受套接字(对于特定接口是“host:port”,对于任何接口是“:port”)。

int make_accept_sock (const char *servspec) {
    const int one = 1;
    struct addrinfo hints = {};
    struct addrinfo *res = 0, *ai = 0, *ai4 = 0;
    char *node = strdup(servspec);
    char *service = strrchr(node, ':');
    int sock;

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

    *service++ = '
void accept_loop (const char *servspec) {
    int sock = make_accept_sock(servspec);

    for (;;) {
        int new_sock = accept(sock, 0, 0);
        std::thread t(new_connection, new_sock);
        t.detach();
    }
}
'; getaddrinfo(*node ? node : "0::0", service, &hints, &res); free(node); for (ai = res; ai; ai = ai->ai_next) { if (ai->ai_family == PF_INET6) break; else if (ai->ai_family == PF_INET) ai4 = ai; } ai = ai ? ai : ai4; sock = socket(ai->ai_family, SOCK_STREAM, 0); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); bind(sock, ai->ai_addr, ai->ai_addrlen); listen(sock, 256); freeaddrinfo(res); return sock; }

The accepting loop routine creates the listening socket, and then launches threads to handle each new incoming connection.

接受循环例程创建侦听套接字,然后启动线程来处理每个新的传入连接。

void new_connection (int sock) {
    ssize_t r;
    while (!isclosed(sock)) {
        r = send(sock, ".\n", 2, 0);
        if (r < 0) break;
        sleep(1);
    }
    close(sock);
}

The new connection handler just outputs a .followed by a newline every second. The isclosed()function can be found among answers to this question.

新的连接处理程序.每秒只输出一个后跟一个换行符。该isclosed()功能可以在这个问题的答案中找到。

int main (int argc, char *argv[])
{
    const char *server = ":11111";

    signal(SIGPIPE, SIG_IGN);

    if (argc > 1) server = argv[1];

    accept_loop(server);

    return 0;
}

And then the main function just ties it all together.

然后主要功能只是将它们联系在一起。

##代码##