【Linux】 Linux网络编程入门:Soket编程详解

【Linux】Linux 网络编程入门:Socket 编程详解

Socket 是 Linux(以及几乎所有类 Unix 系统)网络编程的基石。
掌握 Socket 编程,是理解网络通信、服务器开发、分布式系统、微服务通信的基础。

本文从零开始,逐步带你理解 Socket 的本质 → 基本 API → 完整 TCP 服务器/客户端 → 常见模型,适合初学者快速上手,也适合有一定基础的人查漏补缺。

1. Socket 是什么?(最核心的概念)

一句话总结:

Socket 是操作系统提供的一种抽象,用于在不同主机(或同一主机不同进程)之间进行双向通信。

它本质上是内核中一段通信端点的描述,包含:

  • 协议族(IPv4 / IPv6 / Unix Domain)
  • 传输层协议(TCP / UDP)
  • IP 地址
  • 端口号

在 Linux 中,Socket 是一个文件描述符(fd),可以用 read/write/close 等系统调用操作。

2. Socket 编程核心 API 一览表

分类函数原型作用常见参数说明返回值含义
创建socket(int domain, int type, int protocol)创建 socketdomain: AF_INET / AF_INET6 / AF_UNIX≥0:文件描述符,-1:失败
绑定bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)给 socket 绑定 IP + 端口0 成功,-1 失败
监听listen(int sockfd, int backlog)设置被动监听(服务器)backlog:半连接队列长度建议值0 成功,-1 失败
接受连接accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)接受客户端连接,返回新连接 fdaddr 用于返回客户端地址≥0:新连接 fd,-1:失败
连接connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)客户端主动发起连接0 成功,-1 失败
发送数据send(int sockfd, const void *buf, size_t len, int flags)发送数据flags:常用 0 / MSG_DONTWAIT / MSG_NOSIGNAL>0:发送字节数,0:连接关闭,-1:错误
接收数据recv(int sockfd, void *buf, size_t len, int flags)接收数据>0:接收字节数,0:对方关闭,-1:错误
关闭close(int sockfd)关闭 socket0 成功,-1 失败

3. TCP 服务器完整示例(最经典的写法)

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

#define PORT 8888
#define BACKLOG 128
#define BUF_SIZE 1024

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

    // 1. 创建 socket
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 2. 地址重用(避免 TIME_WAIT 导致 bind 失败)
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 3. 绑定地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;       // 监听所有网卡
    server_addr.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 4. 开始监听
    if (listen(server_fd, BACKLOG) == -1) {
        perror("listen failed");
        exit(EXIT_FAILURE);
    }

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

    while (1) {
        // 5. 接受连接(阻塞式)
        client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
        if (client_fd == -1) {
            perror("accept failed");
            continue;
        }

        char client_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
        printf("New connection from %s:%d\n", client_ip, ntohs(client_addr.sin_port));

        // 6. 读写数据
        ssize_t n = read(client_fd, buffer, BUF_SIZE - 1);
        if (n > 0) {
            buffer[n] = '\0';
            printf("Received: %s\n", buffer);

            // 回显
            write(client_fd, "Server received: ", 17);
            write(client_fd, buffer, n);
        }

        // 7. 关闭客户端连接
        close(client_fd);
    }

    close(server_fd);
    return 0;
}

4. TCP 客户端完整示例

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

#define SERVER_IP "127.0.0.1"
#define PORT 8888
#define BUF_SIZE 1024

int main() {
    int sock;
    struct sockaddr_in serv_addr;
    char buffer[BUF_SIZE];

    // 1. 创建 socket
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 2. 设置服务器地址
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
        perror("Invalid address");
        exit(EXIT_FAILURE);
    }

    // 3. 连接服务器
    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
        perror("connect failed");
        exit(EXIT_FAILURE);
    }

    printf("Connected to server %s:%d\n", SERVER_IP, PORT);

    // 4. 发送数据
    const char *message = "Hello from client!";
    send(sock, message, strlen(message), 0);

    // 5. 接收响应
    ssize_t n = read(sock, buffer, BUF_SIZE - 1);
    if (n > 0) {
        buffer[n] = '\0';
        printf("Server reply: %s\n", buffer);
    }

    close(sock);
    return 0;
}

5. 常见 Socket 编程模型对比(选择题/面试常考)

模型并发能力代码复杂度资源占用适用场景备注
多进程(fork)连接数少、每个连接处理时间长经典 Apache prefork 模式
多线程中高连接数中等pthread / C++ std::thread
IO 多路复用高并发、短连接select / poll / epoll(Linux 首选)
事件驱动极高高并发服务器libevent / libev / libuv / epoll
协程中~高极低高并发、业务逻辑复杂libco / boost.coroutine / C++20 coroutine

6. 学习进阶路线建议(Linux 网络编程)

阶段重点内容推荐练习
入门socket / bind / listen / accept / connect / read / write / close实现 echo 服务器/客户端
中级setsockopt / getsockopt / SO_REUSEADDR / SO_KEEPALIVE / TCP_NODELAY处理 TIME_WAIT、粘包、半包
进阶select / poll / epoll / epoll ET / LT 模式实现高并发 echo server
高级非阻塞 IO + 状态机 / Reactor / Proactor / 协程网络库实现简易 Redis 服务器 / Web 服务器
实战muduo / libevent / nginx / redis 网络模块阅读理解真实项目中的网络模型

7. 小结:一句话记住 Socket 编程本质

“Socket 编程 = 创建通信端点 + 绑定地址(服务器) + 建立连接 + 数据收发 + 关闭连接”

如果你现在想继续深入某个具体方向,可以直接告诉我:

  • epoll 的 ET/LT 模式区别与代码示例
  • 如何处理 TCP 粘包/半包(长度前缀、定界符、协议)
  • 实现一个简单的 Reactor 模型服务器
  • 多进程、多线程、epoll 三种并发模型完整对比代码
  • setsockopt 常用选项详解(SO_REUSEPORT、TCP_NODELAY 等)

随时说,我可以继续手把手带你写代码或深入讲解。

文章已创建 4580

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部