Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1309353
  • 博文数量: 107
  • 博客积分: 10155
  • 博客等级: 上将
  • 技术积分: 2166
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-25 16:57
文章分类

全部博文(107)

文章存档

2010年(1)

2009年(1)

2008年(105)

分类: LINUX

2008-11-25 10:26:35

unixsocket分析文章 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 IPV4socket创建那节中我们看到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循环从接受队列中计算数据包的序列号直到挑选符合顺序的数据包。时间关系,明天继续。

阅读(5008) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~