2. TCP客户端连接

任务说明

本案例要求编写一个TCP客户端程序,实现以下功能:主动连接指定的服务器地址和端口,连接建立后向服务器发送一段数据,并接收服务器返回的数据,最后关闭连接。通过本案例掌握Linux下socket编程中connect()、send()和recv()三个核心函数的使用方法。

实现方案

客户端程序采用以下流程:创建socket → 设置服务器地址结构 → 调用connect()建立连接 → 调用send()发送数据 → 调用recv()接收服务器响应 → 关闭socket。程序支持通过命令行参数指定服务器IP和端口,默认连接本机8888端口。为保证可复现性,代码中内置了默认的测试服务器地址(127.0.0.1:8888),并允许用户覆盖。

完整代码


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

#define BUFFER_SIZE 1024

int main(int argc, char *argv[]) {
    const char *server_ip = "127.0.0.1";
    int server_port = 8888;
    
    // 解析命令行参数:./tcp_client [server_ip] [server_port]
    if (argc > 1) server_ip = argv[1];
    if (argc > 2) server_port = atoi(argv[2]);
    
    // 1. 创建socket
    int client_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (client_fd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    
    // 2. 配置服务器地址
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(server_port);
    if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) {
        perror("inet_pton");
        close(client_fd);
        exit(EXIT_FAILURE);
    }
    
    // 3. 连接服务器
    if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect");
        close(client_fd);
        exit(EXIT_FAILURE);
    }
    printf("已连接到 %s:%d\n", server_ip, server_port);
    
    // 4. 发送数据
    const char *message = "Hello, TCP Server!";
    ssize_t send_len = send(client_fd, message, strlen(message), 0);
    if (send_len < 0) {
        perror("send");
        close(client_fd);
        exit(EXIT_FAILURE);
    }
    printf("已发送 %zd 字节: %s\n", send_len, message);
    
    // 5. 接收服务器响应
    char buffer[BUFFER_SIZE];
    memset(buffer, 0, sizeof(buffer));
    ssize_t recv_len = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
    if (recv_len < 0) {
        perror("recv");
        close(client_fd);
        exit(EXIT_FAILURE);
    } else if (recv_len == 0) {
        printf("服务器已关闭连接\n");
    } else {
        buffer[recv_len] = '\0';
        printf("收到 %zd 字节: %s\n", recv_len, buffer);
    }
    
    // 6. 关闭连接
    close(client_fd);
    printf("连接已关闭\n");
    return 0;
}

编译与运行

将上述代码保存为tcp_client.c,使用gcc编译:


gcc -o tcp_client tcp_client.c

运行客户端前需要有一个TCP服务器在监听。可以使用Linux自带的nc命令快速启动一个测试服务器:


# 终端1:启动监听服务器
nc -l 8888

# 终端2:运行客户端(默认连接127.0.0.1:8888)
./tcp_client

也可以指定其他服务器地址和端口:


./tcp_client 192.168.1.100 9000

验证方法与判定标准

1. 连接成功验证:客户端输出已连接到 127.0.0.1:8888,同时nc终端显示有连接接入。

2. 数据发送验证:客户端输出已发送 18 字节: Hello, TCP Server!,nc终端应显示收到的原始字符串Hello, TCP Server!。

3. 数据接收验证:在nc终端输入任意回复内容(如Hello, Client!)并回车,客户端应输出收到 N 字节: Hello, Client!。

4. 关闭验证:客户端输出连接已关闭,nc终端显示连接断开。

5. 异常路径验证:在不启动服务器的情况下运行客户端,应输出connect: Connection refused错误信息并退出。

TCP客户端 TCP服务器 (nc -l 8888) socket() 创建套接字 connect() 发起连接 send() 发送数据 recv() 接收响应 close() 关闭连接 监听 8888 端口 accept() 接受连接 recv() 接收数据 send() 发送响应 close() 关闭连接 ① SYN ② SYN+ACK ③ "Hello, TCP Server!" ④ 服务器回复数据 ⑤ FIN ①~② TCP三次握手建立连接 ③ 客户端发送数据 ④ 服务器回复 ⑤ 四次挥手关闭连接 验证命令:nc -l 8888(服务器) | ./tcp_client(客户端)

关键函数说明

connect()用于将socket与服务器地址绑定并建立TCP连接,阻塞直到握手完成或超时失败。send()在已连接的socket上发送数据,返回实际发送的字节数。recv()接收对端数据,返回接收字节数,返回0表示对端已关闭连接。三个函数失败时均返回-1,需检查errno获取具体错误原因。本案例代码中已包含完整的错误处理路径,确保每个系统调用失败时都能输出明确提示并安全退出。