1. TCP回显服务器

TCP回显服务器 学习卡片

任务说明

本案例要求实现一个TCP回显服务器:服务器监听指定端口,接收客户端发送的任意文本消息,并将消息原样返回给客户端。通过该最小可运行示例,掌握Linux下socket编程的基本流程:创建socket、绑定地址、监听、接受连接、收发数据、关闭连接。

实现方案

服务器端使用socket()创建IPv4 TCP套接字,通过bind()绑定到本地回环地址的指定端口,listen()进入监听状态。主循环中调用accept()接受客户端连接,为每个连接创建子进程处理回显逻辑,父进程继续等待新连接。客户端使用connect()发起连接,通过send()/recv()完成一次消息发送与接收,验证服务器返回内容与发送内容一致。

代码实现

以下为服务器端完整代码(echo_server.c):

// echo_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 8888
#define BUFFER_SIZE 1024

int main() {
    int server_fd, client_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    char buffer[BUFFER_SIZE];

    // 1. 创建socket
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 2. 绑定地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 3. 监听
    if (listen(server_fd, 5) < 0) {
        perror("listen");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("Echo server listening on port %d...\n", PORT);

    // 4. 接受连接并处理
    while (1) {
        client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
        if (client_fd < 0) {
            perror("accept");
            continue;
        }

        // 子进程处理回显
        pid_t pid = fork();
        if (pid == 0) {
            close(server_fd);  // 子进程关闭监听socket
            ssize_t n;
            while ((n = recv(client_fd, buffer, BUFFER_SIZE, 0)) > 0) {
                send(client_fd, buffer, n, 0);  // 原样返回
            }
            close(client_fd);
            exit(0);
        }
        close(client_fd);  // 父进程关闭已连接socket
    }

    close(server_fd);
    return 0;
}

以下为客户端完整代码(echo_client.c):

// echo_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 8888
#define BUFFER_SIZE 1024

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <message>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int sock;
    struct sockaddr_in server_addr;
    char buffer[BUFFER_SIZE];

    // 1. 创建socket
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 2. 连接服务器
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) {
        perror("inet_pton");
        close(sock);
        exit(EXIT_FAILURE);
    }

    if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect");
        close(sock);
        exit(EXIT_FAILURE);
    }

    // 3. 发送消息并接收回显
    const char *msg = argv[1];
    send(sock, msg, strlen(msg), 0);

    ssize_t n = recv(sock, buffer, BUFFER_SIZE, 0);
    if (n > 0) {
        buffer[n] = '\0';
        printf("Received: %s\n", buffer);
    }

    // 4. 关闭连接
    close(sock);
    return 0;
}

编译与运行

在Linux终端中执行以下命令:

gcc -o echo_server echo_server.c
gcc -o echo_client echo_client.c

先启动服务器(终端1):

./echo_server

再运行客户端(终端2):

./echo_client "Hello, TCP!"

验证方法

观察客户端输出,若打印内容为Received: Hello, TCP!,且与发送的消息完全一致,则说明回显功能正常。服务器终端会持续显示监听状态,不会输出每条消息内容。可通过Ctrl+C终止服务器进程。

预期输出

客户端执行后,终端显示:

Received: Hello, TCP!

该输出由代码逻辑直接推出:客户端发送argv[1]内容,服务器recv()后原样send()返回,客户端recv()接收并打印。若发送其他消息,输出内容将对应变化。

TCP回显服务器通信时序 客户端 服务器 TCP连接 (127.0.0.1:8888) socket() socket() bind() listen() connect() accept() send("Hello, TCP!") recv() → 原样返回 close() close() 核心流程:socket → bind → listen → accept → recv/send → close