accept的一个小陷阱

先看下面的代码:

监听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),这也是以前为什么从第二次开始就正确了的原因。

原文地址:https://www.cnblogs.com/javawebsoa/p/3109122.html