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

  • 相关阅读:
    react父子组件之间传值
    MVC、MVP、MVVM模式的概念与区别
    exports、module.exports 和 export、export default
    进程与线程以及它们的区别
    axios详解
    箭头函数详解
    ES6扩展运算符...
    vue子组件数据跟着父组件改变
    JS实现千分位
    在.NET Core使用TimeZone将客户端时间转服务器本地时间但编译提示已过期
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3109122.html
Copyright © 2020-2023  润新知