协议层使用协议控制块(PCB)存放各UDP和TCP插口所要求的多个信息片。Internet协议维护Internet协议控制块
(internet protocol control block)和TCP控制块(TCP control block)。因为UDP是无连接的,所以一个端结点需要的
所有信息都在Internet PCB中找到,不存在UDP控制块。
Internet PCB含有所有UDP和TCP端结点共有的信息:外部和本地IP地址、外部和本地端号、IP首部原型、该端结点使用的
IP选项以及一个指向该端结点目的地址选路表入口的指针。TCP控制块包含了TCP为各连接维护的所有结点信息:两个方向
的序号、窗口大小、重传次数等等。
下图总结了协议控制块以及它们与file和socket结构之间的关系。
图中有几个要点要考虑:
1.当socket或accept创建一个插口后,插口层生成一个file结构和一个socket结构。文件类型是DTYPE_SOCKET,UDP端结点
的插口类型是SOCK_DGRAM,TCP端结点插口类型是SOCK_STREAM。
2.然后调用协议层。UDP创建一个Internet PCB(一个inpcb结构),并把它链接到socket结构上:so_pcb成员指向inpcb结构,
inp_socket成员指向socket结构。
3.TCP做同样的工作,也创建它自己的控制块(一个inpcb结构),并用指针inp_ppcb和t_inpcb把它链接到inpcb上。在两个
UDP inpcb中,inp_ppcb成员是一个空指针,因为UDP不负责维护它自己的控制块。
4.我们显示的其他四个inpcb结构的成员,从inp_faddr到inp_lport,形成了该端结点的插口对:外部IP地址和端口号,以及
本地IP地址和端口号。
5.UDP和TCP用指针inp_next和inp_prev维护一个所有Internet PCB的双向链表。他们在表头分配一个全局inpcb(命名为udb
和tcb),在该结构中使用三个成员:下一个和前一个指针,以及本地端口号。后一个成员中包含了该协议使用的下一个临时
端口号。
Internet PCB是一个传输层数据结构。TCP、UDP和原始IP使用它,但是IP、ICMP或IGMP不用它。
SO_REUSEADDR:
SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。
这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。
SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我
们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
SO_REUSEADDR允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服
务器。
SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一个套接
口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播)。
SO_REUSEPORT:
允许进程重用IP地址和端口号,但是包含第一个在内的各个IP地址和端口号,必须指定这个插口选项。
2.连接一个UDP插口
我们通常把connect系统调用和TCP客户联系起来,但是UDP客户或UDP服务器也可以可能调用connect,为插口指定外部IP地址
和外部端口号,这就限制插口必须只与某个特定对方交换UDP数据报。
当连接UDP插口时,会有一个副作用:本地IP地址,如果在调用bind是没有指定,会自动被connect设置,它被设成由IP选路指定
对方所选择的本地接口地址。
下图显示了UDP插口的三种不同状态,以及函数为终止各状态调用的伪代码。
前三个状态叫做已连接的UDP插口,后两个叫做未连接的UDP插口,两个没有连接上的UDP插口的区别在于,第一个具有
一个完全指定的本地地址,而第二个具有一个通配本地IP地址。
3.分用TCP接收和IP数据报
下图显示了主机上三个Telnet服务器的状态。前两个插口处于LISTEN状态,等待进入的连接请求。第三个连接到IP地址是
140.252.1.11的主机上的端口1500。第一个监听插口处理在接口140.252.1.29上到达的连接请求,第二个监听将处理所有
其他接口。
当TCP收到一个目的端口是23的报文段时,它调用in_pcblookup,搜索它的整个Internet PCB表,找到一个匹配,它由优先
权,因为它的通配数最少。为了确定通配匹配数,我们只考虑本地和外部IP地址,不考虑外部端口号。本地端口号必须匹配,
否则我们甚至不考虑PCB。通配数可以是0,1,2。
4.分用UDP接收的IP数据报
UDP数据报的交付比我们刚才研究的TCP例子要复杂得多,因为可以把UDP数据报发送到一个广播或多播地址。Net/3的规则
是:
1)把目的地是广播IP地址或多播IP地址的到达UDP数据报交付给所有匹配的插口。这里没有最好的匹配的概念。
2)把目的地是单播IP地址的到达UDP数据报只交付给一个匹配的插口,就是具有最小匹配数的插口。如果有多个插口具有相同
最小通配匹配数,那么具体由哪个插口来接收到达数据报依赖与不同的实现。
in_pcblookup函数有几个作用:
1.当TCP或UDP收到一个IP数据报时,in_pcblookup扫描协议的Internet PCB表,寻找一个匹配的PCB,来接收该数据报。
这是运输层对收到的数据报的分用。
2.当进程执行bind系统调用,为某个插口分配一个本地IP地址和本地端口号,协议调用in_pcbbind,验证请求的本地地址
对没有被使用。
3.当进程执行bind系统调用,请求给它的插口分配一个临时端口时,内核选了一个临时端口,并调用in_pcbbind检查该端
口是否正在被使用,如果正在被使用,就试下一个端口,以此类推,直到找到一个没有被使用的端口号。
4.当进程显式或隐式地执行connect系统调用时,in_pcbbind验证请求的插口对是唯一的。
在第2,3,4种情况下,in_pcbbind调用in_pcblookup。
in_pcbbind把一个本地地址和端口号绑定到一个插口上。从五个函数中调用它:
1.bind为某个TCP插口调用(通常绑定到服务器的一个知名端口上)
2.bind为某个UDP插口调用(绑定到服务器的一个知名端口上,或者绑定到客户插口的一个临时端口上)
3.connect为某个TCP插口调用,如果该插口还没有绑定到一个非零端口上(对TCP客户来说,这是一种典型情况)
4.listen为某个TCP插口调用,如果该插口还没有绑定到一个非零端口(这很少见,因为是TCP服务器调用listen,TCP服务器
通常绑定到一个知名端口上,而不是临时端口)
5.从in_pcbconnect调用,如果本地IP地址和本地端口号被置位。
其中1,2为显示绑定,3,4,5为隐式绑定。
函数in_pcbconnect为插口指定IP地址和外部端口号。有四个函数调用它:
1.connect为某个TCP插口(某个TCP客户的请求)调用。
2.connect为某个UDP插口(对UDP客户是可选的,UDP服务器很少见)调用。
3.当在一个没有连接上的UDP插口上输出数据报时从sendto调用。
4.当一个连接请求(一个SYN报文段)到达一个处于LISTEN状态(对TCP服务器是标准的)的TCP插口时,tcp_input调用。
in_pcbdisconnect把UDP插口断连。把外部IP地址设成全0,外部端口设成0,就把外部相关内容删除了。
这是在已经在一个未连接的UDP插口上发送一个数据报后,在一个连接上的UDP插口上调用connect时做的。在第一种情况
下,调用sendto的次序是:UDP调用in_pcbconnect把插口临时连接到目的地,udp_output发送数据报,然后in_pcbdisconnect
删除临时连接。
当关闭插口时,不调用in_pcbdisconnect,因为in_pcbdetach处理释放PCB。只有当一个不同的地址或端口号要求重用该PCB
时,才断连。
当收到一个ICMP差错时,调用in_pcbnotify函数,把差错通知给合适的进程。把差错通知给合适的进程。通过对所有的PCB
搜索一个协议(TCP或UDP),并把本地和外部IP地址及端口号与ICMP差错返回的值进行比较,找到合适的进程。例如,当
因为一些路由器丢掉了某个TCP报文段而收到ICMP源抑制差错时,TCP必须找到产生该差错的连接的PCB,放慢在该连接
上的传输速度。
下图显示了处理ICMP差错时调用的函数。
当收到一个ICMP报文时,调用icmp_input。icmp的五种报文按照差错来划分:
目的主机不可达
参数问题
重定向
源抑制
超时
每个协议都定义了它的控制输入函数,即protosw机构中pr_ctlinput。对TCP和UDP,他们分别是tcp_ctlinput和udp_cltinput。
因为收到的ICMP差错中包含了引起差错的数据报的IP首部,所有引起该差错的协议是已知的。这五个ICMP差错中的四个将引起
对协议的控制输入函数的调用。
原文:http://blog.csdn.net/todd911/article/details/40630321