// 2. 绑定地址和端口
    struct sockaddr_in server_addr{};
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    if (bind(listen_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind failed");
        return 1;
    }

这是 TCP 服务器编程中最关键的一步:bind() 绑定

整段代码的核心作用

给服务器的套接字,“挂牌营业”:指定 IP 地址 + 端口号。

  • 没有这一步,客户端根本找不到你的服务器
  • 就像开店必须挂出门牌(IP)和门牌号(端口)。

逐行拆解讲解

1. 创建地址结构体

struct sockaddr_in server_addr{};

这是什么?

  • sockaddr_in专门存放 IPv4 地址 + 端口的结构体
  • 作用:告诉操作系统,服务器要监听哪个 IP、哪个端口

结构体内部长这样(你只需要理解):

struct sockaddr_in {
    short sin_family;     // 地址族(AF_INET)
    unsigned short sin_port;  // 端口号
    struct in_addr sin_addr; // IP 地址
    char sin_zero[8];    // 填充位(不用管)
};

2. 设置地址族

server_addr.sin_family = AF_INET;
  • AF_INET = 使用 IPv4 协议
  • 和你之前 socket() 第一个参数必须一致

3. 设置监听 IP 地址

server_addr.sin_addr.s_addr = INADDR_ANY;

超级重点:INADDR_ANY 是什么?

意思:监听本机所有网卡的 IP

  • 你的电脑可能有:
    • 局域网 IP:192.168.1.100
    • 本机回环:127.0.0.1
    • 外网 IP:…

INADDR_ANY = 0.0.0.0
表示:
不管哪个 IP 发来的请求,我都接收!

这是服务器最常用写法,代表“接收所有网络的连接”。


4. 设置端口号(必须用 htons!)

server_addr.sin_port = htons(PORT);

为什么必须用 htons()

这是网络编程最经典、最容易错的地方!

  • 主机字节序(小端):电脑自己用的
  • 网络字节序(大端):网络传输必须用的

两者存储顺序相反,不转换 → 端口错乱,客户端永远连不上!

htons 含义

  • h:host 主机
  • to:to 转成
  • n:network 网络
  • s:short 短整型(端口是16位)

htons(PORT) = 把端口转为网络能识别的格式


最核心函数:bind() 绑定

bind(listen_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

作用

把“IP + 端口” 绑定到套接字上

三个参数解释

1. listen_sockfd

  • 你创建的监听套接字
  • 要给这个套接字挂牌

2. (struct sockaddr*)&server_addr

  • 传入你填好的 IP + 端口结构体
  • 必须强转成 struct sockaddr*(历史原因,固定写法)

3. sizeof(server_addr)

  • 告诉系统结构体大小

返回值判断

if (bind(...) < 0) {
    perror("bind failed");
    return 1;
}
  • 绑定成功:返回 0
  • 绑定失败:返回 -1
    常见原因:
    • 端口被占用
    • 权限不足(1024以下端口需要管理员)
    • IP 错误

这段代码的完整意义(大白话)

// 我要创建一个服务器地址
struct sockaddr_in server_addr{};

// 使用 IPv4 协议
server_addr.sin_family = AF_INET;

// 监听本机所有 IP(0.0.0.0)
server_addr.sin_addr.s_addr = INADDR_ANY;

// 监听 PORT 端口,并转成网络格式
server_addr.sin_port = htons(PORT);

// 把这个 IP + 端口 绑定到服务器套接字上
bind(listen_sockfd, ...);

最终效果:

服务器现在开始监听:0.0.0.0:8080
任何客户端访问这台机器的 8080 端口,都能找到这个程序!


绑定成功后会发生什么?

执行完 bind 后:

  1. 操作系统内核会注册这个程序占用了某个端口
  2. 发往该端口的网络数据都会转发给这个程序
  3. 下一步就可以调用 listen() 开始等待客户端连接

最经典的错误(90%的人踩过坑)

  1. 没写 htons() → 端口不对,客户端连不上
  2. bind 之前没设置 SO_REUSEADDR → 重启报错 Address in use
  3. 端口被占用 → bind 失败
  4. 结构体没初始化 → 随机值导致绑定失败

总结(超级好记)

  1. struct sockaddr_in:存放 IP + 端口
  2. AF_INET:IPv4
  3. INADDR_ANY:监听所有网卡 IP(0.0.0.0)
  4. htons():端口必须转网络字节序
  5. bind():把 IP + 端口 绑定到套接字
  6. 作用:让客户端能找到服务器

这是服务器必须执行的一步,没有 bind,就没有网络通信!

需要我给你画一张 socket → setsockopt → bind → listen 的完整流程示意图吗?一看就懂。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐