先看下面的代码:
监听127.0.0.1 : 5563 ,如果有连接,就输出这个客户端的IP、端口和连接描述符。
#include <stdio.h> #include <arpa/inet.h> int main(int argc,char** argv){ int _socket = 0 ; struct sockaddr_in addr_server,addr_client; _socket = socket(AF_INET, SOCK_STREAM, 0); addr_server.sin_family = AF_INET; addr_server.sin_addr.s_addr = htonl(INADDR_ANY); addr_server.sin_port = htons(5563); int ret = bind(_socket,(struct sockaddr *)&addr_server, sizeof(addr_server)); ret = listen(_socket, 16); int length ; while(1){ int fd = accept(_socket, (struct sockaddr *)&addr_client, &length); if (fd == -1) { break; } printf("IP:%s.Port:%d.Fd:%d Connect!\n",inet_ntoa(addr_client.sin_addr),addr_client.sin_port,fd); } return 0; }
在执行gcc -o accept accept.c之后,分两次在客户端用telnet进行连接,输出如下:
[root@localhost network]# ./accept IP:255.127.0.0.Port:22011.Fd:4 Connect! IP:127.0.0.1.Port:25237.Fd:5 Connect!
注意:第一次的IP输出的并不是127.0.0.1,而是一个其它的IP地址,事实上第一次在每次启动的时候,所输出的IP都不相同。
accept定义如下:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数说明
1)sockfd 。通过调用socket函数所返回的socket 文件描述符。
2)addr。sockaddr结构,这个参数在accept成功返回之后,将会被socket连接另一端的地址填充,也就是会包含客户端的IP和端口信息。
3)addrlen。这是一个引用类型的参数,调用accept的人必须要对这个值以初始为第二个参数addr的结构所包含的字节数大小。当accept返回之后,它将包含从另一端返回的实际字节数大小。
那么问题就出来了,在前面的代码中第一次调用 accept时,并没有指定length参数的值为addr结构体大小,这样内核就会以为这个结构大小为0,因此不足以存储将客户端的信息返回,但在第一次以后,length的值就被内核修改了,改成了真实的结构返回大小,而后每次调用就正常了。
修改如下:
#include <stdio.h> #include <arpa/inet.h> int main(int argc,char** argv){ int _socket = 0 ; struct sockaddr_in addr_server,addr_client; _socket = socket(AF_INET, SOCK_STREAM, 0); addr_server.sin_family = AF_INET; addr_server.sin_addr.s_addr = htonl(INADDR_ANY); addr_server.sin_port = htons(5563); int ret = bind(_socket,(struct sockaddr *)&addr_server, sizeof(addr_server)); ret = listen(_socket, 16); int length ; while(1){ length = sizeof(struct sockaddr_in); int fd = accept(_socket, (struct sockaddr *)&addr_client, &length); if (fd == -1) { break; } printf("IP:%s.Port:%d.Fd:%d %d Connect!\n",inet_ntoa(addr_client.sin_addr),addr_client.sin_port,fd,length); } return 0; }
运行就正常了:
[root@localhost network]# ./accept IP:127.0.0.1.Port:26005.Fd:4 16 Connect! IP:127.0.0.1.Port:26261.Fd:5 16 Connect! IP:127.0.0.1.Port:26517.Fd:6 16 Connect! IP:127.0.0.1.Port:26773.Fd:7 16 Connect!
从结果可以看出,第一次显示的IP也是正确的,然后每次accept返回之后,length的值都为sizeof(struct sockaddr_in),这也是以前为什么从第二次开始就正确了的原因。