在網絡編程中,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. */ |
04 |
sa_family_t
sin_family; /* Address family */ |
05 |
__be16
sin_port; /* Port number */ |
06 |
struct
in_addr sin_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:
sin_port:指明UDP端口;sin_addr指明IP地址:
1 |
/*
Internet address. */ |
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:
01 |
#include
<sys/types.h> |
02 |
#include
<sys/socket.h> |
03 |
#include
<netinet/in.h> |
04 |
#include
<arpa/inet.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); |
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) ; |
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); |
42 |
printf ( "Received
a string from client %s, string is: %s\n" , |
43 |
inet_ntoa(addr.sin_addr),
buffer); |
45 |
sendto(sockfd,buffer,
len, 0, ( struct sockaddr
*)&addr, addr_len); |
UDP Client:
01 |
#include
<sys/types.h> |
02 |
#include
<sys/socket.h> |
03 |
#include
<netinet/in.h> |
04 |
#include
<arpa/inet.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); |
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); |
上述代碼是經過驗證可用的。