接受总是返回相同的套接字 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;
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是您正在accept连接并分离一个线程来为其提供服务(我认为这cli_thread是最终打算做的),但是您的主线程正在关闭文件描述符。
同一进程中的不同线程共享相同的文件描述符。如果你在主线程中关闭它,你的另一个线程就不能使用它。
这与fork. 当您 fork 时,您正在创建一个单独的进程——该进程在 fork 时获取每个文件描述符的副本,因此在您分离子进程后关闭主进程中的文件描述符就可以了(它将在孩子中保持开放)。
无论是分叉还是创建线程,在分离子进程/服务线程后写入主线程中的套接字可能是一个坏主意。通过这样做,你创造了一场比赛。大多数时候,父/主线程会write在子/服务线程之前调用。但偶尔由于随机事件(其他进程的中断/抢占),子线程或服务线程会完全启动并首先达到自己的写入。这可能会让对等方不知道接下来会发生什么(假设这会对您的协议产生影响)。
还有一两件事,这是一个不好的做法和危险的传递地址的conn_fd给服务线程。conn_fd是主线程中的局部变量。如果您的主线程要返回,您提供给服务线程的指针现在指向可能已被重用的堆栈空间,谁知道那里将存储什么。
您可以通过conn_fd适当地将值直接传递给服务线程:
pthread_create(&cli_thread, NULL, cli_func, (void *)(uintptr_t)connfd)
Run Code Online (Sandbox Code Playgroud)
然后在服务线程中:
void *cli_func(void *arg){
int conn_fd = (uintptr_t)arg;
Run Code Online (Sandbox Code Playgroud)
(uintptr_t在中声明<stdint.h>并保证是一个与指针大小相同的整数,因此使用它作为中介可以防止 gcc 抱怨从指针转换为不同大小的整数 [或反之亦然]。)