C语言Socket网络编程完全指南

Socket是网络通信的基础,C语言通过socket API实现TCP和UDP编程。本文详细介绍服务端和客户端的开发流程。

Socket基础

Socket是应用程序与网络协议栈之间的接口。常用的socket类型有:

  • TCP (SOCK_STREAM):面向连接、可靠传输
  • UDP (SOCK_DGRAM):无连接、不可靠但高效

TCP服务端

TCP服务端的开发步骤:创建socket、绑定地址、监听、接受连接、数据交换:

#include 
#include 
#include 
#include 
#include 

#define PORT 8080

int main(void) {
    int server_fd, client_fd;
    struct sockaddr_in addr, client_addr;
    char buffer[1024];
    socklen_t client_len = sizeof(client_addr);

    /* 创建socket */
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("socket");
        return 1;
    }

    /* 设置地址复用 */
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    /* 绑定地址 */
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        close(server_fd);
        return 1;
    }

    /* 监听连接 */
    listen(server_fd, 5);
    printf("Server listening on port %d\n", PORT);

    /* 接受连接 */
    client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
    printf("Client connected\n");

    /* 接收数据 */
    ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
    if (n > 0) {
        buffer[n] = '\0';
        printf("Received: %s\n", buffer);

        /* 发送响应 */
        const char *response = "Hello from server";
        send(client_fd, response, strlen(response), 0);
    }

    close(client_fd);
    close(server_fd);
    return 0;
}

TCP客户端

TCP客户端的步骤:创建socket、连接服务器、发送和接收数据:

#include 
#include 
#include 
#include 
#include 

#define SERVER_IP "127.0.0.1"
#define PORT 8080

int main(void) {
    int sock;
    struct sockaddr_in server_addr;
    char buffer[1024];

    /* 创建socket */
    sock = socket(AF_INET, SOCK_STREAM, 0);

    /* 设置服务器地址 */
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    /* 连接服务器 */
    if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect");
        return 1;
    }

    /* 发送数据 */
    const char *message = "Hello server";
    send(sock, message, strlen(message), 0);

    /* 接收响应 */
    ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (n > 0) {
        buffer[n] = '\0';
        printf("Server response: %s\n", buffer);
    }

    close(sock);
    return 0;
}

UDP编程

UDP是无连接的,不需要建立连接过程:

/* UDP服务端 */
int udp_server(void) {
    int sock;
    struct sockaddr_in addr, client_addr;
    char buffer[1024];
    socklen_t len = sizeof(client_addr);

    sock = socket(AF_INET, SOCK_DGRAM, 0);

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(9000);

    bind(sock, (struct sockaddr*)&addr, sizeof(addr));

    /* 接收数据(无需accept) */
    ssize_t n = recvfrom(sock, buffer, sizeof(buffer) - 1, 0,
                          (struct sockaddr*)&client_addr, &len);
    buffer[n] = '\0';
    printf("Received: %s\n", buffer);

    close(sock);
}

非阻塞Socket

使用fcntl将socket设置为非阻塞模式,实现异步IO:

/* 设置非阻塞 */
int set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

/* 使用select或epoll处理多连接 */
/* 详见后续C语言IO多路复用指南 */

网络编程建议

实际开发中推荐使用epoll或kqueue处理高并发,使用缓冲区减少系统调用,注意字节序转换。

总结

  • TCP是面向连接的可靠传输协议
  • UDP是无连接的高效传输协议
  • 服务端需要bind、listen、accept流程
  • 客户端使用connect建立连接
  • 非阻塞IO适合高并发场景