前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UDP协议源码解析之发送

UDP协议源码解析之发送

作者头像
theanarkh
发布2019-03-06 10:02:37
1.2K0
发布2019-03-06 10:02:37
举报
文章被收录于专栏:原创分享原创分享原创分享

UDP不是面向连接的协议,发送数据的时候指定目的地址的信息即可以发送。下面是入口函数的代码。

static int sock_sendto(int fd, void * buff, int len, unsigned flags,
       struct sockaddr *addr, int addr_len)
{
    struct socket *sock;
    struct file *file;
    char address[MAX_SOCK_ADDR];
    int err;

    if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
        return(-EBADF);
    if (!(sock = sockfd_lookup(fd, NULL)))
        return(-ENOTSOCK);

    if(len<0)
        return -EINVAL;
    err=verify_area(VERIFY_READ,buff,len);
    if(err)
        return err;

    if((err=move_addr_to_kernel(addr,addr_len,address))<0)
        return err;

    return(sock->ops->sendto(sock, buff, len, (file->f_flags & O_NONBLOCK),
        flags, (struct sockaddr *)address, addr_len));
}

static int inet_sendto(struct socket *sock, void *ubuf, int size, int noblock, 
        unsigned flags, struct sockaddr *sin, int addr_len)
{
    struct sock *sk = (struct sock *) sock->data;
    if (sk->shutdown & SEND_SHUTDOWN) 
    {
        send_sig(SIGPIPE, current, 1);
        return(-EPIPE);
    }
    if (sk->prot->sendto == NULL) 
        return(-EOPNOTSUPP);
    if(sk->err)
        return inet_error(sk);
    /* We may need to bind the socket. */
    if(inet_autobind(sk)!=0)
        return -EAGAIN;
    return(sk->prot->sendto(sk, (unsigned char *) ubuf, size, noblock, flags, 
               (struct sockaddr_in *)sin, addr_len));
}

由代码知道,入口函数做的事情不多,有一个需要注意的是,如果调用sendto函数之前没有调bind绑定过端口则这里会随机绑定一个端口。接着是调用udp层的接口udp_sendto。下面是udp_sendto的代码。

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) 
    {
        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 
    {   
        // 没传目的信息又没有建立起连接,即没有调connect函数,则无法知道数据包应该发送给谁
        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)
          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);

    /* The datagram has been sent off.  Release the socket. */
    release_sock(sk);
    return(tmp);
}
static int udp_send(struct sock *sk, 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;
    // 在写缓冲区申请一个skb
    skb = sock_alloc_send_skb(sk, size, 0, &tmp);


    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;
    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
    // 构建ip和mac头
    tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
            &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,ttl);

    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. 
     */

    uh = (struct udphdr *) buff;
    // 设置udp头各字段
    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);

    /* 
     *  Send the datagram to the interface. 
     */

    udp_statistics.UdpOutDatagrams++;
    // 下发到ip层    
    sk->prot->queue_xmit(sk, dev, skb, 1);
    return(len);
}

UDP的源地址和端口如果用户没有设置的话都是由系统自己决定的,目的地址和端口则需要用户提供,提供的方式可以通过connect函数或者sendto的时候传入。

// udp不是面向连接的,connect函数主要是根据目的ip,从路由表找到目的地址的信息然后保存在sk中,并修改态为以已连接
int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
{
    struct rtable *rt;
    unsigned long sa;
    if (addr_len < sizeof(*usin)) 
        return(-EINVAL);

    if (usin->sin_family && usin->sin_family != AF_INET) 
        return(-EAFNOSUPPORT);
    if (usin->sin_addr.s_addr==INADDR_ANY)
        usin->sin_addr.s_addr=ip_my_addr();

    if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
        return -EACCES;         /* Must turn broadcast on first */

      rt=ip_rt_route(usin->sin_addr.s_addr, NULL, &sa);
      if(rt==NULL)
          return -ENETUNREACH;
      sk->saddr = sa;     /* Update source address */
    sk->daddr = usin->sin_addr.s_addr;
    sk->dummy_th.dest = usin->sin_port;
    sk->state = TCP_ESTABLISHED;
    return(0);
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档