UDP sendto和recvfrom使用

在網絡編程中,UDP運用非常廣泛。很多網絡協議是基於UDP來實現的,如SNMP等。大家常常用到的局域網文件傳輸軟件飛鴿傳書也是基於UDP實現的。

本篇文章跟大家分享linux下UDP的使用和實現,主要介紹下sendto()和recvfrom()兩個函數的使用,以及INADDR_ANY的說明,並在最後展示了一個經過自己測試可用的UDP Server和UDP Client的代碼示例。

頭文件

1 #include <sys/types.h>
2 #include <sys/socket.h>

函數原型

1 int sendto (int s, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen);
2  
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)  */
03 struct sockaddr_in {
04   sa_family_t       sin_family; /* Address family       */
05   __be16        sin_port;   /* Port number          */
06   struct in_addr    sin_addr;   /* Internet address     */
07  
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)];
11 };

其中,sin_family指明地址族,一般使用AF_INET:

1 #define AF_INET     2   /* Internet IP Protocol     */

sin_port:指明UDP端口;sin_addr指明IP地址:

1 /* Internet address. */
2 struct in_addr {
3     __be32  s_addr;
4 };

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>
05 #include <unistd.h>
06 #include <stdlib.h>
07 #include <string.h>
08 #include <stdio.h>
09  
10 #define UDP_TEST_PORT       50001
11  
12 int main(int argC, char* arg[])
13 {
14     struct sockaddr_in addr;
15     int sockfd, len = 0;   
16     int addr_len = sizeof(struct sockaddr_in);
17     char buffer[256];  
18  
19     /* 建立socket,注意必須是SOCK_DGRAM */
20     if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
21         perror ("socket");
22         exit(1);
23     }
24  
25     /* 填寫sockaddr_in 結構 */
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發來的數據
30  
31     /* 綁定socket */
32     if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr))<0) {
33         perror("connect");
34         exit(1);
35     }
36  
37     while(1) {
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);
46     }
47  
48     return 0;
49 }
50  
51 // ----------------------------------------------------------------------------
52 // End of udp_server.c

UDP Client:

01 #include <sys/types.h>
02 #include <sys/socket.h>
03 #include <netinet/in.h>
04 #include <arpa/inet.h>
05 #include <unistd.h>
06 #include <stdlib.h>
07 #include <string.h>
08 #include <stdio.h>
09  
10 #define UDP_TEST_PORT       50001
11 #define UDP_SERVER_IP       "127.0.0.1"
12  
13 int main(int argC, char* arg[])
14 {
15     struct sockaddr_in addr;
16     int sockfd, len = 0;   
17     int addr_len = sizeof(struct sockaddr_in);     
18     char buffer[256];
19  
20     /* 建立socket,注意必須是SOCK_DGRAM */
21     if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
22         perror("socket");
23         exit(1);
24     }
25  
26     /* 填寫sockaddr_in*/
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);
31  
32     while(1) {
33         bzero(buffer, sizeof(buffer));
34  
35         printf("Please enter a string to send to server: \n");
36  
37         /* 從標準輸入設備取得字符串*/
38         len = read(STDIN_FILENO, buffer, sizeof(buffer));
39  
40         /* 將字符串傳送給server端*/
41         sendto(sockfd, buffer, len, 0, (struct sockaddr *)&addr, addr_len);
42  
43         /* 接收server端返回的字符串*/
44         len = recvfrom(sockfd, buffer, sizeof(buffer), 0,
45                        (struct sockaddr *)&addr, &addr_len);
46         printf("Receive from server: %s\n", buffer);
47     }
48  
49     return 0;
50 }
51  
52 // ----------------------------------------------------------------------------
53 // End of udp_client.c

上述代碼是經過驗證可用的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章