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 分钟前
尚硅谷课程【笔记】——大数据之Linux【二】
大数据·linux
爱学习的大白菜3 分钟前
查看二进制程序内的 .interp 段
linux
久绊A31 分钟前
网络基础知识与配置
linux·服务器
前端早间课1 小时前
无法使用ip连接服务器的mysql
服务器·tcp/ip·mysql
qq_4908246141 小时前
NetCore项目实现IP黑名单功能
网络·网络协议·tcp/ip
云梦谭1 小时前
ubuntu server环境下使用mitmproxy代理
linux·mitmproxy
yqcoder1 小时前
centos 和 ubuntu 区别
linux·ubuntu·centos
众乐乐_200810 小时前
说一下 Tcp 粘包是怎么产生的?
网络·网络协议·tcp/ip
Hi-Dison11 小时前
VMware 虚拟机中 Ubuntu 20 网络不通问题解决总结
linux·网络·ubuntu
小镇敲码人12 小时前
【Linux网络编程】之守护进程
linux·运维·网络