网络协议栈11:Connect函数分解之TCP层
Connect函数之分解1.
首先,connect函数从参数获得远端的IP,把这个地址赋值给对应的sock结构体的对应变量,并设置了sock结构体中的一些其他变量后,首先分配(skb_buff+用户空间)大小内存,这两部分是通过调用kmalloc(sizeof(struct skb_buff)+size,priority)来分配的,分配后两个数据块是连续的地址;分配后的内存如下
接下来,就是初始化这个刚刚分配好的skb_buff结构体的一些指针成员,之后,把整个数据空间的首地址返回给一个skb_buff指针,以后对skb结构体的操作,就通过这个指针来完成了。接着,把skb_buff跟sock关联起来,即把skb_buff的成员sk设置为对应的sock,路由指向本地路由,就开始配置通信数据了。
第一步,把指针指向skb_buff空间的数据空间,即skb_buff的成员data所指向的地址(上图 5)。
第二步(上图 1),设置以太网头部(本地MAC地址6字节+远端MAC地址6字节+数据帧类型2字节),把本地MAC地址复制到数据空间的开始处,本地的MAC地址,是从本地路由中获得,而目的MAC地址一般为NULL,如果为NULL,则需要设置skb_buff成员arp为0,表示远端MAC地址(或者下一跳MAC地址)并不知道,需要在发送数据之前使用ARP来查找,数据帧可以选择:
#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */
#define ETH_P_ECHO 0x0200 /* Ethernet Echo packet */
#define ETH_P_PUP 0x0400 /* Xerox PUP packet */
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */
#define ETH_P_X25 0x0805 /* CCITT X.25 */
#define ETH_P_ATALK 0x809B /* Appletalk DDP */
#define ETH_P_IPX 0x8137 /* IPX over DIX */
#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */
#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */
#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */
#define ETH_P_802_2 0x0004 /* 802.2 frames */
#define ETH_P_SNAP 0x0005 /* Internal only */
第三步,指针跳过以太网首部14字节,指向IP首部(上图 2),跟着就是数据空间初始化IP首部,IP首部的数据基本上在sock结构体中都有用,现在只是从sock结构体复制过来,初始化完后,在skb_buff中还有一个专门的指针指向数据空间的IP首部的地址的成员ip_hdr,此时也初始化一下。
第四步,指针跳过IP首部,指向TCP首部(上图 3),跟着初始化数据空间的tcp首部,tcp首部一些是从sock结构体中复制的,一些是在这里第一次被赋值的,注意tcp首部不包含tcp选项部分,因此,需要把指针跳过tcp首部后,指向tcp的选项部分,进行初始化,才算把tcp初始化完成(把MSS赋值到选项部分)(上图 4)。
到此,我们就把数据空间初始化完,我们在开始时分配数据空间大小为44+18,现在使用的是14(MAC)+20(TCP)+20(IP)+4(MSS),也就用完了,从这里,我们看到,connect函数发送的数据,都是本地的一些信息,包装到MAC,TCP,IP等结构体中,目的是告知远端本地的一些情况的信息,以后通信时,就按照现在发送的规格进行了,而真正的数据部分是没有的。
最后,就是把数据发送的网络层,进行排队了。