6.5 UDP编程简单示例
UDP服务器首先进行初始化操作:调用函数socket创建一个数据报类型的套接字,函数bind将这个套接字与服务器的公认地址绑定在一起。然后调用函数recvfrom接收UDP客户机的数据报。UDP客户机首先调用函数socket创建一个数据报套接字,然后调用函数sendto向服务器发送数据报。在结束通信后,客户机调用close关闭UDP套接字,服务器继续使用这个UDP套接字接收其它客户机的数据报。
6.5.1 服务器端代码
参考UDP/server_line.c
c
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4//#include <sys/type.h>
5#include <sys/socket.h>
6#include <netinet/in.h>
7#include <arpa/inet.h>
8#include <unistd.h>
9#include <signal.h>
10
11/*服务器端口为8180*/
12#define SERVER_PORT 8180
13
14/************************************************************
15*函数功能描述:从8180端口接收客户端数据
16*输入参数:无
17*输出参数:打印客户IP以及发来的信息
18*返回值:无
19*修改日期 版本号 修改人 修改内容
20*2020/05/13 v1.0.0 zonghzha creat
21*************************************************************/
22
23
24int main(int argc, char **argv)
25{
26 unsigned char buf[512];
27 int len;
28 int duty_socket;
29 int customer_socket;
30 struct sockaddr_in socket_server_addr;
31 struct sockaddr_in socket_client_addr;
32 int ret;
33 int addr_len;
34
35 /* 创建数据报套接字 */
36 duty_socket = socket(AF_INET, SOCK_DGRAM, 0);
37 if (duty_socket == -1)
38 {
39 printf("socket error");
40 return -1;
41 }
42
43 /* 服务器端填充 sockaddr_in结构 */
44 socket_server_addr.sin_family = AF_INET;
45 socket_server_addr.sin_port = htons(SERVER_PORT);
46 socket_server_addr.sin_addr.s_addr = INADDR_ANY;
47 memset(socket_server_addr.sin_zero, 0, 8);
48
49 /*绑定套接字*/
50 ret = bind(duty_socket, (const struct sockaddr *)&socket_server_addr, sizeof(struct sockaddr));
51 if (ret == -1)
52 {
53 printf("bind error!\n");
54 return -1;
55 }
56
57
58 while (1)
59 {
60 addr_len = sizeof(struct sockaddr);
61 /* 接收客户端数据报,返回的为接收到的字节数 */
62 len = recvfrom(duty_socket, buf, sizeof(buf), 0, (struct sockaddr *)&socket_client_addr, &addr_len);
63 if (len > 0)
64 {
65 buf[len] = '\0';
66 printf("Get Msg from %s : %s\n", inet_ntoa(socket_client_addr.sin_addr), buf);
67 }
68
69 }
70
71 close(duty_socket);
72 return 0;
73}
74
6.5.2 客户端代码
6.5.2.1 客户端程序1
参考UDP/client_line_1.c
c
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/socket.h>
5#include <netinet/in.h>
6#include <arpa/inet.h>
7#include <unistd.h>
8
9#define SERVER_PORT 8180
10
11/************************************************************
12*函数功能描述:向指定IP的8180端口发送数据
13*输入参数:点分十进制服务器IP
14*输出参数:无
15*返回值:无
16*修改日期 版本号 修改人 修改内容
17*2020/05/13 v1.0.0 zonghzha creat
18*************************************************************/
19
20int main(int argc, char **argv)
21{
22 unsigned char buf[512];
23 int len;
24 struct sockaddr_in socket_server_addr;
25 int ret;
26 int addr_len;
27 int client_socket;
28
29
30 if (argc != 2)
31 {
32 printf("Usage:\n");
33 printf("%s <server_ip>\n", argv[0]);
34 return -1;
35 }
36
37 /*创建套接字*/
38 client_socket = socket(AF_INET, SOCK_DGRAM, 0);
39 if (client_socket == -1)
40 {
41 printf("socket error");
42 return -1;
43 }
44
45 /* 填充服务端的资料 */
46 socket_server_addr.sin_family = AF_INET;
47 socket_server_addr.sin_port = htons(SERVER_PORT);
48 if (inet_aton(argv[1], &socket_server_addr.sin_addr) == 0)
49 {
50 printf("invalid server ip\n");
51 return -1;
52 }
53 memset(socket_server_addr.sin_zero, 0, 8);
54
55
56
57
58 while (1)
59 {
60 if (fgets(buf, sizeof(buf), stdin))
61 {
62 // len = send(client_socket, buf, strlen(buf), 0);
63 /*向服务器端发送数据报*/
64 addr_len = sizeof(struct sockaddr);
65 len = sendto(client_socket, buf, sizeof(buf), 0, (struct sockaddr *)&socket_server_addr, addr_len);
66 if (len <= 0)
67 {
68 close(client_socket);
69 return -1;
70 }
71 }
72 }
73
74 close(client_socket);
75 return 0;
76}
77
问:用UDP协议写网络通讯程序不可以用connect函数吗?
答:非也。
6.5.2.2 客户端程序2
参考UDP/client_line_2.c
c
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/socket.h>
5#include <netinet/in.h>
6#include <arpa/inet.h>
7#include <unistd.h>
8
9/*服务器端口为8180*/
10#define SERVER_PORT 8180
11
12/************************************************************
13*函数功能描述:向指定IP的8180端口发送数据
14*输入参数:点分十进制服务器IP
15*输出参数:无
16*返回值:无
17*修改日期 版本号 修改人 修改内容
18*2020/05/13 v1.0.0 zonghzha creat
19*************************************************************/
20
21int main(int argc, char **argv)
22{
23 unsigned char buf[512];
24 int len;
25 struct sockaddr_in socket_server_addr;
26 int ret;
27 int addr_len;
28 int client_socket;
29
30
31 if (argc != 2)
32 {
33 printf("Usage:\n");
34 printf("%s <server_ip>\n", argv[0]);
35 return -1;
36 }
37
38 /*创建数据报套接字*/
39 client_socket = socket(AF_INET, SOCK_DGRAM, 0);
40 if (client_socket == -1)
41 {
42 printf("socket error");
43 return -1;
44 }
45
46 socket_server_addr.sin_family = AF_INET;
47 socket_server_addr.sin_port = htons(SERVER_PORT);
48 if (inet_aton(argv[1], &socket_server_addr.sin_addr) == 0)
49 {
50 printf("invalid server ip\n");
51 return -1;
52 }
53 memset(socket_server_addr.sin_zero, 0, 8);
54
55 ret = connect(client_socket, (const struct sockaddr *)&socket_server_addr, sizeof(struct sockaddr));
56 if (ret == -1)
57 {
58 printf("connect error!\n");
59 return -1;
60 }
61
62
63 while (1)
64 {
65 if (fgets(buf, sizeof(buf), stdin))
66 {
67 len = send(client_socket, buf, strlen(buf), 0);
68 if (len <= 0)
69 {
70 close(client_socket);
71 return -1;
72 }
73 }
74 }
75
76 close(client_socket);
77 return 0;
78}
79
在客户端代码2中,connect函数并非真的在协议层建立了连接,它只是指定了服务器的地址和端口号信息。
因为在connect中指定了服务器的地址和端口号信息,所以后面的send就可以直接发送了,而不用再次指定地址和端口号。
6.5.3 Makefile文件
c
all:server client_1 client_2
server:server.c
gcc $^ -o $@
client_1:client_1.c
gcc $^ -o $@
client_2:client_2.c
gcc $^ -o $@
clean:
rm server client_1 client_2 -f
(注意:命令语句的开头要用"Tab"键。)
6.5.4 执行
服务器端执行:
c
./server
客户端执行:
c
./client_1 127.0.0.1
客户端输入:
c
good night
服务器端显示:
c
Get Msg from 127.0.0.1 : good night