在网络编程中,UDP运用非常广泛。很多网络协议是基于UDP来实现的,如SNMP等。大家常常用到的局域网文件传输软件飞鸽传书也是基于UDP实现的。
本篇文章跟大家分享linux下UDP的使用和实现,主要介绍下sendto()和recvfrom()两个函数的使用,以及INADDR_ANY的说明,并在最后展示了一个经过自己测试可用的UDP Server和UDP Client的代码示例。
头文件
函数原型
1 | int
sendto (int s, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen); |
3 | int
recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen); |
函数说明
sendto(),是把UDP数据报发给指定地址;recvfrom()是从指定地址接收UDP数据报。
参数说明
\s:socket描述符。
\buf: UDP数据报缓存地址。
\len:UDP数据报长度。
\flags: 该参数一般为0。
\to:sendto()函数参数,struct sockaddr_in类型,指明UDP数据发往哪里报。
\tolen:对方地址长度,一般为:sizeof(struct sockaddr_in)。
\fromlen:recvfrom()函数参数,struct sockaddr_in类型,指明从哪里接收UDP数据报。
函数返回值
对于sendto()函数,成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno 中。
对于recvfrom()函数,成功则返回接收到的字符数,失败则返回-1,错误原因存于errno中。
struct sockaddr_in结构体
该结构体的定义如下:
01 | /*
Structure describing an Internet (IP) socket address. */ |
02 | #define
__SOCK_SIZE__ 16/* sizeof(struct sockaddr)*/ |
04 | sa_family_t sin_family; /* Address family */ |
05 | __be16 sin_port; /* Port number*/ |
06 | struct in_addrsin_addr; /* Internet address */ |
08 | /*
Pad to size of `struct sockaddr'. */ |
09 | unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - |
10 | sizeof(unsigned
short int) - sizeof(struct in_addr)]; |
其中,sin_family指明地址族,一般使用AF_INET:
1 | #define AF_INET 2 /* Internet IP Protocol */ |
sin_port:指明UDP端口;sin_addr指明IP地址:
INADDR_ANY说明
INADDR_ANY,是个特殊IP地址 ,表示任务的IP地址,作为服务器端的时候经常要用到。对于它的解释,摘用下面一段英文(来自于:
http://www.cs.cmu.edu/~srini/15-441/F01.full/www/assignments/P2/htmlsim_split/node18.html):
When you wrote your simple FTP server in project 1, you probably bound your listening socket to the special IP address INADDR_ANY. This allowed your program to work
without knowing the IP address of the machine it was running on, or, in the case of a machine with multiple network interfaces, it allowed your server to receive packets destined to any of the interfaces. In reality, the semantics of INADDR_ANY are
more complex and involved.
In the simulator, INADDR_ANY has the following semantics: When receiving, a socket bound to this address receives packets from all interfaces.
For example, suppose that a host has interfaces 0, 1 and 2. If a UDP socket on this host is bound using INADDR_ANY and udp port 8000, then the socket will receive
all packets for port 8000 that arrive on interfaces 0, 1, or 2. If a second socket attempts to Bind to port 8000 on interface 1, the Bind will fail since the first socket already “owns” that port/interface.
When sending, a socket bound with INADDR_ANY binds to the default IP address, which is that of the lowest-numbered interface.
大概的意思就是,作为接收端,当你调用bind()函数绑定IP时使用INADDR_ANY,表明接收来自任意IP、任意网卡的发给指定端口的数据。作为发送端,当用调用bind()函数绑定IP时使用INADDR_ANY,表明使用网卡号最低的网卡进行发送数据,也就是UDP数据广播。
关于UDP数据报
UDP都是以数据报的形式进行发送和接收的,而TCP是以数据流的形式进行发送和接收的。数据报和数据流,这两者要区分开来。
UDP Server和Client源码实例
UDP Server:
02 | #include
<sys/socket.h> |
03 | #include
<netinet/in.h> |
10 | #define UDP_TEST_PORT 50001 |
12 | int main( int argC, char *
arg[]) |
14 | struct sockaddr_in
addr; |
16 | int addr_len
= sizeof ( struct sockaddr_in); |
19 | /*
建立socket,注意必须是SOCK_DGRAM */ |
20 | if ((sockfd
= socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
26 | bzero(&addr, sizeof (addr)); |
27 | addr.sin_family
= AF_INET; |
28 | addr.sin_port
= htons(UDP_TEST_PORT); |
29 | addr.sin_addr.s_addr
= htonl(INADDR_ANY) ; //
接收任意IP发来的数据 |
32 | if (bind(sockfd,
( struct sockaddr
*)&addr, sizeof (addr))<0)
{ |
38 | bzero(buffer, sizeof (buffer)); |
39 | len
= recvfrom(sockfd, buffer, sizeof (buffer),
0, |
40 | ( struct sockaddr
*)&addr ,&addr_len); |
41 | /*
显示client端的网络地址和收到的字符串消息 */ |
42 | printf ( "Received
a string from client %s, string is: %s\n" , |
43 | inet_ntoa(addr.sin_addr),
buffer); |
44 | /*
将收到的字符串消息返回给client端 */ |
45 | sendto(sockfd,buffer,
len, 0, ( struct sockaddr
*)&addr,addr_len); |
51 | //
---------------------------------------------------------------------------- |
UDP Client:
02 | #include
<sys/socket.h> |
03 | #include
<netinet/in.h> |
10 | #define UDP_TEST_PORT 50001 |
11 | #define UDP_SERVER_IP "127.0.0.1" |
13 | int main( int argC, char *
arg[]) |
15 | struct sockaddr_in
addr; |
17 | int addr_len
= sizeof ( struct sockaddr_in); |
20 | /*
建立socket,注意必须是SOCK_DGRAM */ |
21 | if ((sockfd
= socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
27 | bzero(&addr, sizeof (addr)); |
28 | addr.sin_family
= AF_INET; |
29 | addr.sin_port
= htons(UDP_TEST_PORT); |
30 | addr.sin_addr.s_addr
= inet_addr(UDP_SERVER_IP); |
33 | bzero(buffer, sizeof (buffer)); |
35 | printf ( "Please
enter a string to send to server: \n" ); |
38 | len
= read(STDIN_FILENO, buffer, sizeof (buffer)); |
41 | sendto(sockfd,
buffer, len, 0, ( struct sockaddr
*)&addr,addr_len); |
44 | len
= recvfrom(sockfd, buffer, sizeof (buffer),
0, |
45 | ( struct sockaddr
*)&addr,&addr_len); |
46 | printf ( "Receive
from server: %s\n" ,
buffer); |
52 | //
---------------------------------------------------------------------------- |
上述代码是经过验证可用的。