Linux网络编程:TCP网络编程实例。
问题描述
在Linux系统下,采用C语言编写一个TCP服务器程序和一个TCP客户端程序。客户端向服务器发送字符串后,服务器能把该字符串返回给客户端,客户端再把字符串显示出来。服务器采用多进程并发服务的方式,保证多个客户连接到该服务器。如果客户端向服务器端发送GetTime字符串时,客户机端能接收并显示当前服务器端的系统时间。
TCP协议
什么是TCP协议
TCP编程框架
代码实现
头文件
1 | // tcp.h |
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
服务器端程序
1 | // tcp_server |
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | |
8 | // 多进程并发服务 |
9 | void server_process(int conn_fd) { |
10 | int recv_num, send_num; // 收发字节数 |
11 | char recv_buf[MAXLINE], send_buf[MAXLINE]; // 收发数据缓冲区 |
12 | int pid = getpid(); // 当前进程号 |
13 | printf("\nService for client %d has started.\n", pid); |
14 | while(1) { |
15 | recv_num = recv(conn_fd, recv_buf, sizeof(recv_buf), 0); // 接收客户端的网络数据 |
16 | if(recv_num < 0) { |
17 | close(conn_fd); |
18 | exit(1); |
19 | } |
20 | recv_buf[recv_num] = '\0'; |
21 | printf("Child process %d received: %s\n", pid, recv_buf); |
22 | if(strcmp(recv_buf,"quit") == 0) { |
23 | printf("Child process %d quit.\n", pid); |
24 | break; |
25 | } |
26 | strcpy(send_buf, recv_buf); |
27 | |
28 | // 如果需要获取时间 |
29 | char Get[MAXLINE] = "GetTime"; |
30 | int flag = 1; |
31 | for(int i=0; i<7; i++) { |
32 | if(send_buf[i] == Get[i]) |
33 | continue; |
34 | else flag = 0; |
35 | } |
36 | if(flag == 1) { |
37 | time_t times = time(NULL); |
38 | struct tm* utcTime = gmtime(×); |
39 | sprintf(send_buf, "%04d.%02d.%02d %02d:%02d:%02d", utcTime->tm_year+1900, utcTime->tm_mon+1, utcTime->tm_mday, utcTime->tm_hour+8, utcTime->tm_min, utcTime->tm_sec); |
40 | } |
41 | |
42 | send_num = send(conn_fd, send_buf, strlen(send_buf), 0); // 向客户端主机发送数据 |
43 | if(send_num < 0) { |
44 | close(conn_fd); |
45 | exit(1); |
46 | } |
47 | printf("Child process %d sent: %s\n", pid, send_buf); |
48 | } |
49 | close(conn_fd); |
50 | } |
51 | |
52 | int main() { |
53 | pid_t pid; // 进程号 |
54 | int conn_fd; // 接收的套接字文件描述符 |
55 | int sock_fd = socket(AF_INET, SOCK_STREAM, 0); // 建立套接字文件描述符 |
56 | if(sock_fd < 0) { |
57 | perror("Create socket failed"); |
58 | exit(1); |
59 | } |
60 | struct sockaddr_in client_address; // 存放数据发送方的地址信息 |
61 | socklen_t client_size = sizeof(client_address); // client_address所指内容的长度 |
62 | struct sockaddr_in server_address; // 服务端网络地址结构信息 |
63 | memset(&server_address, 0, sizeof(server_address)); |
64 | server_address.sin_family = AF_INET; // 地址类型为AF_INET |
65 | server_address.sin_port = htons(SERVER_PORT); // 服务器端口 |
66 | server_address.sin_addr.s_addr = inet_addr(SERVER_IP); // 服务器地址 |
67 | |
68 | if(bind(sock_fd, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) { // 绑定侦听端口 |
69 | perror("Bind error"); |
70 | exit(1); |
71 | } |
72 | if(listen(sock_fd, MAX_LISTEN) < 0) { // 开始监听(参数: 服务器的套接字, 连接等待队列的最大数目) |
73 | perror("Listen failed"); |
74 | exit(1); |
75 | } |
76 | while(1) { |
77 | conn_fd = accept(sock_fd, (struct sockaddr *)&client_address, &client_size); // 开始接收客户端的连接 |
78 | if(conn_fd < 0) { |
79 | perror("Accept failed"); |
80 | exit(1); |
81 | } |
82 | pid = fork(); // 创建子进程 |
83 | if(pid == 0) { // 当前是子进程 |
84 | close(sock_fd); |
85 | server_process(conn_fd); // 该子进程服务客户请求conn_fd |
86 | }else close(conn_fd); |
87 | } |
88 | close(sock_fd); |
89 | return 0; |
90 | } |
客户端程序
1 | // tcp_client |
2 |
|
3 |
|
4 |
|
5 |
|
6 | |
7 | int main() { |
8 | int sock_fd = socket(AF_INET, SOCK_STREAM, 0); // 建立套接字文件描述符 |
9 | if(sock_fd < 0) { |
10 | perror("create socket failed"); |
11 | exit(1); |
12 | } |
13 | struct sockaddr_in server_address; // 服务器网络地址结构信息 |
14 | memset(&server_address, 0 ,sizeof(server_address)); |
15 | server_address.sin_family = AF_INET; // 地址类型为AF_INET |
16 | server_address.sin_port = htons(SERVER_PORT); // 服务器端口 |
17 | server_address.sin_addr.s_addr = inet_addr(SERVER_IP); // 服务器地址 |
18 | if(connect(sock_fd, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) { // 把socket套接字连上服务端 |
19 | perror("Connect failed"); |
20 | exit(1); |
21 | } |
22 | int recv_num, send_num; // 收发字节数 |
23 | char recv_buf[MAXLINE], send_buf[MAXLINE]; // 收发数据缓冲区 |
24 | while(1) { |
25 | scanf("%s", send_buf); |
26 | send_num = send(sock_fd, send_buf, strlen(send_buf), 0); |
27 | if(send_num < 0) { |
28 | perror("Send error"); |
29 | exit(1); |
30 | } |
31 | recv_num = recv(sock_fd, recv_buf, sizeof(recv_buf), 0); |
32 | if(recv_num < 0) { |
33 | perror("Receive error"); |
34 | exit(1); |
35 | } |
36 | recv_buf[recv_num] = '\0'; |
37 | printf("%s\n", recv_buf); |
38 | } |
39 | close(sock_fd); |
40 | return 0; |
41 | } |
测试程序
编译并运行服务器端程序
1 | gcc -o tcp_server tcp_server.c |
2 | ./tcp_server |
编译并运行(多个)客户端程序
1 | gcc -o tcp_client tcp_client.c |
2 | ./tcp_client |