Socket
- Socket 编程
- 针对 TCP 应该如何 Socket 编程?
- listen 时候参数 backlog 的意义?
- accept 发送在三次握手的哪一步?
- 客户端调用 close 了,连接断开的流程是什么?
Socket 编程
- 服务端和客户端初始化
socket,得到文件描述符 - 服务端调用
bind,将绑定 IP 和端口 - 服务端调用
listen,进行监听 - 服务端调用
accept,等待客户端连接 - 客户端调用
conncet,向服务端的地址和端口发起连接请求 - 服务端
accept返回用于传输的socket的文件描述符 - 客户端
wirte写入数据;服务器调用read读取数据 - 客户端断开连接时,会调用
close,那么服务端read读取数据的时候,就会读取到 EOF ,待处理完数据后,服务端调用close,表示连接关闭。
服务器调用 accept 时,连接成功了会返回一个已完成连接的 socket ,后续用来传输数据,所以监听的 socket 和真正用来传输数据的 socket ,一个叫监听 socket ,一个叫作已完成连接 socket 。成功建立连接后,双方开始通过 read 和 write 读写数据,就像往一个文件流里写东西一样。
客户端 connect 成功返回是在第二次握手,服务端 accept 成功返回实在第三次握手成功之后。
backlog
Linux 内核会维护两个队列:
- 未完成连接队列(SYN 队列) :接收到一个 SYN 建立连接请求,处于
SYN_RCVD状态 - 已完成连接队列(Accpet 队列):已完成 TCP 三次握手的过程,处于
ESTABLISHED状态
int listen(int socketfd, int backlog)
socketfd 为 socket 文件描述符, 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_LINGER 即 l_onoff 为 1 时1:
l_linger 为 0 则导致所有数据丢失且连接立即中止。
l_linger 非 0 则:
- 待发送的数据全部得到了对端确认,返回值 0
- 发生信号中断或异常(比如意外收到对端发送过来的数据)或超时,返回值 0
一种使用方法1:
- 设置
SO_LINGER选项l_onoff非 0 而l_linger为 0 - 调用函数
shutdown(sock_fd, SHUT_WR) - 设置超时计时器
- 调用函数
read(sock_fd)阻塞等待,直到读到EOF或被超时器中断 - 执行函数
close(sock_fd)或者exit(0)进程退出
Reference
- 图解网络-小林 coding-v3.0.pdf
Footnotes:
Socket 选项系列之 SO_LINGER,链接:http://www.lenky.info/archives/2013/02/2220