【问题标题】:C Sockets: accept() always returns the same socket ID?C 套接字:accept() 总是返回相同的套接字 ID?
【发布时间】:2017-08-11 18:48:04
【问题描述】:

accept 总是返回相同的套接字 ID/文件描述符 (connfd: 4)

无论我是在同一台本地机器上使用两个不同的进程还是在两台不同的机器上。为什么返回值不一样?

不确定它是否与输出有关。我以前只使用 printf(),但 stdout 上没有输出,所以我将其更改为 fprintf(stderr, ...)。我也尝试使用 fflush(),同样的东西,来自 accept() 的相同套接字 ID/描述符

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <pthread.h> /* -pthread */

#define SERVER_PORT 5000

void *cli_func(void *arg);

int main(int argc, char* argv[]){
        fflush(stdout);
        int listenfd = 0, connfd = 0;
        struct sockaddr_in serv_addr;

        char sendBuff[1025];
        time_t ticks;
        listenfd = socket(AF_INET, SOCK_STREAM, 0);

        fprintf(stderr, "Starting server...\n");

        memset(&serv_addr, '0', sizeof(serv_addr));
        memset(sendBuff, '0', sizeof(sendBuff));

        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port = htons(SERVER_PORT);

        bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

        listen(listenfd, 10);

        fprintf(stderr, "Listening...\n");

        while(1){

                connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
                pthread_t cli_thread;
                fprintf(stderr, "Got new client\n");

                if(pthread_create(&cli_thread, NULL, cli_func, &connfd)){
                        fprintf(stderr, "Error creating thread\n");
                        return 1;
                }
                /*
                if(pthread_join(cli_thread, NULL)){
                        fprintf(stderr, "Error joining thread\n");
                        return 2;
                }
                */
                ticks = time(NULL);
                snprintf(sendBuff, sizeof(sendBuff), "%.24s\r\n", ctime(&ticks));
                write(connfd, sendBuff, strlen(sendBuff));

                close(connfd);

                sleep(1);

        }

}

void *cli_func(void *arg){
        int* clifd_ptr = (int *)arg;
        fprintf(stderr, "client ID: %d", *clifd_ptr);

        return NULL;
}

【问题讨论】:

  • 它返回最低的可用文件描述符。一旦你close它,它就会再次可用。
  • 为什么 fd 的值很重要?它很可能是某个不透明的每进程表的索引。
  • 但是当我执行 write() 时,如果两个客户端的 fd 值相同,我如何指定要写入哪个客户端?

标签: c sockets


【解决方案1】:

这里的问题是你accepting 连接并关闭一个线程来服务它(我假设这是cli_thread 最终打算做的),但是你的主线程正在关闭文件描述符.

同一进程中的不同线程共享相同的文件描述符。如果您在主线程中关闭它,您的其他线程将无法使用它。

这与fork 不同。当你分叉时,你正在创建一个单独的进程——该进程在分叉时获取每个文件描述符的副本,所以在你之后关闭主进程中的文件描述符就可以了'已经分离出子进程(它将在子进程中保持打开状态)。

无论是分叉还是创建线程,在主线程中写入套接字可能是个坏主意分离子进程/服务线程之后。通过这样做,你创造了一场比赛。大多数时候,父/主线程将在子/服务线程之前到达write 调用。但偶尔由于随机事件(中断/被其他进程抢占),子线程或服务线程会完全启动并首先达到自己的写入。这可能会使对等方不知道接下来会发生什么(假设这会对您的协议产生影响)。

还有一点,将conn_fd地址 传递给服务线程是一种不好的做法和危险。 conn_fd 是主线程中的局部变量。如果您的主线程要返回,那么您给服务线程的指针现在指向可能已被重复使用的堆栈空间,谁知道将在那里存储什么。

您可以改为将 conn_fd 值通过适当的转换直接传递给服务线程:

pthread_create(&cli_thread, NULL, cli_func, (void *)(uintptr_t)connfd)

然后在服务线程中:

void *cli_func(void *arg){
    int conn_fd = (uintptr_t)arg;

(uintptr_t&lt;stdint.h&gt; 中声明,并保证是与指针大小相同的整数,因此使用它作为中介可以防止 gcc 抱怨从指针转换为不同大小的整数 [或反之亦然反之亦然]。)

【讨论】:

  • 非常感谢,这很有意义。不过问题是,如果我只传入一个 int 有什么关系?为什么需要强制转换为 uintptr_t?我不能在线程函数中指定参数可以是 int 并直接传入一个 int 值吗?
  • 大多数编译器至少会警告您传递错误的类型(如果没有产生实际错误)——并且使代码没有警告被认为是一种好习惯。指针-整数/整数-指针转换在 C 中是“实现定义的”,但另见stackoverflow.com/a/7826402/1076479。最后,作为参数传递但随后被解释为整数的指针值可能与随后转换为整数的同一指针不同(特别是考虑到字节顺序问题)。因此,显式转换更安全。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
  • 2013-12-26
  • 1970-01-01
  • 1970-01-01
  • 2013-01-10
相关资源
最近更新 更多