基于linux内核的网络协议源码分析

本文深入分析了Linux内核中UDP协议的源码实现,从应用层的sendto和recvfrom函数开始,逐步讲解到传输层的udp_sendto和udp_recvfrom函数,揭示了UDP数据报的发送和接收流程。UDP协议以其无连接特性,提供了高效但不可靠的数据传输,常用于实时通信和流媒体等场景。
摘要由CSDN通过智能技术生成

简要介绍下UDP数据报格式,相比TCP数据报格式,实在是简洁不少。

                                  

上面的各个字段含义一目了然(上面是16是表示该字段占16bit,udp头部占8字节),其中长度指的是此 UDP 数据报的长度(包括 UDP 数据报头部和 “数据” 部分)。

 

一、应用层——sendto 函数

 

#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,
               const struct sockaddr *to, socklen_t *addrlen);
//若成功则返回写的字节数,出错则返回-1

 


/*参数解析
前面三个参数分别表示:套接字描述符,指向写出缓冲区的指针和写字节数。
to:指向一个含有数据报接收者的协议地址(如IP地址和端口号)的套接字地址结构,其大小由addrlen参数指定
*/
该函数的作用是:向指定端口发送给定地址中的指定大小数据(如客户端sockfd,向 to 指定的远端套接字发送buff 缓冲区内nbytes 个字节数据)

 


二、BSD Socket层——sock_sendto 函数

 

 

/*
 *    Send a datagram to a given address. We move the address into kernel
 *    space and check the user space data area is readable before invoking
 *    the protocol.
 */
//发送数据给指定的远端地址,主要用于UDP协议
//前面三个参数分别表示套接口描述字、指向缓冲区的指针和读写字节数
//addr指向一个含有数据包接收者的协议地址(含ip地址和端口号)的套接口地址结构
//其大小由addr_len参数指定
//该函数的作用就是向指定地址的远端发送数据包:将buff缓冲区中len大小的数据发送给addr指定的远端套接字
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);
    //找到给定文件描述符对应的socket结构
    if (!(sock = sockfd_lookup(fd, NULL)))
        return(-ENOTSOCK);
 
    if(len<0)
        return -EINVAL;
    //检查权限,buff中len个字节区域是否可读
    err=verify_area(VERIFY_READ,buff,len);
    if(err)
          return err;
      //从addr拷贝addr_len大小的数据到address
    if((err=move_addr_to_kernel(addr,addr_len,address))<0)
          return err;
    //调用下层函数sendto,inet域为inet_sendto函数
    return(sock->ops->sendto(sock, buff, len, (file->f_flags & O_NONBLOCK),
        flags, (struct sockaddr *)address, addr_len));
}

 


三、INET Socket层——inet_sendto 函数

 


 //INET socket层
static int inet_sendto(struct socket *sock, void *ubuf, int size, int noblock, 
        unsigned flags, struct sockaddr *sin, int addr_len)
{
    //得到socket对应的sock结构
    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;
    //调用下层传输层函数udp_sendto函数
    return(sk->prot->sendto(sk, (unsigned char *) ubuf, size, noblock, flags, 
               (struct sockaddr_in *)sin, addr_len));
}

 


四、传输层
udp_sento 函数

 

 

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
     */
     //udp除了MSG_DONTROUTE外,不支持任何其他标志位
    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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值