zoukankan      html  css  js  c++  java
  • 转: 由socket的accept说开去

    from: http://ticktick.blog.51cto.com/823160/779866

    今天与同学争执一个话题:由于socket的accept函数在有客户端连接的时候产生了新的socket用于服务该客户端,那么,这个新的socket到底有没有占用一个新的端口?

        

    讨论完后,才发现,自己虽然熟悉socket的编程套路,但是却并不是那么清楚socket的原理,今天就趁这个机会,把有关socket编程的几个疑问给搞清楚吧。

        

    先给出一个典型的TCP/IP通信示意图。

     

        

    问题一:socket结构体对象究竟是怎样定义的?

        

    我们知道,在使用socket编程之前,需要调用socket函数创建一个socket对象,该函数返回该socket对象的描述符。

    函数原型:int socket(int domain, int type, int protocol);

        

    那么,这个socket对象究竟是怎么定义的呢?它记录了哪些信息呢?只记录了本机IP及端口、还是目的IP及端口、或者都记录了?

        

    关于这个问题,大家可以在内核源码里面找,也可以参考这篇文章《struct socket 结构详解》,我们可以看到 socket  结构体的定义如下:   

    struct socket   
    {   
        socket_state              state;   
        unsigned long             flags;   
        const struct proto_ops    *ops;   
        struct fasync_struct      *fasync_list;   
        struct file               *file;   
        struct sock               *sk;   
        wait_queue_head_t         wait;   
        short                     type;   
    };

        

    其中,struct sock 包含有一个 sock_common 结构体,而sock_common结构体又包含有struct inet_sock 结构体,而struct inet_sock 结构体的部分定义如下:

    struct inet_sock   
    {   
        struct sock     sk;   
    #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)   
        struct ipv6_pinfo   *pinet6;   
    #endif   
        __u32           daddr;          //IPv4的目的地址。   
        __u32           rcv_saddr;      //IPv4的本地接收地址。   
        __u16           dport;          //目的端口。   
        __u16           num;            //本地端口(主机字节序)。  
        
        …………      
    }

         

    由此,我们清楚了,socket结构体不仅仅记录了本地的IP和端口号,还记录了目的IP和端口。

         

    问题二:connect函数究竟做了些什么操作?

         

    在TCP客户端,首先调用一个socket()函数,得到一个socket描述符socketfd,然后通过connect函数对服务器进行连接,连接成功后,就可以利用这个socketfd描述符使用send/recv函数收发数据了。

        

    关于connect函数和send函数的原型如下:

    int connect( int sockfd, const struct sockaddr* server_addr, socklen_t addrlen)  
     
    int send( int sockfd, const void *msg,int len,int flags);

        

    那么,现在的困惑是,为什么send函数仅仅传入sockfd就可以知道服务器的ip和端口号?

        

    其实,由“问题一”中的答案我们已经很清楚了,sockfd 描述符所描述的socket对象不仅包含了本地IP和端口,同时也包含了服务器的IP和端口,这样,才能使得send函数只需要传入sockfd 即可知道该把数据发向什么地方。而代码中,目的IP和端口只是在connect函数中出现过,因此,肯定是connect函数在成功建立连接后,将目的IP和端口写入了sockfd 描述符所描述的socket对象中。

        

    问题三: accept函数产生的socket有没有占用新的端口?

        

    首先,回顾一下accept函数,原型如下:

    /* 参数:sockfd 监听套接字,即服务器端创建的用于listen的socket描述符。  
     * 参数:addr  这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址  
     * 参数:len 描述 addr 的长度  
     */ 
    int accept(int sockfd, struct sockaddr* addr, socklen_t* len)

        

    accept函数主要用于服务器端,一般位于listen函数之后,默认会阻塞进程,直到有一个客户请求连接,建立好连接后,它返回的一个新的套接字 socketfd_new ,此后,服务器端即可使用这个新的套接字socketfd_new与该客户端进行通信,而sockfd 则继续用于监听其他客户端的连接请求。

        

    至此,我的困惑产生了,这个新的套接字 socketfd_new 与监听套接字sockfd 是什么关系?它所代表的socket对象包含了哪些信息?socketfd_new 是否占用了新的端口与客户端通信?

        

    先简单分析一番,由于网站的服务器也是一种TCP服务器,使用的是80端口,并不会因客户端的连接而产生新的端口给客户端服务,该客户端依然是向服务器端的80端口发送数据,其他客户端依然向80端口申请连接。因此,可以判断,socketfd_new 并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd_new一样的端口号。

        

    那这么说,难道一个端口可以被两个socket对象绑定?当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?
     
        

    我是这么理解的(欢迎拍砖)。

        

    首先,一个端口肯定只能绑定一个socket。我认为,服务器端的端口在bind的时候已经绑定到了监听套接字socetfd所描述的对象上,accept函数新创建的socket对象其实并没有进行端口的占有,而是复制了socetfd的本地IP和端口号,并且记录了连接过来的客户端的IP和端口号。

        

    那么,当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?

        

    客户端发送过来的数据可以分为2种,一种是连接请求,一种是已经建立好连接后的数据传输。

        

    由于TCP/IP协议栈是维护着一个接收和发送缓冲区的。在接收到来自客户端的数据包后,服务器端的TCP/IP协议栈应该会做如下处理:如果收到的是请求连接的数据包,则传给监听着连接请求端口的socetfd套接字,进行accept处理;如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲区。这样,当服务器端需要读取指定客户端的数据时,则可以利用socketfd_new 套接字通过recv或者read函数到缓冲区里面去取指定的数据(因为socketfd_new代表的socket对象记录了客户端IP和端口,因此可以鉴别)。

    这就是我对socket编程的一些疑问的理解,有不正确的地方欢迎留言或者来信lujun.hust@gmail.com交流。 

    本文出自 “Jhuster的专栏” 博客,请务必保留此出处http://ticktick.blog.51cto.com/823160/779866

  • 相关阅读:
    Security headers quick reference Learn more about headers that can keep your site safe and quickly look up the most important details.
    Missing dollar riddle
    Where Did the Other Dollar Go, Jeff?
    proteus 与 keil 联调
    cisco router nat
    router dhcp and dns listen
    配置802.1x在交换机的端口验证设置
    ASAv931安装&初始化及ASDM管理
    S5700与Cisco ACS做802.1x认证
    playwright
  • 原文地址:https://www.cnblogs.com/jhj117/p/5914668.html
Copyright © 2011-2022 走看看