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);
}