本文主要是介绍【Linux 内核网络协议栈源码剖析】sendto 函数剖析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前面介绍的函数基本上都是TCP协议的,如listen,connect,accept 等函数,这都是为可靠传输协议TCP定制的。对于另一个不可靠udp协议(通信系统其可靠性交由上层应用层负责),则主要由两个函数完成,sendto 和 recvfrom 函数。这里先介绍 sendto 函数。
说明:sendto 和 recvfrom 函数不限于udp协议,这里只是udp协议当中是采用这两个函数实现的,所以就放在udp协议中介绍。
对于 udp 协议的介绍和编程实现请参考下文:UDP 客户/服务器简单 Socket 程序
简要介绍下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);
-
-
-
-
-
该函数的作用是:向指定端口发送给定地址中的指定大小数据(如客户端sockfd,向 to 指定的远端套接字发送buff 缓冲区内nbytes 个字节数据)
二、BSD Socket层——sock_sendto 函数
-
-
-
-
-
-
-
-
-
-
- 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));
- }
三、INET Socket层——inet_sendto 函数
-
- tatic 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);
-
-
- if(inet_autobind(sk)!=0)
- return -EAGAIN;
-
- 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;
-
-
-
-
-
- if (flags&~MSG_DONTROUTE)
- return(-EINVAL);
-
-
-
-
-
- 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
- {
-
- 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;
- }
-
-
-
-
-
-
- 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;
-
- sk->inuse = 1;
-
-
-
- tmp = udp_send(sk, &sin, from, len, flags);
-
-
-
-
- release_sock(sk);
- return(tmp);
- }
udp_send 函数
-
- 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;
-
-
-
-
-
- size = sk->prot->max_header + len;
-
- skb = sock_alloc_send_skb(sk, size, 0, &tmp);
-
-
- if (skb == NULL)
- return tmp;
-
- skb->sk = NULL;
- skb->free = 1;
- skb->localroute = sk->localroute|(rt&MSG_DONTROUTE);
-
-
-
-
-
- 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
-
- 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;
-
-
-
-
-
- if (tmp < 0 )
- {
- sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
- return(tmp);
- }
-
- buff += tmp;
- saddr = skb->saddr;
-
- skb->len = tmp + sizeof(struct udphdr) + len;
- skb->dev = dev;
-
-
-
-
-
- uh = (struct udphdr *) buff;
- uh->len = htons(len + sizeof(struct udphdr));
- uh->source = sk->dummy_th.source;
- uh->dest = sin->sin_port;
- buff = (unsigned char *) (uh + 1);
-
-
-
-
-
-
-
-
-
- memcpy_fromfs(buff, from, len);
-
-
-
-
-
- udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);
-
-
-
-
-
- udp_statistics.UdpOutDatagrams++;
-
-
- sk->prot->queue_xmit(sk, dev, skb, 1);
- return(len);
- }
关于ip_queue_xmit 函数的介绍以及更下层的数据传送,参见博文:
【Linux 内核网络协议栈源码剖析】数据包发送
可以看出,udp是一种无连接传输层协议,不像tcp那样需要服务器监听,也不必等待客户端与服务器建立连接后才能通信,效率优于tcp协议,但udp则不能保证数据传输的可靠性。
udp 的数据传输,实现并不像tcp那样要建立一条数据传输通道,而是直接创建套接字后,直接传送数据到给定的远端(提供远端地址),数据传送过程无超时重传和序列号校验工作,适用于数据传输的连续性比数据的完整性更重要的场合,允许数据在传输过程中有部分丢失,如IP电话、流媒体通信等。
这篇关于【Linux 内核网络协议栈源码剖析】sendto 函数剖析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!