TCP网络编程实例

Linux网络编程:TCP网络编程实例。

问题描述

在Linux系统下,采用C语言编写一个TCP服务器程序和一个TCP客户端程序。客户端向服务器发送字符串后,服务器能把该字符串返回给客户端,客户端再把字符串显示出来。服务器采用多进程并发服务的方式,保证多个客户连接到该服务器。如果客户端向服务器端发送GetTime字符串时,客户机端能接收并显示当前服务器端的系统时间。

TCP协议

什么是TCP协议

TCP编程框架

代码实现

头文件

1
// tcp.h
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <sys/types.h>
6
#include <sys/socket.h>
7
#include <netinet/in.h>
8
#include <arpa/inet.h>
9
#include <netinet/in.h>
10
#include <ctype.h>
11
#include <errno.h>
12
#include <signal.h>
13
#include <unistd.h>
14
#include <time.h>

服务器端程序

1
// tcp_server
2
#include"tcp.h" // TCP编程所需头文件
3
#define SERVER_IP "127.0.0.1" // 服务器IP
4
#define SERVER_PORT 8000	// 服务器端口
5
#define MAX_LISTEN 5 // 最大连接客户程序数
6
#define MAXLINE 200	// 数据缓冲区大小
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(&times);
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
#include"tcp.h" // TCP编程所需头文件
3
#define SERVER_IP "127.0.0.1" // 服务器地址
4
#define SERVER_PORT 8000 // 服务器端口
5
#define MAXLINE 200	// 数据缓冲区大小
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