【问题标题】:Socket performance插座性能
【发布时间】:2014-10-01 04:09:15
【问题描述】:

我只是想知道即时通讯工具和在线游戏如何能够如此快速地接受和传递消息。 (使用套接字进行网络编程)

我了解到这是通过 非阻塞套接字 完成的。 我尝试了 blocking sockets 使用 pthreads(每个客户端都有自己的线程)和 nonblocking sockets 使用kqueue。然后我用一个建立 99 个连接的程序(每个一个线程中的连接),然后向它写入一些垃圾(睡眠时间为 1 秒)。当所有线程都设置好后,我在主线程中测量了从服务器获得连接所需的时间(带有挂钟时间)(当“99 个用户”正在写入时)。

threads (avg): 0.000350 // only small difference to kqueue kqueue (avg): 0.000300 // and this is not even stable (client side)

问题是,在使用 kqueue 进行测试时,我多次收到 SIGPIPE 错误(客户端)。 (有一点超时usleep(50) 这个错误是固定的)。我认为这真的很糟糕,因为服务器应该能够处理数千个连接。 (或者是我在客户端的错?)关于这个的疯狂的事情是臭名昭著的 pthread 方法做得很好(有和没有超时)。

所以我的问题是:如何在C 中构建一个可以“异步”处理数千个客户端的稳定 套接字服务器?我只认为线程方法是一件好事,但这被认为是不好的做法。

问候

编辑:

我的测试代码:

double get_wall_time(){
    struct timeval time;
    if (gettimeofday(&time,NULL)){
        //  Handle error
        return 0;
    }
    return (double)time.tv_sec + (double)time.tv_usec * .000001;
}

#define NTHREADS    100

volatile unsigned n_threads = 0;
volatile unsigned n_writes  = 0;

pthread_mutex_t main_ready;
pthread_mutex_t stop_mtx;
volatile bool running = true;

void stop(void)
{
    pthread_mutex_lock(&stop_mtx);
    running = false;
    pthread_mutex_unlock(&stop_mtx);
}

bool shouldRun(void)
{
    bool copy;

    pthread_mutex_lock(&stop_mtx);
    copy = running;
    pthread_mutex_unlock(&stop_mtx);

    return copy;
}

#define TARGET_HOST "localhost"
#define TARGET_PORT "1336"

void *thread(void *args)
{
    char tmp = 0x01;

    if (__sync_add_and_fetch(&n_threads, 1) == NTHREADS) {
        pthread_mutex_unlock(&main_ready);

        fprintf(stderr, "All %u Threads are ready...\n", (unsigned)n_threads);
    }

    int fd = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);

    if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) {
        socket_close(fd);

        fd = -1;
    }

    if (fd <= 0) {
        fprintf(stderr, "socket_create failed\n");
    }

    if (write(fd, &tmp, 1) <= 0) {
        fprintf(stderr, "pre-write failed\n");
    }

    do {
        /* Write some garbage */
        if (write(fd, &tmp, 1) <= 0) {
            fprintf(stderr, "in-write failed\n");

            break;
        }

        __sync_add_and_fetch(&n_writes, 1);

        /* Wait some time */
        usleep(500);
    } while (shouldRun());

    socket_close(fd);

    return NULL;
}

int main(int argc, const char * argv[])
{
    pthread_t threads[NTHREADS];

    pthread_mutex_init(&main_ready, NULL);
    pthread_mutex_lock(&main_ready);
    pthread_mutex_init(&stop_mtx, NULL);

    bzero((char *)&hint, sizeof(hint));
    hint.ai_socktype    = SOCK_STREAM;
    hint.ai_family      = AF_INET;

    if (getaddrinfo(TARGET_HOST, TARGET_PORT, &hint, &res) != 0) {
        return -1;
    }

    for (int i = 0; i < NTHREADS; ++i) {
        pthread_create(&threads[i], NULL, thread, NULL);
    }

    /* wait for all threads to be set up */
    pthread_mutex_lock(&main_ready);

    fprintf(stderr, "Main thread is ready...\n");

    {
        double start, end;
        int fd;

        start = get_wall_time();

        fd = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);

        if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) {
            socket_close(fd);

            fd = -1;
        }

        end = get_wall_time();

        if (fd > 0) {
            fprintf(stderr, "Took %f ms\n", (end - start) * 1000);

            socket_close(fd);
        }
    }

    /* Stop all running threads */
    stop();
    /* Waiting for termination */
    for (int i = 0; i < NTHREADS; ++i) {
        pthread_join(threads[i], NULL);
    }

    fprintf(stderr, "Performed %u successfull writes\n", (unsigned)n_writes);

    /* Lol.. */
    freeaddrinfo(res);

    return 0;
}

SIGPIPE 在我尝试连接到kqueue 服务器时出现(在建立 10 个连接后,服务器“卡住”了?)。当太多用户在写东西时,服务器无法打开新连接。 (kqueue 服务器代码来自http://eradman.com/posts/kqueue-tcp.html

【问题讨论】:

  • 你的问题真的是“如何编写高性能 TCP 服务器代码?”
  • @DavidSchwartz 是的。确实。
  • 阅读选择。阅读有关 unix 网络编程的经典书籍

标签: c multithreading sockets network-programming


【解决方案1】:

SIGPIPE 表示您正在尝试写入另一端已关闭的套接字(或管道)(因此没有人能够读取它)。如果您不在乎,可以忽略SIGPIPE 信号(致电signal(SIGPIPE, SIG_IGN)),信号不会有问题。当然,套接字上的write(或send)调用仍然会失败(使用EPIPE),所以你需要让你的代码足够健壮来处理这个问题。

SIGPIPE 通常会杀死进程的原因是,编写忽略write/send 调用错误的程序太容易了,否则会占用 100% 的 CPU 时间。只要您始终仔细检查错误并处理它们,您就可以放心地忽略SIGPIPEs

【讨论】:

    【解决方案2】:

    还是我的错?

    这是你的错。 TCP 工作。很可能您没有读取发送的所有数据。

    当用户写东西太多时,服务器无法打开新连接

    服务器不打开连接。客户端打开连接。服务器接受连接。如果您的服务器停止这样做,则您的接受循环有问题。它应该只做两件事:接受一个连接,并启动一个线程。

    【讨论】:

    • 我是认真的。不多也不少。此错误通常是由写入已被另一端关闭的连接引起的。换句话说,应用程序协议错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-19
    相关资源
    最近更新 更多