Linux TCP 编程详解与实例

一、引言

在网络编程的领域中,TCP(Transmission Control Protocol)协议因其可靠的数据传输特性而被广泛应用。在 Linux 环境下,使用 C 或 C++ 进行 TCP 编程可以实现各种强大的网络应用。本文将深入探讨 Linux TCP 编程的各个方面,包括 API 接口的详细说明、TCP Server 和 TCP Client 的实例代码,以及完整的测试流程。

二、TCP 编程的 API 接口说明

(一)socket() 函数

plaintext 复制代码
int socket(int domain, int type, int protocol);
  • 功能:创建一个套接字。
  • 参数:
    • domain:指定协议族,常见的有 AF_INET(IPv4 网络协议)和 AF_INET6(IPv6 网络协议)。
    • type:套接字类型,对于 TCP 通常使用 SOCK_STREAM
    • protocol:指定使用的具体协议,通常设置为 0 以使用默认的 TCP 协议。
  • 返回值:成功时返回一个非负的套接字描述符,失败时返回 -1。

(二)bind() 函数

plaintext 复制代码
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 功能:将套接字与本地地址和端口绑定。
  • 参数:
    • sockfd:由 socket() 函数返回的套接字描述符。
    • addr:指向包含地址和端口信息的结构体,如 struct sockaddr_in(IPv4)或 struct sockaddr_in6(IPv6)。
    • addrlenaddr 结构体的长度。
  • 返回值:成功返回 0,失败返回 -1。

(三)listen() 函数

plaintext 复制代码
int listen(int sockfd, int backlog);
  • 功能:将套接字设置为监听状态,准备接受客户端的连接请求。
  • 参数:
    • sockfd:已绑定的套接字描述符。
    • backlog:指定等待连接队列的最大长度。
  • 返回值:成功返回 0,失败返回 -1。

(四)accept() 函数

plaintext 复制代码
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 功能:从已完成连接队列中取出一个连接,并创建一个新的套接字与客户端进行通信。
  • 参数:
    • sockfd:监听套接字描述符。
    • addr:用于存储客户端的地址信息。
    • addrlen:用于指定 addr 结构体的长度。
  • 返回值:成功返回一个新的套接字描述符用于与客户端通信,失败返回 -1。

(五)connect() 函数(客户端使用)

plaintext 复制代码
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 功能:客户端向服务器发起连接请求。
  • 参数:
    • sockfd:套接字描述符。
    • addr:指向服务器的地址结构体。
    • addrlenaddr 结构体的长度。
  • 返回值:成功返回 0,失败返回 -1。

(六)send()recv() 函数

send() 函数
plaintext 复制代码
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • 功能:用于发送数据。
  • 参数:
    • sockfd:套接字描述符。
    • buf:指向要发送数据的缓冲区。
    • len:要发送的数据长度。
    • flags:控制选项,通常设置为 0。
  • 返回值:成功返回实际发送的字节数,失败返回 -1。
recv() 函数
plaintext 复制代码
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • 功能:用于接收数据。
  • 参数:
    • sockfd:套接字描述符。
    • buf:用于存储接收数据的缓冲区。
    • len:缓冲区的长度。
    • flags:控制选项,通常设置为 0。
  • 返回值:成功返回实际接收的字节数,失败返回 -1。

(七)close() 函数

plaintext 复制代码
int close(int fd);
  • 功能:关闭套接字。
  • 参数:要关闭的套接字描述符。

三、TCP Server 实例代码(支持多线程和回显)

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

#define MAX_CONNECTIONS 10  // 最大连接数量

pthread_mutex_t connectionCountMutex;
int connectionCount = 0;  // 记录当前连接数

void *handle_client(void *arg) {
    int client_fd = *((int *)arg);
    char buffer[1024];
    int bytes_read;

    while ((bytes_read = recv(client_fd, buffer, sizeof(buffer), 0)) > 0) {
        // 回显接收到的数据
        send(client_fd, buffer, bytes_read, 0);
    }

    // 处理客户端断开连接
    pthread_mutex_lock(&connectionCountMutex);
    connectionCount--;
    printf("Client disconnected. Current connections: %d\n", connectionCount);
    pthread_mutex_unlock(&connectionCountMutex);

    close(client_fd);
    pthread_exit(NULL);
}

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    int port = 8080;  // 服务器监听的端口

    // 创建套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 初始化地址结构体
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(port);

    // 绑定套接字到本地地址和端口
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }

    // 开始监听
    if (listen(server_fd, MAX_CONNECTIONS) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    printf("Server is listening on port %d...\n", port);

    pthread_mutex_init(&connectionCountMutex, NULL);

    while (1) {
        // 接受客户端连接
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
            perror("Accept failed");
            exit(EXIT_FAILURE);
        }

        printf("New connection accepted. Current connections: %d\n", ++connectionCount);

        // 检查连接数是否达到上限
        if (connectionCount > MAX_CONNECTIONS) {
            printf("Reached maximum connections. Closing new connection.\n");
            close(new_socket);
            connectionCount--;
            continue;
        }

        pthread_t thread;
        if (pthread_create(&thread, NULL, handle_client, &new_socket)!= 0) {
            perror("Thread creation failed");
            close(new_socket);
            connectionCount--;
            continue;
        }
        // 分离线程,使其资源在结束时自动回收
        pthread_detach(thread);
    }

    // 清理
    pthread_mutex_destroy(&connectionCountMutex);
    // 关闭服务器套接字
    close(server_fd);

    return 0;
}

四、TCP Client 实例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char *argv[]) {
    if (argc!= 3) {
        printf("Usage: %s <server_ip> <port>\n", argv[0]);
        return 1;
    }

    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[1024] = {0};

    int port = atoi(argv[2]);  // 将命令行参数转换为端口号
    char *server_ip = argv[1];  // 服务器 IP 地址

    // 创建套接字
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);

    // 将服务器 IP 地址从字符串转换为网络地址格式
    if (inet_pton(AF_INET, server_ip, &serv_addr.sin_addr) <= 0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }

    printf("Connected to server\n");

    while (1) {
        printf("Enter message: ");
        fgets(buffer, sizeof(buffer), stdin);

        // 发送数据
        send(sock, buffer, strlen(buffer), 0);

        // 接收服务器响应
        int valread = recv(sock, buffer, 1024, 0);
        if (valread <= 0) {
            printf("Server disconnected\n");
            break;
        }

        printf("Received: %s", buffer);
    }

    // 关闭套接字
    close(sock);

    return 0;
}

五、测试验证

使用 GCC 编译器编译服务器程序:

plaintext 复制代码
gcc server.c -o server -lpthread
plaintext 复制代码
./server

同样使用 GCC 编译器编译客户端程序:

plaintext 复制代码
gcc client.c -o client
  1. 运行客户端程序,并传入服务器的 IP 地址和端口作为参数,例如:
plaintext 复制代码
./client 127.0.0.1 8080
相关推荐
眠修2 分钟前
Kuberrnetes 服务发布
linux·运维·服务器
即将头秃的程序媛3 小时前
centos 7.9安装tomcat,并实现开机自启
linux·运维·centos
fangeqin3 小时前
ubuntu源码安装python3.13遇到Could not build the ssl module!解决方法
linux·python·ubuntu·openssl
爱奥尼欧4 小时前
【Linux 系统】基础IO——Linux中对文件的理解
linux·服务器·microsoft
超喜欢下雨天5 小时前
服务器安装 ros2时遇到底层库依赖冲突的问题
linux·运维·服务器·ros2
00后程序员张5 小时前
免Mac上架实战:全平台iOS App上架流程的工具协作经验
websocket·网络协议·tcp/ip·http·网络安全·https·udp
tan77º6 小时前
【Linux网络编程】网络基础
linux·服务器·网络
笑衬人心。6 小时前
Ubuntu 22.04 + MySQL 8 无密码登录问题与 root 密码重置指南
linux·mysql·ubuntu
chanalbert8 小时前
CentOS系统新手指导手册
linux·运维·centos
星宸追风8 小时前
Ubuntu更换Home目录所在硬盘的过程
linux·运维·ubuntu