前面讲了很多的铺垫知识,现在终于开始要讲到TCP和UDP了,不过别急,之前会先介绍一个基本和重要的概念:协议控制块。
协议层使用协议控制块存放各UDP和TCP插口所要求的多个信息片。Internet协议维护Internet协议控制块和TCP控制块。由于UDP是无连接的,所以一个端结点需要的所有信息都可以在InternetPCB中找到,不需要UDP控制块。
Internet协议控制块含有所有UDP和TCP端结点共有的信息:外部和本地IP地址、外部和本地端号、IP首部原型、该端结点使用的IP选项以及一个指向该端结点目的地址选路表入口的指针。TCP控制块包含了TCP为各连接维护的所有结点信息:两个方向的序号、窗口大小、重传次数等等。
struct inpcb * inp_next, * inp_prev; /* pointers to other pcb's */
struct inpcb * inp_head; /* pointer back to chain of inpcb's for this protocol */
struct in_addr inp_faddr; /* foreign host table entry */
u_short inp_fport; /* foreign port */
struct in_addr inp_laddr; /* local host table entry */
u_short inp_lport; /* local port */
struct socket * inp_socket; /* back pointer to socket */
caddr_t inp_ppcb; /* pointer to per-protocol pcb */
struct route inp_route; /* placeholder for routing entry */
int inp_flags; /* generic IP/datagram flags */
struct ip inp_ip; /* header prototype; should have more */
struct mbuf * inp_options; /* IP options */
struct ip_moptions * inp_moptions; /* IP multicast options */
};
inp_next和inp_prev为UDP和TCP的所有PCB形成一个双向链表。另外,每个PCB都有一个指向协议链表表头的指针(inp_head)。
inp_faddr、inp_fport、inp_laddr和inp_lport这四个成员,包含了这个IP端结点的插口对:外部IP地址和端口号,以及本地IP地址和端口号,使用网络字节序存储。
inp_socket是一个指向该PCB的socket结构的指针。
inp_ppcb是一个指针,它指向这个PCB的可选运输层专用控制块。TCP使用,UDP不使用。
如果IP有一个到外部地址的路由,则它被保存在ipp_route入口处。
TCP的协议控制块结构如下图所示:
1)当socket或accept创建一个插口后,插口层生成一个file结构和一个socket结构。文件类型是DTYPE_SOCKET,UDP端结点的插口类型是SOCK_DGRAM,TCP端结点的插口类型是SOCK_STREAM。
2)然后调用协议层。UDP创建一个InternetPCB(一个inpcb结构),并把它链接到socket结构上:so_pcb成员指向inpcb结构,inp_socket成员指向socket结构。
3)TCP做同样的工作,也创建它自己的控制块(一个tcpcb结构),并用指针inp_ppcb和t_inpcb把它链接到inpcb上。在两个UDPinpcb中,inp_ppcb成员是一个空指针,因为UDP不负责维护它自己的控制块。
4)其他四个inpcb结构的成员,从inp_faddr到inp_lport,形成了该端结点的插口对:外部IP地址和端口号,以及本地IP地址和端口号。
5)UDP和TCP用指针inp_next和inp_prev维护一个所有InternetPCB的双向链表。它们在表头分配一个全局inpcb结构(命名为udb和tcb),在该结构中只使用三个成员:下一个和前一个指针,以及本地端口号。后一个成员中包含了该协议使用的下一个临时端口号。