当前位置: 首页 > article >正文

Linux TCP 编程详解与实例

一、引言

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

二、TCP 编程的 API 接口说明

(一)socket() 函数

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

(二)bind() 函数

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() 函数

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

(四)accept() 函数

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

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

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

(六)send()recv() 函数

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

(七)close() 函数

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

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

#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 实例代码

#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 编译器编译服务器程序:

gcc server.c -o server -lpthread
./server

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

gcc client.c -o client
  1. 运行客户端程序,并传入服务器的 IP 地址和端口作为参数,例如:
./client 127.0.0.1 8080

在这里插入图片描述


http://www.kler.cn/a/540177.html

相关文章:

  • 如何使用DeepSeek帮助自己的工作?
  • DFS+回溯+剪枝(深度优先搜索)——搜索算法
  • PostgreSQL 18新特性之DML语句RETURNING增强
  • 在请求时打印出实际代理的目标地址
  • 【vscode+latex】实现overleaf本地高效编译
  • 多光谱成像技术在华为Mate70系列的应用
  • json转typescript在线工具
  • webpack配置之---output.chunkFormat
  • [权限提升] Linux 提权 维持 — 系统错误配置提权 - 明文 ROOT 密码提权
  • Websocket从原理到实战
  • 大模型Prompt 提示词攻击,大语言模型安全的潜在威胁
  • 深入解析:Java中如何使用Redis存储购物车信息?
  • Deepseek使用途径以及Prompt 提示词编写原理
  • 【漫话机器学习系列】087.常见的神经网络最优化算法(Common Optimizers Of Neural Nets)
  • Kokoro 开源文本转语音引擎上线!多语言支持,无需联网,浏览器内极速运行
  • java项目之美妆产品进销存管理系统的设计与开发源码(ssm+mysql)
  • nvm:node 版本管理器
  • java数据结构_二叉树_5.4
  • 小白系列:数据库基础知识解析
  • 等待时间问题(C++)
  • 蓝桥杯填空题汇总
  • 【从零开始的LeetCode-算法】63. 不同路径 II
  • bladeX微服务框架如何修改nacos分组
  • 避开Arrays.asList使用的坑
  • SAP ABAP调用DeepSeek API大模型接口
  • git实现回退