• linux网络编程之socket,bind,listen,connect,accept


    socket

           #include           /* See NOTES */
           #include 
    
           int socket(int domain, int type, int protocol);
    
    • 1
    • 2
    • 3
    • 4
    • domain:协议族,如AF_INET,AF_INET6等
    • type:套接字类型,如SOCK_STREAM,SOCK_DGRAM 等
    • protocol:协议,一般默认为0

    内核创建基本过程:

    1. 通过系统调用,陷入内核;
    2. 调用sock_create函数,创建socket结构体类型的实例sock,并设置sock的属性。如根据AF_INET可知,需要创建的是IPV4,调用其相应的创建函数;同样,根据type来创建是面向数据包还是流式的等等;
      在这里插入图片描述

    socket fd是对用户提供接口,与文件描述符关联

    而sock实例负责与内核协议栈进行对接。

    1. 将初始化好的sock与文件描述符关联起来。从task_srruct中拿到一个未使用的fd, 创建一个新的filefile->f_op = &socket_file_ops file->private_data = sock sock->file = file;
    2. 将该fd返回回去。

    bind

           #include           /* See NOTES */
           #include 
    
           int bind(int sockfd, const struct sockaddr *addr,
                    socklen_t addrlen);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • socket 需要绑定地址的监听socket的文件描述符
    • addr 服务端绑定的地址 IP + PORT
    • addrlen 传入addr数据的长度。实际上,传入的可能是IPV4,IPV6或者本地套接字格式。bind会根据传入的不同的addrlen值,就知道是哪种类型的地址,然后去解析addr.

    执行的基本流程:

    1. 根据fd文件描述符找到file实例,从file实例的private_data中拿到sock实例;
    2. 将sockaddr类型的实例拷贝到内核态;
    3. 调用sock->ops->bind()方法,从sock->sk取得sk,调用sk->sk_port->get_port()检查端口是否冲突,是否可以绑定。如果可以绑定,则初始化本地地址和端口。

    listen

           #include           /* See NOTES */
           #include 
    
           int listen(int sockfd, int backlog);
    
    • 1
    • 2
    • 3
    • 4
    • socket 表示需要监听的socket的文件描述符
    • 表示已完成ESTABLISHED且未aceept的队列大小,这个参数的大小决定了可以接收的并发数目。(该参数一般不使用)
      在这里插入图片描述

    内核调用的基本过程:

    1. 根据fd文件描述符找到file实例,从file实例的private_data中拿到sock实例;
    2. 调用sock->ops->listen(),从socket中拿到sock实例sk;
    3. 设置sk->sk_max_ack_backlog = backlog;
    4. 创建一个存放已连接的队列,icsk_accept_queue(三次握手后的连接);
    5. 最后设置sk的状态,sk_state_store(sk,TCP_LISTEN)

    connect

           #include           /* See NOTES */
           #include 
    
           int connect(int sockfd, const struct sockaddr *addr,
                       socklen_t addrlen);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • socket 表示客户端这边连接服务器时自己使用的socket,在connet前调用socket()进行创建
    • addr 表示服务端监听的套接字绑定着的 IP +PORT
    • addrlen addr的长度

    内核调用的基本过程:

    1. 根据fd文件描述符找到file实例,从file实例的private_data中拿到sock实例;
    2. 调用sock->ops->onnect(),从socket中拿到sock实例sk;
    3. 拿到sk是为了进行内核协议栈的操作,这里是调用sk->sk_port->connect;
    4. 握手报文处理,将数据发送到网卡

    accept

           #include           /* See NOTES */
           #include 
    
           int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
           #define _GNU_SOURCE             /* See feature_test_macros(7) */
           #include 
    
           int accept4(int sockfd, struct sockaddr *addr,
                       socklen_t *addrlen, int flags);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    内核处理的基本流程:

    1. 根据传入参数fd文件描述符找到file实例,从file实例的private_data中拿到sock实例;
    2. 调用sock->ops->accept
    3. 从socket中拿到sock实例sk
    4. sk->sk_port->accept
    5. 于此同时,不断ongoing已经完成三次握手队列中取出连接,如果队列为空,贼放弃CPU。否则进行下一步;
    6. 创建新的socket实例new_sock,设置 new_sock->sk=sk2;new_sock->ops=sock->ops
    7. 创建一个新的file实例new_file,设置new_file->private_data=new_sock,new_sock->file = new_file 进行关联;
    8. 然后从task_struct中找到新的fd,与new_file进行关联。

    在这里插入图片描述

  • 相关阅读:
    git使用
    在PCB走线中线不直,有小疙瘩,让走线变直的方法
    30张图 讲清楚Redis Cluster
    java.lang.StackOverflowError
    注解@PostConstruct分析
    接口测试结果字段太多,断言烦不胜烦,DeepDiff帮你一键搞定
    java毕业设计——基于java+Spring+SSH的CRM客户关系管理系统设计与实现(毕业论文+程序源码)——CRM客户关系管理系统
    App逆向之frida-dexdump脱壳分析某肿瘤sign
    C# 利用ffmpeg的image2pipe参数实现USB摄系头本地预览同时推流
    AUTOSAR协议栈 - 功能简介
  • 原文地址:https://blog.csdn.net/KingOfMyHeart/article/details/126441226