本文介绍TCP的用户请求处理函数tcp_usrreq,它被协议的pr_usrreq函数调用,处理各种与tcp插口有关的系统调用,此外,
还将介绍tcp_ctloutput,应用进程调用setsockopt设定tcp插口选项时会用到它。
TCP的用户请求函数用于处理多种操作,主要由tcp_usrreq函数完成,函数的对各个请求的大概处理如下:
1.PRU_ATTACH请求。应用程序调用socket系统调用,或者监听服务器收到连接其你去,调用sonewconn函数,都会发出
PRU_ATTACH请求。如果插口结构已经指向某个PCB,则返回错误,调用tcp_attach完成处理:分配并初始化Internet PCB
和TCP控制块。
2.PRU_DETACH请求。close系统调用在PRU_DISCONNECT请求失败后,将发送PRU_DETACH请求,如果尚未建立连接,
则无需向对端发送任何信息,但如果连接已经建立,则调用tcp_disconnect初始化TCP的连接关闭过程。(发送所有缓存中
的数据,之后发送FIN)
3.PRU_BIND请求的处理只是简单地调用in_pcbbind。
4.对于PRU_LISTEN请求,如果插口还没有绑定到某个本地端口上,在调用in_pcbbind自动为其分配一个。连接状态变迁
到LISTEN,完成了listen调用的主要目的:设定插口的状态,以便接收到达的连接请求。
5.PRU_CONNECT请求。connect系统调用发起该请求,处理的大概流程如下:
a.分配临时端口。
b.连接PCB。
c.初始化IP和TCP首部。
d.计算窗口缩放因子。
e.设定插口和连接的状态。
f.初始化序号。
g.发送初始SYN。
5.PRU_CONNECT2请求。来自于socketpair系统调用,对TCP协议无效。
6.close系统调用会发送PRU_DISCONNECT请求。如果连接已经建立,应调用tcp_disconnect,发送FIN,执行正常的TCP
关闭操作。
7.与accept系统调用有关的工作全部由插口层和协议层完成。PRU_ACCEPT请求只简单地向应用进程返回对端的IP地址
和端口号。
8.PRU_SHUTDOWN请求。应用进程调用shutdown,禁止更多的输出时,soshutdown会发送PRU_SHUTDOWN请求。
调用socantsendmore置位插口标志,禁止继续发送报文段,接着调用tcp_usrclosed,设定正确的连接状态。
9.PRU_RCVD请求。应用进程从插口的接收缓存中读取数据后,soreceive会发送这个请求。此时接收缓存已扩大,也许
会有足够的空间,让TCP发送更大的窗口通告。tcp_output会决定是否需要发送窗口更新报文段。
10.PRU_SEND请求。调用sbappend,向插口的发送缓存中添加数据,并调用tcp_output发送新报文段(如果条件允许)。
11.PRU_ABORT请求。如果插口是监听插口,并且存在等待建立的连接,例如已发送初始SYN或已完成三次握手过程,但
还未被服务器accept的连接,调用soclose会导致发送PRU_ABORT请求。如果连接已同步,tcp_drop将发送RST。
12.PRU_SENSE请求。fastat系统调用会生成PRU_SENSE请求。TCP返回发送缓存的大小,保存在stat结构的成员变量中。
13.PRU_RCVOOB请求。当应用进程设置MSG_OOB标志,试图读取带外数据时,soreceive会发送这一请求。大概流程如下:
a.判断能否读取带外数据。
b.判断是否有带外数据到达。
c.返回带外数据字节。
d.更新标志。
e.确认发送缓存中有足够空间并添加新数据。
f.计算紧急指针。
g.强制TCP输出。
14.PRU_SOCKADDR请求。getsockname系统调用会发送该请求,调用in_setsockaddr函数。
15.PRU_PEERADDR请求。getpeername系统调用会发送该请求,调用in_setpeeraddr函数。调用in_setsockaddr和
in_setpeeraddr函数,从PCB中获取需要信息,存储在addr参数中。
16.执行tcp_slowtimo会发送PRU_SLOWTIMO函数。