下面是入口函数
static int sock_recvfrom(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;
int alen;
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;
if(len==0)
return 0;
err=verify_area(VERIFY_WRITE,buff,len);
if(err)
return err;
len=sock->ops->recvfrom(sock, buff, len, (file->f_flags & O_NONBLOCK),
flags, (struct sockaddr *)address, &alen);
if(len<0)
return len;
if(addr!=NULL && (err=move_addr_to_user(address,alen, addr, addr_len))<0)
return err;
return len;
}
static int inet_recvfrom(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->prot->recvfrom == 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->recvfrom(sk, (unsigned char *) ubuf, size, noblock, flags,
(struct sockaddr_in*)sin, addr_len));
}
调用读取数据的函数前,我们要先调用bind绑定socket对应的地址信息,因为系统是根据地址和端口去查找一个socket的。由代码可以知道,入口没有什么逻辑,主要逻辑在udp层的实现代码中。代码的实现比较简单,就是从socket的接收队列中摘下数据。
/*
* This should be easy, if there is something there we\
* return it, otherwise we block.
*/
int udp_recvfrom(struct sock *sk, unsigned char *to, int len,
int noblock, unsigned flags, struct sockaddr_in *sin,
int *addr_len)
{
int copied = 0;
int truesize;
struct sk_buff *skb;
int er;
/*
* Check any passed addresses
*/
if (addr_len)
*addr_len=sizeof(*sin);
/*
* From here the generic datagram does a lot of the work. Come
* the finished NET3, it will do _ALL_ the work!
*/
skb=skb_recv_datagram(sk,flags,noblock,&er);
if(skb==NULL)
return er;
truesize = skb->len;
copied = min(len, truesize);
/*
* FIXME : should use udp header size info value
*/
skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);
sk->stamp=skb->stamp;
/* Copy the address. */
// 复制源地址信息给应用层
if (sin)
{
sin->sin_family = AF_INET;
sin->sin_port = skb->h.uh->source;
sin->sin_addr.s_addr = skb->daddr;
}
skb_free_datagram(skb);
release_sock(sk);
return(truesize);
}
读取的时候是直接从socket的接收队列进行的,那接收队列的数据又是怎么来的呢,当底层的收到udp的数据包的时候,会调用udp的udp_rcv函数,该函数把数据包缓存到socket的接收队列。
int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
unsigned long daddr, unsigned short len,
unsigned long saddr, int redo, struct inet_protocol *protocol)
{
struct sock *sk;
struct udphdr *uh;
unsigned short ulen;
int addr_type = IS_MYADDR;
if(!dev || dev->pa_addr!=daddr)
addr_type=ip_chk_addr(daddr);
/*
* Get the header.
*/
uh = (struct udphdr *) skb->h.uh;
ip_statistics.IpInDelivers++;
/*
* Validate the packet and the UDP length.
*/
ulen = ntohs(uh->len);
if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh))
{
printk("UDP: short packet: %d/%d\n", ulen, len);
udp_statistics.UdpInErrors++;
kfree_skb(skb, FREE_WRITE);
return(0);
}
// 检查检验和
if (uh->check && udp_check(uh, len, saddr, daddr))
{
/* <mea@utu.fi> wants to know, who sent it, to
go and stomp on the garbage sender... */
printk("UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
ntohl(saddr),ntohs(uh->source),
ntohl(daddr),ntohs(uh->dest),
ulen);
udp_statistics.UdpInErrors++;
kfree_skb(skb, FREE_WRITE);
return(0);
}
len=ulen;
#ifdef CONFIG_IP_MULTICAST
if (addr_type!=IS_MYADDR)
{
/*
* Multicasts and broadcasts go to each listener.
*/
struct sock *sknext=NULL;
sk=get_sock_mcast(udp_prot.sock_array[ntohs(uh->dest)&(SOCK_ARRAY_SIZE-1)], uh->dest,
saddr, uh->source, daddr);
if(sk)
{
do
{
struct sk_buff *skb1;
sknext=get_sock_mcast(sk->next, uh->dest, saddr, uh->source, daddr);
if(sknext)
skb1=skb_clone(skb,GFP_ATOMIC);
else
skb1=skb;
if(skb1)
udp_deliver(sk, uh, skb1, dev,saddr,daddr,len);
sk=sknext;
}
while(sknext!=NULL);
}
else
kfree_skb(skb, FREE_READ);
return 0;
}
#endif
// 获取对应socket
sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
if (sk == NULL)
{
udp_statistics.UdpNoPorts++;
// 没有对应的socket
if (addr_type == IS_MYADDR)
{
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
}
/*
* Hmm. We got an UDP broadcast to a port to which we
* don't wanna listen. Ignore it.
*/
skb->sk = NULL;
kfree_skb(skb, FREE_WRITE);
return(0);
}
return udp_deliver(sk,uh,skb,dev, saddr, daddr, len);
}
static int udp_deliver(struct sock *sk, struct udphdr *uh, struct sk_buff *skb, struct device *dev, long saddr, long daddr, int len)
{
skb->sk = sk;
skb->dev = dev;
skb->len = len;
/*
* These are supposed to be switched.
*/
skb->daddr = saddr;
skb->saddr = daddr;
/*
* Charge it to the socket, dropping if the queue is full.
*/
skb->len = len - sizeof(*uh);
// 把skb挂载到sk接收队列
if (sock_queue_rcv_skb(sk,skb)<0)
{
udp_statistics.UdpInErrors++;
ip_statistics.IpInDiscards++;
ip_statistics.IpInDelivers--;
skb->sk = NULL;
kfree_skb(skb, FREE_WRITE);
release_sock(sk);
return(0);
}
udp_statistics.UdpInDatagrams++;
release_sock(sk);
return(0);
}