- 論壇徽章:
- 0
|
在unix的socket分析文章
http://blog.chinaunix.net/u2/64681/showart.php?id=1432584
那樣我們也要分析一下服務(wù)器端是如何接收客戶端發(fā)送的數(shù)據(jù)的,同樣我們結(jié)合使用IP地址通訊的練習(xí)
http://blog.chinaunix.net/u2/64681/showart.php?id=1280050
在那節(jié)中我們看到了服務(wù)器端在建立了與客戶端連接后要通過read(client_sockfd, &ch, 1);來接收客戶端的數(shù)據(jù)。按照
http://blog.chinaunix.net/u2/64681/showart.php?id=1333991
那篇文章的過程細(xì)節(jié)我們不講了,因?yàn)橹饕穆窂蕉际桥cunix完全一樣的,只不過在__sock_recvmsg()函數(shù)中與unix執(zhí)行的函數(shù)調(diào)用不同了,所以我們這里直接從__sock_recvmsg()函數(shù)開始分析,這個(gè)函數(shù)最后是進(jìn)入了socket中的鉤子結(jié)構(gòu)struct proto_ops *ops中的掛入的函數(shù)中去了,我們回憶一下在
http://blog.chinaunix.net/u2/64681/showart.php?id=1360583
IPV4的socket創(chuàng)建那節(jié)中我們看到ops掛入的是inet_stream_ops結(jié)構(gòu)變量。而且在第三節(jié)中我們列出了他的詳細(xì)內(nèi)容
http://blog.chinaunix.net/u2/64681/showart.php?id=1362024
,所以在開始之節(jié)這前我們會(huì)很多的結(jié)合以前的文章進(jìn)行分析,所以當(dāng)朋友們讀這篇文章時(shí)感覺鏈接特別多,這樣幫助朋友們有利于結(jié)合以前的內(nèi)容,好了,我們?yōu)榱朔治龇奖阍儋N一下重要的代碼
static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags)
{
。。。。。。
return sock->ops->recvmsg(iocb, sock, msg, size, flags);
}
const struct proto_ops inet_stream_ops = {
。。。。。。
.recvmsg = sock_common_recvmsg,
。。。。。。
}
很顯然這里的要執(zhí)行的鉤子函數(shù)是sock_common_recvmsg,這個(gè)函數(shù)在/net/core/sock.c中的1852行處
int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags)
{
struct sock *sk = sock->sk;
int addr_len = 0;
int err;
err = sk->sk_prot->recvmsg(iocb, sk, msg, size, flags & MSG_DONTWAIT,
flags & ~MSG_DONTWAIT, &addr_len);
if (err >= 0)
msg->msg_namelen = addr_len;
return err;
}
同樣我們會(huì)通過socket得到sock結(jié)構(gòu),進(jìn)而執(zhí)行sock中的鉤子結(jié)構(gòu)變量所掛入的函數(shù),這個(gè)sock的鉤子結(jié)構(gòu)是在
http://blog.chinaunix.net/u2/64681/showart.php?id=1360583
那節(jié)中講述了設(shè)置成了tcp_prot結(jié)構(gòu)變量,并且在那里也列出該變量的詳細(xì)內(nèi)容,我們?cè)倭幸才c上面函數(shù)相關(guān)的部分
struct proto tcp_prot = {
。。。。。。
.recvmsg = tcp_recvmsg,
。。。。。。
}
所以上面的函數(shù)進(jìn)入了tcp_recvmsg(),這個(gè)函數(shù)在/net/ipv4/tcp.c中的1271行處
int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int nonblock, int flags, int *addr_len)
{
struct tcp_sock *tp = tcp_sk(sk);
int copied = 0;
u32 peek_seq;
u32 *seq;
unsigned long used;
int err;
int target; /* Read at least this many bytes */
long timeo;
struct task_struct *user_recv = NULL;
int copied_early = 0;
struct sk_buff *skb;
lock_sock(sk);
TCP_CHECK_TIMER(sk);
err = -ENOTCONN;
if (sk->sk_state == TCP_LISTEN)
goto out;
timeo = sock_rcvtimeo(sk, nonblock);
/* Urgent data needs to be handled specially. */
if (flags & MSG_OOB)
goto recv_urg;
seq = &tp->copied_seq;
if (flags & MSG_PEEK) {
peek_seq = tp->copied_seq;
seq = &peek_seq;
}
target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
函數(shù)非常的長(zhǎng),我們看到上面設(shè)置了一下定時(shí)器,并且檢查了一下socket的狀態(tài),如果還是監(jiān)聽狀態(tài)就說明出錯(cuò)了,因?yàn)槲覀兘邮帐且呀?jīng)在監(jiān)聽成功并且已經(jīng)與客戶端建立連接后進(jìn)行的,這些過程我們前邊的章節(jié)都說過了。
#ifdef CONFIG_NET_DMA
tp->ucopy.dma_chan = NULL;
preempt_disable();
skb = skb_peek_tail(&sk->sk_receive_queue);
{
int available = 0;
if (skb)
available = TCP_SKB_CB(skb)->seq + skb->len - (*seq);
if ((available target) &&
(len > sysctl_tcp_dma_copybreak) && !(flags & MSG_PEEK) &&
!sysctl_tcp_low_latency &&
__get_cpu_var(softnet_data).net_dma) {
preempt_enable_no_resched();
tp->ucopy.pinned_list =
dma_pin_iovec_pages(msg->msg_iov, len);
} else {
preempt_enable_no_resched();
}
}
#endif
do {
u32 offset;
/* Are we at urgent data? Stop if we have read anything or have SIGURG pending. */
if (tp->urg_data && tp->urg_seq == *seq) {
if (copied)
break;
if (signal_pending(current)) {
copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
break;
}
}
/* Next get a buffer. */
skb = skb_peek(&sk->sk_receive_queue);
do {
if (!skb)
break;
/* Now that we have two receive queues this
* shouldn't happen.
*/
if (before(*seq, TCP_SKB_CB(skb)->seq)) {
printk(KERN_INFO "recvmsg bug: copied %X "
"seq %X\n", *seq, TCP_SKB_CB(skb)->seq);
break;
}
offset = *seq - TCP_SKB_CB(skb)->seq;
if (tcp_hdr(skb)->syn)
offset--;
if (offset skb->len)
goto found_ok_skb;
if (tcp_hdr(skb)->fin)
goto found_fin_ok;
BUG_TRAP(flags & MSG_PEEK);
skb = skb->next;
} while (skb != (struct sk_buff *)&sk->sk_receive_queue);
如果內(nèi)核打開了網(wǎng)絡(luò)的DMA功能則會(huì)直接從sock結(jié)構(gòu)中的sk_receive_queue接受隊(duì)列中摘取數(shù)據(jù)了,我們將在后邊的客戶端的數(shù)據(jù)發(fā)送中看到如何鏈入到這個(gè)隊(duì)列的,這里我們看到如果支持網(wǎng)絡(luò)的DMA的話就會(huì)直接從接受隊(duì)列中摘取sk_buff數(shù)據(jù)包,如果不支持DMA的話就會(huì)執(zhí)行代碼中的do-while循環(huán)從接受隊(duì)列中計(jì)算數(shù)據(jù)包的序列號(hào)直到挑選符合順序的數(shù)據(jù)包。時(shí)間關(guān)系,明天繼續(xù)。
本文來自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u2/64681/showart_1664386.html |
|