【问题标题】:C - accept() accepting the same client twice?C - accept() 两次接受同一个客户端?
【发布时间】:2014-07-04 12:53:55
【问题描述】:

我面临着我生命中最奇怪的编程问题之一。 我过去建了几台服务器,客户端可以正常连接,没有任何问题。

现在我正在创建一个基本上是 Web 服务器的服务器。但是,我面临着一个非常奇怪的情况(至少对我而言)。

假设您连接到 localhost:8080 并且 accept() 接受您的连接,然后代码将在单独的线程中处理您的请求(想法是在每个子节点上拥有多个分叉和线程 - 这是在另一个文件上实现的暂时但我在该设置上也面临这个问题所以......最好先让它简单)。因此,您的请求得到处理,但在处理完毕并关闭套接字之后,您在浏览器上看到输出,accept() 再次接受连接 - 但当然没有人连接,因为只创建了一个连接。

recv 之后的 errno = 0(成功)(这就是程序崩溃的地方) recv 虽然返回 0 - 所以没有读取字节(当然,因为连接不应该存在)

int main(int argc, char * argv[]){
  int sock;
  int fd_list[2];
  int fork_id;

  /* Socket */
  sock=create_socket(PORT);

    int i, active_n=0;
    pthread_t tvec;
    char address[BUFFSIZE];
    thread_buffer t_buffer;
    int msgsock;

    conf = read_config("./www.config");
    if(conf == NULL)
    {
        conf = (config*)malloc(sizeof(config));
        if(conf == NULL)
        {
            perror("\nError allocating configuration:");
            exit(-1);
        }

        // Set defaults
        sprintf(conf->httpdocs, DOCUMENT_ROOT);
        sprintf(conf->cgibin, CGI_ROOT);
    }

    while(cicle) {

        printf("\tWaiting for connections\n");
        // Waits for a client
        msgsock = wait_connection(sock, address);
        printf("\nSocket: %d\n", msgsock);

        t_buffer.msg = &address;
        t_buffer.sock = msgsock;
        t_buffer.conf = conf;

        /* Send socket to thread */

        if (pthread_create(&tvec, NULL, thread_func, (void*)&t_buffer) != 0)
        {
                perror("Error creating thread: ");
                exit(-1);

        }
  }

  free(conf);

  return 0;
}

这里有两个重要的函数:

int create_socket(int port) {
    struct sockaddr_in server, remote;
    char buffer[BUFF];
    int sock;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("opening stream socket");
        exit(1);
    }

    server.sin_family  = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_in))) {
        perror("binding stream socket");
        exit(1);
    }
    gethostname(buffer, BUFF);
    printf("\n\tServidor a espera de ligações.\n");
    printf("\tUse o endereço %s:%d\n\n", buffer,port);

    if (listen(sock, MAXPENDING) < 0) {
        perror("Impossível criar o socket. O servidor vai sair.\n");
        exit(1);
    }
    return(sock);
}

int wait_connection(int serversock, char *remote_address){
    int clientlen;
    int clientsock;

    struct sockaddr_in  echoclient;

    clientlen = sizeof(echoclient);
    /* Wait for client connection */
    if ((clientsock = accept(serversock, (struct sockaddr *) &echoclient, &clientlen)) < 0)
    {
        perror("Impossivel estabelecer ligacao ao cliente. O servidor vai sair.\n");
        exit(-1);
    }

    printf("\n11111111111111Received request - %d\n", clientsock);

    sprintf(remote_address, "%s", inet_ntoa(echoclient.sin_addr));

    return clientsock;
}

所以基本上你会看到: 11111111111111收到请求 - D

D 两次都不同,所以 fd 肯定不同。

两次!一个接一个地被处理,然后在线程函数中的recv之后爆炸。有时需要一点时间来处理和显示第二个,但它会在几秒钟后完成。现在,这并不总是发生。有时会,有时不会。

这太奇怪了……

我已经推出了作为插件导致它重新连接的可能性,因为 Apache 的 ab 工具在几次请求后会导致同样的问题。

我想指出,即使我不为客户端运行线程并简单地关闭套接字,它也会发生!我已经考虑过标头未被完全读取的可能性,因此浏览器会发送另一个请求。但是浏览器会正确接收数据,否则将无法正常显示结果,如果显示结果正常,则连接必须已关闭 - 否则应出现连接重置。

有什么建议吗?感谢您的帮助。

编辑: 如果我取出代码的启动线程部分,有时连接被接受4、5、6次......

编辑2:请注意,我知道recv失败后程序会崩溃,我故意退出。

【问题讨论】:

  • 我正在使用 Apache 的 ab 工具对其进行测试,我得到了相同的结果,所以我怀疑它是浏览器。至于 fork 的事情,我现在只是使用一个简单的线程服务器设置来测试它,forks 系统在其他地方有注释。
  • 当对端执行有序关闭时,recv 的返回值为 0。您应该立即关闭套接字。也许您的浏览器连接了多次。
  • 问题是,ab 也这样做!不只是我的浏览器。 Apache 的 ab 也可以做到这一点。发生这种情况时,我目前正在关闭套接字并退出相应的线程(在我发布的代码中,我关闭了程序以便对其进行测试)
  • 你也谈到了fork()。你确定你没有在这里复制一些东西吗?
  • 是的,如果您仔细查看我所说的内容(在我的帖子和下面的答案中),您会看到分叉部分已被评论,根本没有运行。您在第一篇文章中看到的大部分代码都没有包含等。

标签: c multithreading sockets


【解决方案1】:

这肯定是一个等待发生的错误:

  pthread_create(&tvec, NULL, thread_func, (void*)&t_buffer

您正在将局部变量 t_buffer 传递给您的线程。下次您接受客户时,可能会发生这种情况 在另一个客户端完成之前,您也会将相同的变量传递给该线程,从而导致许多非常不确定的行为。(例如,从同一个连接读取的 2 个线程、描述符上的双 close() 和其他奇怪的事情。)

不是将相同的局部变量传递给每个线程,而是为每个新客户端动态分配一个新的t_buffer

【讨论】:

  • 你是对的,完全错过了!我会这样做,很快就会回来提供更多信息。非常感谢。
  • 这一定是使用pthread_create()时最常见的错误。
  • @nos 我已经做到了,但我仍然面临同样的问题,所以我们可以排除这个问题的原因! (虽然可能是其他一些我没有注意到的)
  • 即使你为每个t_buffer分配单独的内存,它仍然包含一个指向主线程本地的address的指针,所以每个线程都将共享这个资源,并且每次获得连接时,wait_connection() 都会覆盖它。这段代码是非常不安全的,你得到“奇怪”的行为并不奇怪。
  • @DiogoParrinha 然后发布你的新代码,也许你没有做对。还有thread_func的代码。并进行一些调试,例如找出源 IP 地址和端口号,并确定它们是否有意义。 wireshark 在这里也是一个非常有用的工具,因此您可以看到在线上实际发生的事情。
【解决方案2】:

假设 ... 在处理完套接字并关闭后,您在浏览器上看到输出,accept() 再次接受连接 - 但当然没有人连接,因为只创建了一个连接。

所以如果没有人连接,就没有什么可以接受(),所以这永远不会发生。

所以无论你看到什么,都不是。

【讨论】:

  • 因此我说这是我一生中在编程时发现的最奇怪的问题之一。为什么只发送一个请求时,accept() 会处理两个请求?
猜你喜欢
  • 1970-01-01
  • 2018-05-28
  • 2018-04-16
  • 2014-09-25
  • 1970-01-01
  • 2014-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多