標籤:des blog http ar os 使用 for sp strong
本文分析基於Linux Kernel 1.2.13
原創作品,轉載請標明http://blog.csdn.net/yming0221/article/details/7549340
更多請查看專欄,地址http://blog.csdn.net/column/details/linux-kernel-net.html
閆明
註:標題中的”(上)“,”(下)“表示分析過程基於資料包的傳遞方向:”(上)“表示分析是從底層向上分析、”(下)“表示分析是從上向下分析。
上篇分析了應用程式層經過BSD socket層到INET socket層的函數調用關係和資料的處理流程,INET層會調用具體的傳輸層協議,還是以UDP協議為例
udp_write()函數
[cpp] view plaincopy
- static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,
- unsigned flags)
- {
- return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));
- }
在分析udp_sendto()函數之前,先瞭解一下sockaddr_in結構,這是標準的網路介面地址結構的定義
[cpp] view plaincopy
- struct sockaddr_in {
- short int sin_family; /* Address family 地址族 */
- unsigned short int sin_port; /* Port number 連接埠號碼 */
- struct in_addr sin_addr; /* Internet address 網路地址 */
-
- /* Pad to size of `struct sockaddr‘. */
- unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
- sizeof(unsigned short int) - sizeof(struct in_addr)];
- };
- #define sin_zero __pad /* for BSD UNIX comp. -FvK */
udp_sentdto()函數
[cpp] view plaincopy
- static int udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
- unsigned flags, struct sockaddr_in *usin, int addr_len)
- {
- struct sockaddr_in sin;
- int tmp;
-
- /*
- * Check the flags. We support no flags for UDP sending
- */
- if (flags&~MSG_DONTROUTE)
- return(-EINVAL);
- /*
- * Get and verify the address.
- */
-
- if (usin) //如果usin不是空
- {
- if (addr_len < sizeof(sin))
- return(-EINVAL);
- memcpy(&sin,usin,sizeof(sin));
- if (sin.sin_family && sin.sin_family != AF_INET)
- return(-EINVAL);
- if (sin.sin_port == 0)
- return(-EINVAL);
- }
- else //usin為空白
- {
- if (sk->state != TCP_ESTABLISHED)
- return(-EINVAL);
- sin.sin_family = AF_INET;//協議族
- sin.sin_port = sk->dummy_th.dest;//目的連接埠
- sin.sin_addr.s_addr = sk->daddr;//目的地址
- }
-
- /*
- * BSD socket semantics. You must set SO_BROADCAST to permit
- * broadcasting of data.
- */
-
- if(sin.sin_addr.s_addr==INADDR_ANY)//目的地址是全0地址,對應當前主機
- sin.sin_addr.s_addr=ip_my_addr();//將目的地址設為當前主機的網路地址
-
- if(!sk->broadcast && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
- return -EACCES; /* Must turn broadcast on first */
-
- sk->inuse = 1;
-
- /* Send the packet. */
- tmp = udp_send(sk, &sin, from, len, flags);//調用udp_send()真正的發送資料
-
- /* The datagram has been sent off. Release the socket. */
- release_sock(sk);
- return(tmp);
- }
udp_send()函數
[cpp] view plaincopy
- static int udp_send(struct sock *sk, //要發送的資料包使用的協議對用的sock結構
- struct sockaddr_in *sin,//目的端的標準的網路介面地址
- unsigned char *from,//要發送的資料所在地址
- int len,//發送資料的長度
- int rt)
- {
- struct sk_buff *skb;
- struct device *dev;
- struct udphdr *uh;
- unsigned char *buff;
- unsigned long saddr;
- int size, tmp;
- int ttl;
-
- /*
- * Allocate an sk_buff copy of the packet.
- */
-
- size = sk->prot->max_header + len;//計算大小為UDP最長表頭+ 資料長度
- skb = sock_alloc_send_skb(sk, size, 0, &tmp);//根據要發送的資料分配sk_buff結構空間並對當前通訊端狀態檢查
-
-
- if (skb == NULL)
- return tmp;
-
- skb->sk = NULL; /* to avoid changing sk->saddr */
- skb->free = 1;
- skb->localroute = sk->localroute|(rt&MSG_DONTROUTE);
-
- /*
- * Now build the IP and MAC header.
- */
-
- buff = skb->data;//將skb中的資料指標賦值給buff指標
- saddr = sk->saddr;//本地地址
- dev = NULL;
- ttl = sk->ip_ttl;//存留時間
- #ifdef CONFIG_IP_MULTICAST
- if (MULTICAST(sin->sin_addr.s_addr))
- ttl = sk->ip_mc_ttl;
- #endif
- tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
- &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,ttl);//調用ip_build_header()建立IP前序和調用ip_send()建立MAC首部
-
- skb->sk=sk; /* So memory is freed correctly */
-
- /*
- * Unable to put a header on the packet.
- */
-
- if (tmp < 0 )
- {
- sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
- return(tmp);
- }
-
- buff += tmp;
- saddr = skb->saddr; /*dev->pa_addr;*/
- skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */
- skb->dev = dev;
-
- /*
- * Fill in the UDP header. 填寫UDP的前序
- */
-
- uh = (struct udphdr *) buff;
- uh->len = htons(len + sizeof(struct udphdr));//資料包長度
- uh->source = sk->dummy_th.source;//本地連接埠
- uh->dest = sin->sin_port;//遠端連接埠
- buff = (unsigned char *) (uh + 1);
-
- /*
- * Copy the user data.
- */
-
- memcpy_fromfs(buff, from, len);//複製使用者資料
-
- /*
- * Set up the UDP checksum.
- */
-
- udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);//計算UDP前序的校正和
-
- /*
- * Send the datagram to the interface.
- */
-
- udp_statistics.UdpOutDatagrams++;
-
- sk->prot->queue_xmit(sk, dev, skb, 1);//調用IP層函數發送資料
- return(len);
- }
這樣要發送的資料填充的sk_buff結構中之後再對UDP資料包添加IP資料前序和MAC幀的首部。最後調用IP層的發送函數發送資料包。
Linux核心--網路棧實現分析(九)--傳輸層之UDP協議(下)