6.4 TCP编程简单示例
服务器首先进行初始化操作:调用函数socket创建一个套接字,函数bind将这个套接字与服务器的公认地址绑定在一起,函数listen将这个套接字换成倾听套接字,然后调用函数accept来等待客户机的请求。过了一段时间后,客户机启动,调用socket创建一个套接字,然后调用函数connect来与服务器建立连接。连接建立之后,客户机和服务器通过读、写套接字来进行通信。
6.4.1 服务器端代码
参考:TCP/server_line.c
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/types.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#define SERVER_PORT 8180
12#define C_QUEUE 10
13
14/************************************************************
15*函数功能描述:从8180端口接收客户端数据
16*输入参数:无
17*输出参数:打印客户IP以及发来的信息
18*返回值:无
19*修改日期 版本号 修改人 修改内容
20*2020/05/13 v1.0.0 zonghzha reat
21*************************************************************/
22
23int main(int argc, char **argv)
24{
25 char buf[512];
26 int len;
27 int duty_socket;
28 int customer_socket;
29 struct sockaddr_in socket_server_addr;
30 struct sockaddr_in socket_client_addr;
31 int ret;
32 int addr_len;
33
34 signal(SIGCHLD, SIG_IGN);
35
36 /* 服务器端开始建立socket描述符 */
37 duty_socket = socket(AF_INET, SOCK_STREAM, 0);
38 if (duty_socket == -1)
39 {
40 printf("socket error");
41 return -1;
42 }
43
44 /* 服务器端填充 sockaddr_in结构 */
45 socket_server_addr.sin_family = AF_INET;
46 /* 端口号转换为网络字节序 */
47 socket_server_addr.sin_port = htons(SERVER_PORT);
48 /* 接收本机所有网口的数据 */
49 socket_server_addr.sin_addr.s_addr = INADDR_ANY;
50 memset(socket_server_addr.sin_zero, 0, 8);
51
52 /* 捆绑sockfd描述符 */
53 ret = bind(duty_socket, (const struct sockaddr *)&socket_server_addr, sizeof(struct sockaddr));
54 if (ret == -1)
55 {
56 printf("bind error!\n");
57 return -1;
58 }
59 ret = listen(duty_socket, C_QUEUE);
60 if (ret == -1)
61 {
62 printf("listen error!\n");
63 return -1;
64 }
65
66 while (1)
67 {
68 addr_len = sizeof(struct sockaddr);
69 /* 服务器阻塞,直到客户程序建立连接 */
70 customer_socket = accept(duty_socket, (struct sockaddr *)&socket_client_addr, &addr_len);
71 if (customer_socket != -1)
72 {
73 /*inet_ntoa的作用是将一个32位Ipv4地址转换为相应的点分十进制数串*/
74 printf("Get connect from %s\n", inet_ntoa(socket_client_addr.sin_addr));
75 }
76 if (!fork())
77 {
78 while (1)
79 {
80 memset(buf, 512, 0);
81 /*接收数据*/
82 len = recv(customer_socket, buf, sizeof(buf), 0);
83 buf[len] = '\0';
84 if (len <= 0)
85 {
86 close(customer_socket);
87 return -1;
88 }
89 else
90 {
91 printf("Get connect from %s, Msg is %s\n", inet_ntoa(socket_client_addr.sin_addr), buf);
92 }
93 }
94 }
95 }
96
97 close(duty_socket);
98 return 0;
99}
6.4.2 客户端代码
参考:TCP/client_line.c
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/types.h>
5#include <sys/socket.h>
6#include <netinet/in.h>
7#include <arpa/inet.h>
8#include <unistd.h>
9
10#define SERVER_PORT 8180
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 /* 客户程序开始建立 sockfd描述符 */
38 client_socket = socket(AF_INET, SOCK_STREAM, 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 /*主机字节序转换为网络字节序*/
48 socket_server_addr.sin_port = htons(SERVER_PORT);
49 if (inet_aton(argv[1], &socket_server_addr.sin_addr) == 0)
50 {
51 printf("invalid server ip\n");
52 return -1;
53 }
54 memset(socket_server_addr.sin_zero, 0, 8);
55 /* 客户程序发起连接请求 */
56 ret = connect(client_socket, (const struct sockaddr *)&socket_server_addr, sizeof(struct sockaddr));
57 if (ret == -1)
58 {
59 printf("connect error!\n");
60 return -1;
61 }
62
63
64 while (1)
65 {
66 if (fgets(buf, sizeof(buf), stdin))
67 {
68 len = send(client_socket, buf, strlen(buf), 0);
69 if (len <= 0)
70 {
71 close(client_socket);
72 return -1;
73 }
74 }
75 }
76
77 close(client_socket);
78 return 0;
79}
6.4.3 Makefile文件
all:server client
server:server.c
gcc $^ -o $@
client:client.c
gcc $^ -o $@
clean:
rm server client -f
(注意:命令语句的开头要用“Tab”键。)
6.4.4 执行
服务器端:
./server
客户端:
./client 127.0.0.1
6.5 UDP编程简单示例
UDP服务器首先进行初始化操作:调用函数socket创建一个数据报类型的套接字,函数bind将这个套接字与服务器的公认地址绑定在一起。然后调用函数recvfrom接收UDP客户机的数据报。UDP客户机首先调用函数socket创建一个数据报套接字,然后调用函数sendto向服务器发送数据报。在结束通信后,客户机调用close关闭UDP套接字,服务器继续使用这个UDP套接字接收其它客户机的数据报。
6.5.1 服务器端代码
参考UDP/server_line.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
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
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文件
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 执行
服务器端执行:
./server
客户端执行:
./client_1 127.0.0.1
客户端输入:
good night
服务器端显示:
Get Msg from 127.0.0.1 : good night
客户端输入:
good night
服务器端显示:
Get connect from 127.0.0.1
Get connect from 127.0.0.1, Msg is good night