Linux网络编程:UDP网络编程实例。
问题描述
在Linux系统下,采用C语言编写一个UDP服务器程序和一个UDP客户端程序。客户端向服务器发送字符串后,服务器能把该字符串返回给客户端,客户端再把字符串显示出来。如果客户端向服务器端发送GetTime字符串时,客户机端能接收并显示当前服务器端的系统时间。
UDP协议
什么是UDP协议
UDP(User Datagram Protocol),即用户数据报协议,是一种面向非连接的协议。面向非连接指的是在正式通信前不必与对方先建立连接,不管对方状态就直接发送。至于对方是否可以接收到这些数据内容,UDP协议无法控制,因此UDP协议是一种不可靠的协议。
UDP适用于一次只传送少量数据、对可靠性要求不高的应用和环境。由于没有建立连接的过程,所以它的通信效率高。使用UDP传输,需要发送端和接收端。
UDP特点:无连接的,不可靠的,数据报包小于等于64k,效率高。
UDP编程框架
UDP网络编程可以分为客户端和服务器端两部分。服务器端主要包含建立套接字、将套接字与地址结构进行绑定、读写数据、关闭套接字几个过程。客户端包括建立套接字、读写数据、关闭套接字几个过程。服务器端和客户端两个流程之间的主要差别在于对地址的绑定(bind())函数,客户端可以不用进行地址和端口的绑定操作。
代码实现
头文件
1 | // udp.h |
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
服务器端程序
1 | // udp_server.c |
2 |
|
3 |
|
4 |
|
5 | char data_buf[MAXLINE]; // 数据缓冲区 |
6 | int main(void) { |
7 | int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // socket()建立套接字文件描述符 |
8 | struct sockaddr_in server_address; // 服务端网络地址结构信息 |
9 | struct sockaddr_in client_address; // 存放数据发送方的地址信息 |
10 | socklen_t client_address_len; // client_address所指内容的长度 |
11 | char str[INET_ADDRSTRLEN]; |
12 | bzero(&server_address, sizeof(server_address)); |
13 | server_address.sin_family = AF_INET; // 地址类型为AF_INET |
14 | server_address.sin_addr.s_addr = htonl(INADDR_ANY); // 任意本地地址 |
15 | server_address.sin_port = htons(SERV_PORT); // 服务器端口 |
16 | bind(sockfd, (struct sockaddr *)&server_address, sizeof(server_address)); // bind()函数绑定侦听端口 |
17 | |
18 | printf("Accepting connections ...\n"); |
19 | while (1) { |
20 | client_address_len = sizeof(client_address); |
21 | // recvfrom()函数接收客户端的网络数据 |
22 | int n = recvfrom(sockfd, data_buf, MAXLINE, 0, (struct sockaddr *)&client_address, &client_address_len); |
23 | if (n == -1) |
24 | perror("Recvfrom error"); |
25 | printf("Received from %s at PORT %d\n", inet_ntop(AF_INET, &client_address.sin_addr, str, sizeof(str)), ntohs(client_address.sin_port)); |
26 | // 如果需要获取时间 |
27 | char Get[MAXLINE] = "GetTime"; |
28 | int flag = 1; |
29 | for(int i=0; i<7; i++) { |
30 | if(data_buf[i] == Get[i]) |
31 | continue; |
32 | else flag = 0; |
33 | } |
34 | if(flag == 1) { |
35 | time_t times = time(NULL); |
36 | struct tm* utcTime = gmtime(×); |
37 | sprintf(data_buf, "%04d.%02d.%02d %02d:%02d:%02d\n", utcTime->tm_year+1900, utcTime->tm_mon+1, utcTime->tm_mday, utcTime->tm_hour+8, utcTime->tm_min, utcTime->tm_sec); |
38 | n = sizeof(data_buf); |
39 | } |
40 | // sendto()函数向服务器主机发送数据 |
41 | n = sendto(sockfd, data_buf, n, 0, (struct sockaddr*)&client_address, sizeof(client_address)); |
42 | if (n == -1) |
43 | perror("Sendto error"); |
44 | } |
45 | return 0; |
46 | } |
客户端程序
1 | // udp_client.c |
2 |
|
3 |
|
4 |
|
5 | int main(int argc, char *argv[]) { |
6 | int sockfd = sockfd = socket(AF_INET, SOCK_DGRAM, 0); // Socket()函数建立套接字文件描述符 |
7 | struct sockaddr_in server_address; // 服务端网络地址结构信息 |
8 | socklen_t server_address_len; // server_address所指内容的长度 |
9 | char data_buf[MAXLINE]; // 数据缓冲区 |
10 | char str[INET_ADDRSTRLEN]; |
11 | bzero(&server_address, sizeof(server_address)); |
12 | server_address.sin_family = AF_INET; // 地址类型为AF_INET |
13 | inet_pton(AF_INET, "127.0.0.1", &server_address.sin_addr); |
14 | server_address.sin_port = htons(SERV_PORT); // 服务器端口 |
15 | |
16 | while (fgets(data_buf, MAXLINE, stdin) != NULL) { |
17 | // sendto()向服务器发送数据 |
18 | int n = sendto(sockfd, data_buf, strlen(data_buf), 0, (struct sockaddr *)&server_address, sizeof(server_address)); |
19 | if (n == -1) |
20 | perror("Sendto error"); |
21 | // recvfrom()接收服务器的数据 |
22 | n = recvfrom(sockfd, data_buf, MAXLINE, 0, NULL, 0); |
23 | if (n == -1) |
24 | perror("Recvfrom error"); |
25 | write(STDOUT_FILENO, data_buf, n); |
26 | } |
27 | // 关闭套接字 |
28 | close(sockfd); |
29 | return 0; |
30 | } |
测试程序
编译并运行服务器端程序
1 | gcc -o udp_server udp_server.c |
2 | ./udp_server |
编译并运行客户端程序
1 | gcc -o udp_client udp_client.c |
2 | ./udp_client |