前言
本篇文章介绍UDP的socket编程流程,使用的接口都是Linux操作系统中的接口。这些编程接口可能与Windows操作系统中有些不同。文章中如果发现有错误的地方,可以一起讨论!!!
一、UDP协议
1.简单了解UDP协议
无连接:UDP客户端给服务端发送数据的时候,不需要和服务端先建立连接,而是直接进行发送数据 不可靠:不保证数据是可靠有序到达对端的 面向数据报:UDP数据不管是应用层还是网络层传递,都是整条数据进行交付。
2.网络字节序
数据在内存中进行存储时,有两种存储顺序:大端字节序和小端字节序。 大端字节序:数据的低位存储在高地址 小端字节序:数据的地位存储在低地址 在网络中进行收发数据时,也是需要遵守一定的规则,按照一定的字节序进行数据的组织。 网络字节序:采用的是大端字节序 主机字节序:根据机器本身采用的字节序(小端机器为小端字节序,大端机器为大端字节序) 如果机器的主机字节序为小端字节序,在网络中发送数据时就需要将主机字节序转换为网络字节序,否则发送的数据与接收到的数据就会有差异。 有两个接口可以进行主机字节序与网络字节序之间的转换: 1.主机字节序转换成网络字节序  2.网络字节序转换成主机字节序 
二、UDPsocket编程
1.编程流程
首先需要服务端与客户端创建套接字,然后进行绑定地址信息,之后就可以进行正常通信了。通信结束需要关闭套接字。 
2.编程接口
①创建套接字(socket)
使用socket函数创建套接字,具体参数含义和返回值内容见下图: 
②绑定地址信息(bind)
使用bind函数对地址信息进行绑定  struct sockaddr结构体内部信息: 
③发送数据(sendto)
调用sendto函数进行发送数据 
④接收数据(recvfrom)
调用recvfrom函数接收数据 
3.服务端测试
根据编程流程,使用socket函数创建套接字并绑定地址信息
1 #include<stdio.h>
2 #include<iostream>
3 #include<unistd.h>
4
5 #include<sys/socket.h>
6 #include<netinet/in.h>
7 #include<arpa/inet.h>
8 using namespace std;
9
10 int main()
11 {
12 int sock_fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
13 if(sock_fd < 0)
14 {
15 perror("socket");
16 return 0;
17 }
18 cout << "套接字描述符:" << sock_fd << endl;
19
20 struct sockaddr_in addr;
21 addr.sin_family = AF_INET;
22 addr.sin_port = htons(27089);
23 addr.sin_addr.s_addr = inet_addr("172.16.0.9");
24
25 int ret = bind(sock_fd,(struct sockaddr*)&addr, sizeof(addr));
26 if(ret < 0)
27 {
28 perror("bind");
29 return 0;
30 }
31 while(1)
32 {
33 sleep(1);
34 }
35 return 0;
36 }
程序运行结果  使用命令对服务端状态进行测试,下图状态标识服务端监听成功,可以进行收发数据了: 
3.客户端代码
1 #include<stdio.h>
2 #include<iostream>
3 #include<unistd.h>
4 #include<string.h>
5
6 #include<sys/socket.h>
7 #include<netinet/in.h>
8 #include<arpa/inet.h>
9 using namespace std;
10
11 int main()
12 {
13 int sock_fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
14 if(sock_fd < 0)
15 {
16 perror("socket");
17 return 0;
18 }
19 cout << "套接字描述符:" << sock_fd << endl;
20
21 struct sockaddr_in addr;
22 addr.sin_family = AF_INET;
23 addr.sin_port = htons(27089);
24 addr.sin_addr.s_addr = inet_addr("172.16.0.9");
25
26 while(1)
27 {
28 char buf[1024] = "i am client";
29
30 struct sockaddr_in dest_addr;
31 dest_addr.sin_family = AF_INET;
32 dest_addr.sin_port = htons(27089);
33 dest_addr.sin_addr.s_addr = inet_addr("1.14.151.67");
34
35 sendto(sock_fd, buf, strlen(buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
36
37 memset(buf, '\0', sizeof(buf));
38
39 struct sockaddr_in peer_addr;
40 socklen_t len = sizeof(peer_addr);
41 ssize_t recv_size = recvfrom(sock_fd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&peer_addr, &len);
42 if(recv_size < 0)
43 {
44 continue;
45 }
46
47 printf("收到消息: \"%s\" 来自:%s:%d\n", buf, inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
48 sleep(1);
49 }
50 close(sock_fd);
51 return 0;
52 }
4.服务端代码
1 #include<stdio.h>
2 #include<iostream>
3 #include<unistd.h>
4 #include<string.h>
5
6 #include<sys/socket.h>
7 #include<netinet/in.h>
8 #include<arpa/inet.h>
9 using namespace std;
10
11 int main()
12 {
13 int sock_fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
14 if(sock_fd < 0)
15 {
16 perror("socket");
17 return 0;
18 }
19 cout << "套接字描述符:" << sock_fd << endl;
20
21 struct sockaddr_in addr;
22 addr.sin_family = AF_INET;
23 addr.sin_port = htons(27089);
24 addr.sin_addr.s_addr = inet_addr("172.16.0.9");
25
26 int ret = bind(sock_fd,(struct sockaddr*)&addr, sizeof(addr));
27 if(ret < 0)
28 {
29 perror("bind");
30 return 0;
31 }
32
33 while(1)
34 {
35 char buf[1024] = {0};
36 struct sockaddr_in peer_addr;
37 socklen_t len = sizeof(peer_addr);
38 ssize_t recv_size = recvfrom(sock_fd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&peer_addr, &len);
39 if(recv_size < 0)
40 {
41 continue;
42 }
43 printf("收到消息: \"%s\" ,来自 %s:%d\n" , buf, inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
44 memset(buf, '\0', sizeof(buf));
45 sprintf(buf, "welcome client %s:%d", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
46 sendto(sock_fd, buf, strlen(buf), 0, (struct sockaddr*)&peer_addr, sizeof(peer_addr));
47 }
48
49 close(sock_fd);
50 return 0;
51 }
5.连接测试
测试结果如下,服务端和客户端能够正常进行通信 
6.缓冲区的概念
UDP在进行发送数据时,是先将数据放入UDP的发送缓冲区当中,然后经过网络协议栈对数据进行层层封装,然后到达服务端的接收缓冲区,服务端再从接收缓冲区中读数据。 同理服务端向客户端发送数据时也是需要经过缓冲区这样一个过程,如下图: 
|