免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1022 | 回复: 0
打印 上一主题 下一主题

内核中的TCP的追踪分析-19-TCP(IPV4)的服务器端数据的接收 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-11-25 10:26 |只看该作者 |倒序浏览

在unix的socket分析文章
http://blog.chinaunix.net/u2/64681/showart.php?id=1432584
那样我们也要分析一下服务器端是如何接收客户端发送的数据的,同样我们结合使用IP地址通讯的练习
http://blog.chinaunix.net/u2/64681/showart.php?id=1280050
在那节中我们看到了服务器端在建立了与客户端连接后要通过read(client_sockfd, &ch, 1);来接收客户端的数据。按照
http://blog.chinaunix.net/u2/64681/showart.php?id=1333991
那篇文章的过程细节我们不讲了,因为主要的路径都是与unix完全一样的,只不过在__sock_recvmsg()函数中与unix执行的函数调用不同了,所以我们这里直接从__sock_recvmsg()函数开始分析,这个函数最后是进入了socket中的钩子结构struct proto_ops *ops中的挂入的函数中去了,我们回忆一下在
http://blog.chinaunix.net/u2/64681/showart.php?id=1360583
IPV4的socket创建那节中我们看到ops挂入的是inet_stream_ops结构变量。而且在第三节中我们列出了他的详细内容
http://blog.chinaunix.net/u2/64681/showart.php?id=1362024
,所以在开始之节这前我们会很多的结合以前的文章进行分析,所以当朋友们读这篇文章时感觉链接特别多,这样帮助朋友们有利于结合以前的内容,好了,我们为了分析方便再贴一下重要的代码
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,
。。。。。。
}
很显然这里的要执行的钩子函数是sock_common_recvmsg,这个函数在/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;
}
同样我们会通过socket得到sock结构,进而执行sock中的钩子结构变量所挂入的函数,这个sock的钩子结构是在
http://blog.chinaunix.net/u2/64681/showart.php?id=1360583
那节中讲述了设置成了tcp_prot结构变量,并且在那里也列出该变量的详细内容,我们再列也与上面函数相关的部分
struct proto tcp_prot = {
。。。。。。
.recvmsg        = tcp_recvmsg,
。。。。。。
}
所以上面的函数进入了tcp_recvmsg(),这个函数在/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);
函数非常的长,我们看到上面设置了一下定时器,并且检查了一下socket的状态,如果还是监听状态就说明出错了,因为我们接收是已经在监听成功并且已经与客户端建立连接后进行的,这些过程我们前边的章节都说过了。
#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);
如果内核打开了网络的DMA功能则会直接从sock结构中的sk_receive_queue接受队列中摘取数据了,我们将在后边的客户端的数据发送中看到如何链入到这个队列的,这里我们看到如果支持网络的DMA的话就会直接从接受队列中摘取sk_buff数据包,如果不支持DMA的话就会执行代码中的do-while循环从接受队列中计算数据包的序列号直到挑选符合顺序的数据包。时间关系,明天继续。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/64681/showart_1664386.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP