Socket

Socket 编程

20220718-socket_1.png
Figure 1: socket flow
  1. 服务端和客户端初始化 socket ,得到文件描述符
  2. 服务端调用 bind ,将绑定 IP 和端口
  3. 服务端调用 listen ,进行监听
  4. 服务端调用 accept ,等待客户端连接
  5. 客户端调用 conncet ,向服务端的地址和端口发起连接请求
  6. 服务端 accept 返回用于传输的 socket 的文件描述符
  7. 客户端 wirte 写入数据;服务器调用 read 读取数据
  8. 客户端断开连接时,会调用 close ,那么服务端 read 读取数据的时候,就会读取到 EOF ,待处理完数据后,服务端调用 close ,表示连接关闭。

服务器调用 accept 时,连接成功了会返回一个已完成连接的 socket ,后续用来传输数据,所以监听的 socket 和真正用来传输数据的 socket ,一个叫监听 socket ,一个叫作已完成连接 socket 。成功建立连接后,双方开始通过 readwrite 读写数据,就像往一个文件流里写东西一样。

客户端 connect 成功返回是在第二次握手,服务端 accept 成功返回实在第三次握手成功之后。

backlog

Linux 内核会维护两个队列:

  • 未完成连接队列(SYN 队列) :接收到一个 SYN 建立连接请求,处于 SYN_RCVD 状态
  • 已完成连接队列(Accpet 队列):已完成 TCP 三次握手的过程,处于 ESTABLISHED 状态
int listen(int socketfd, int backlog)

socketfdsocket 文件描述符, backlog 为 SYN 队列的大小, Linux 2.2 后, backlog 为 Accpet 队列的大小。 但上限值是内核参数 somaxconn 的大小,即 Accpet 队列长度 = min(backlog, somaxconn)

TCP_NODELAY

配置是否关闭 nagle 算法。

setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int));

TCP_CORK

功能类似于在发送数据管道出口处插入一个“塞子”,使得发送数据全部被阻塞,直到取消 TCP_CORK 选项(即拔去塞子)或被阻塞数据长度已超过 MSS 才将其发送出去。

用户层可通过 setsockopt() 系统调用设置 TCP 套接字的 TCP_CORK 选项。开启时,内核将阻塞不完整的报文,当关闭此选项时,发送阻塞的报文。

TCP_CORK 强于 TCP_NODELAY ,在大部分的情况下,即使通过设置 TCP_NODELAY 而关闭 Nagle 算法,但是开启了 TCP_CORK ,数据也会被缓存而不是立即发送。

setsockopt(sock_fd, IPPROTO_TCP, TCP_CORK, (char *)&value, sizeof(int));

SO_LINGER

struct linger {
    int l_onoff;  /* Linger active */
    int l_linger; /* How long to linger for */
};

在 Linux 系统中,设置了 SO_LINGERl_onoff 为 1 时1l_linger 为 0 则导致所有数据丢失且连接立即中止。 l_linger 非 0 则:

  1. 待发送的数据全部得到了对端确认,返回值 0
  2. 发生信号中断或异常(比如意外收到对端发送过来的数据)或超时,返回值 0

一种使用方法1

  1. 设置 SO_LINGER 选项 l_onoff 非 0 而 l_linger 为 0
  2. 调用函数 shutdown(sock_fd, SHUT_WR)
  3. 设置超时计时器
  4. 调用函数 read(sock_fd) 阻塞等待,直到读到 EOF 或被超时器中断
  5. 执行函数 close(sock_fd) 或者 exit(0) 进程退出

Reference

  • 图解网络-小林 coding-v3.0.pdf

Footnotes:

1

Socket 选项系列之 SO_LINGER,链接:http://www.lenky.info/archives/2013/02/2220

湘ICP备19014083号-1