IO多路复用聊天室首连客户端IP显示为0.0.0.0问题求助
客户端IP显示为0.0.0.0的问题排查与解决
问题描述
用IO多路复用实现简易聊天室时,发现首个连接服务器的客户端IP始终显示为0.0.0.0,但客户端配置的连接地址明明是127.0.0.1。
复现步骤
- 执行命令
gcc server.c -o server && echo "compile finished" && ./server启动服务器; - 编译客户端:
gcc client.c -o client; - 启动客户端后按Ctrl-C中断,此时可观察到首个客户端IP显示为
0.0.0.0。
代码示例
服务端代码
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <stdio.h> #include <sys/select.h> #include <unistd.h> #include <string.h> #include <setjmp.h> #define SERVER_PORT 9098 #define MAX_OR_ADD_LENGTH 100 #define MAX_NAME_SIZE 40 int main(void){ /* * create an array about three socket fd, the first is server * */ int array_fds[3], array_index = 1; int max_fd_p; int server_fd = socket(AF_INET, SOCK_STREAM, 0), client_fd; // create if(server_fd == -1){ perror("create socket descriptor error:"); exit(-1); } char message[1024]; socklen_t len; struct sockaddr_in server_addr, client_addr; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_family = AF_INET; if(inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) != 1){ perror("alloc address error"); exit(-1); } if(bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0){ perror("bind error:"); exit(-1); } listen(server_fd, 1000); // ssize_t n; // array_fds[0] = server_fd; max_fd_p = server_fd + 1; fd_set fds; while (1){ // IO multiplexing int max_resp; FD_ZERO(&fds); memset(message, 0, sizeof(message)); // for(int i=0;i<3;i++){ // if exit not add to select read list if(array_fds[i] != -1){ FD_SET(array_fds[i], &fds); } } if((max_resp = select(max_fd_p, &fds, NULL, NULL, NULL)) == -1){ perror("select error"); break; } for(int i=2; i>=0 && max_resp ;i--){ if(!FD_ISSET(array_fds[i], &fds)){ continue; } max_resp--; if(array_fds[i] == server_fd){ client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len); inet_ntop(AF_INET, &client_addr.sin_addr, message, sizeof(message)); printf("ip is %s\n", message); array_fds[array_index] = client_fd; // add to select read list if(client_fd >= max_fd_p){ // update maxfdp max_fd_p = client_fd + 1; } array_index++; continue; } if((n = read(array_fds[i], message, 1024)) != -1) { if(n == 0){ // if client exit, close and remove it; array_fds[i] = -1; close(array_fds[i]); continue; } if(write(array_fds[i], "ok, got your message", n) == -1){ perror("write error"); } } } } exit(0); }
客户端代码
#include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define SERVER_PORT 9098 int main(void){ int client_fd = socket(AF_INET, SOCK_STREAM, 0); if(client_fd == -1){ perror("create socket error"); exit(-1); } struct sockaddr_in client_addr; client_addr.sin_family = AF_INET; client_addr.sin_port = htons(SERVER_PORT); if(inet_pton(AF_INET, "127.0.0.1", &client_addr.sin_addr.s_addr) == -1){ perror("address error:"); exit(-1); } if(connect(client_fd, (struct sockaddr*)&client_addr, sizeof(client_addr)) == -1){ perror("connect error"); exit(-1); } char message[1024], input_message[1024]; ssize_t n; printf("input your name:"); fgets(input_message, 1024, stdin); if(write(client_fd, input_message, 1024) == -1){ perror("write error"); } while (1){ memset(message, 0, sizeof(message)); if((n = read(client_fd, message, 1024)) != -1){ message[n] = 0; printf("[server]: %s\n", message); } memset(message, 0, sizeof(message)); printf("input message you want to send:"); fgets(input_message, 1024, stdin); if(write(client_fd, input_message, 1024) == -1){ perror("write error"); } } }
问题截图

原因分析与修复方案
问题原因
服务端代码中调用accept函数时,第三个参数socklen_t len未初始化。accept的第三个参数是输入输出参数:调用时需要传入缓冲区(client_addr)的大小,内核才会把完整的客户端地址信息写入缓冲区。未初始化的len是随机值,大概率小于struct sockaddr_in的实际大小,导致内核无法完整写入客户端IP地址,最终inet_ntop解析出错误的0.0.0.0。
修复方案
在调用accept前,给len赋值为client_addr的大小:
// 新增这行初始化代码 len = sizeof(client_addr); client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
修改后,accept能正确获取客户端的完整地址信息,解析出的IP就会是客户端实际连接的127.0.0.1。
内容的提问来源于stack exchange,提问作者ssayno
相关产品推荐
相关产品推荐

