简要介绍下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 &#