微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

socket

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);
  • 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 <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
  • 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 <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);
  • 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 <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
  • 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 <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <sys/socket.h>

       int accept4(int sockfd, struct sockaddr *addr,
                   socklen_t *addrlen, int flags);

内核处理的基本流程:

  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进行关联。

在这里插入图片描述

原文地址:https://www.jb51.cc/wenti/3280789.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐