跳转到内容

C 语言 tcp 编程

来自代码酷


C语言TCP编程是网络编程中的核心内容,它基于传输控制协议(TCP)实现可靠的双向通信。本文将从基础概念到实际应用,逐步讲解如何使用C语言进行TCP编程。

概述[编辑 | 编辑源代码]

TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层协议。在C语言中,TCP编程主要通过套接字(socket)接口实现,涉及以下关键步骤:

  • 创建套接字
  • 绑定地址和端口
  • 监听连接(服务端)
  • 发起连接(客户端)
  • 数据传输
  • 关闭连接

基础概念[编辑 | 编辑源代码]

TCP协议特性[编辑 | 编辑源代码]

  • 可靠性:通过确认机制、重传机制和校验和保证数据正确传输。
  • 面向连接:通信前需建立连接(三次握手),结束时需断开连接(四次挥手)。
  • 全双工通信:双方可同时发送和接收数据。

套接字类型[编辑 | 编辑源代码]

C语言中常用的TCP套接字类型为:

int sockfd = socket(AF_INET, SOCK_STREAM, 0); // AF_INET表示IPv4,SOCK_STREAM表示TCP

编程模型[编辑 | 编辑源代码]

服务端流程[编辑 | 编辑源代码]

1. 创建套接字 2. 绑定地址(bind) 3. 监听连接(listen) 4. 接受连接(accept) 5. 数据交换(send/recv) 6. 关闭连接

客户端流程[编辑 | 编辑源代码]

1. 创建套接字 2. 连接服务器(connect) 3. 数据交换(send/recv) 4. 关闭连接

sequenceDiagram participant Client participant Server Client->>Server: connect() Server->>Client: accept() loop 数据传输 Client->>Server: send() Server->>Client: recv() end Client->>Server: close()

代码实现[编辑 | 编辑源代码]

基本TCP服务端[编辑 | 编辑源代码]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};
    
    // 1. 创建套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    
    // 设置套接字选项
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    
    // 2. 绑定地址
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    
    // 3. 监听连接
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    
    printf("Server listening on port %d...\n", PORT);
    
    // 4. 接受连接
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }
    
    // 5. 数据交换
    read(new_socket, buffer, BUFFER_SIZE);
    printf("Client says: %s\n", buffer);
    send(new_socket, "Hello from server", strlen("Hello from server"), 0);
    
    // 6. 关闭连接
    close(new_socket);
    close(server_fd);
    return 0;
}

基本TCP客户端[编辑 | 编辑源代码]

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

#define PORT 8080

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    char buffer[1024] = {0};
    
    // 1. 创建套接字
    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, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }
    
    // 2. 连接服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }
    
    // 3. 数据交换
    send(sock, hello, strlen(hello), 0);
    printf("Hello message sent\n");
    read(sock, buffer, 1024);
    printf("Server says: %s\n", buffer);
    
    // 4. 关闭连接
    close(sock);
    return 0;
}

关键函数详解[编辑 | 编辑源代码]

socket()[编辑 | 编辑源代码]

创建通信端点,返回文件描述符。

  • 参数:
    • domain:地址族(如AF_INET)
    • type:套接字类型(如SOCK_STREAM)
    • protocol:通常为0(自动选择)

bind()[编辑 | 编辑源代码]

将套接字与特定IP地址和端口号绑定。

  • 参数:
    • sockfd:套接字描述符
    • addr:指向包含地址信息的结构体
    • addrlen:地址结构体长度

listen()[编辑 | 编辑源代码]

使套接字进入被动监听状态。

  • 参数:
    • sockfd:套接字描述符
    • backlog:等待连接队列的最大长度

accept()[编辑 | 编辑源代码]

接受连接请求,返回新套接字描述符。

  • 参数:
    • sockfd:监听套接字描述符
    • addr:客户端地址信息
    • addrlen:地址结构体长度

高级主题[编辑 | 编辑源代码]

多客户端处理[编辑 | 编辑源代码]

使用多进程或多线程处理多个客户端连接:

// 在accept循环中
while (1) {
    int new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
    if (fork() == 0) {  // 子进程处理连接
        close(server_fd);
        // 处理客户端请求
        handle_client(new_socket);
        exit(0);
    }
    close(new_socket);
}

非阻塞I/O[编辑 | 编辑源代码]

使用fcntl设置非阻塞模式:

int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

实际应用案例[编辑 | 编辑源代码]

简单聊天程序[编辑 | 编辑源代码]

实现一个基本的客户端-服务器聊天系统: 1. 服务器启动并等待连接 2. 客户端连接后可以发送消息 3. 服务器接收并回复消息 4. 双方可以持续通信直到输入"exit"

文件传输[编辑 | 编辑源代码]

通过TCP实现文件传输: 1. 客户端发送文件名 2. 服务器确认文件存在 3. 分块传输文件数据 4. 校验文件完整性

常见问题[编辑 | 编辑源代码]

地址已在使用[编辑 | 编辑源代码]

解决方法:

  • 设置SO_REUSEADDR套接字选项
  • 等待TCP TIME_WAIT状态结束(通常2MSL时间)

连接重置[编辑 | 编辑源代码]

可能原因:

  • 对端意外关闭连接
  • 网络问题
  • 协议不匹配

性能优化[编辑 | 编辑源代码]

  • 使用select/poll/epoll处理多连接
  • 设置合适的缓冲区大小
  • 启用TCP_NODELAY选项减少延迟
  • 使用SO_KEEPALIVE检测连接状态

安全考虑[编辑 | 编辑源代码]

  • 验证输入数据防止缓冲区溢出
  • 使用SSL/TLS加密敏感数据
  • 限制并发连接数防止DoS攻击
  • 正确处理错误和异常情况

总结[编辑 | 编辑源代码]

C语言TCP编程是构建网络应用的基础,通过套接字API可以实现可靠的通信。掌握TCP编程需要理解协议特性、熟悉相关系统调用,并注意性能和安全性问题。实际开发中,通常会结合多线程、I/O多路复用等技术构建更复杂的网络应用。