Linux Socket 网络编程学习笔记

一、Socket 是什么

Socket 是一套网络编程接口,把网络通信抽象成文件描述符,可以像读写文件一样收发网络数据。

  • AF_INET:IPv4 协议
  • SOCK_STREAM:TCP 流式套接字
  • SOCK_DGRAM:UDP 数据报套接字

二、通用基础 API

1. socket 创建套接字

c 复制代码
int socket(int domain, int type, int protocol);
  • domain:协议族,AF_INET 代表 IPv4
  • type:套接字类型,SOCK_STREAM(TCP) / SOCK_DGRAM(UDP)
  • protocol:协议编号,填 0 自动匹配
  • 返回值:成功返回套接字文件描述符;失败返回 -1

TCP:socket(AF_INET, SOCK_STREAM, 0)

UDP:socket(AF_INET, SOCK_DGRAM, 0)

2. bind 绑定 IP 与端口

c 复制代码
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd:socket 返回的文件描述符
  • addr:地址结构体,实际传 struct sockaddr_in,需要强制转换 (struct sockaddr *)&var
  • addrlen:地址结构体大小 sizeof(struct sockaddr_in)
  • 返回:成功 0,失败 -1

✅ 服务端必须 bind;客户端 TCP 一般不需要 bind,内核自动分配临时端口。

INADDR_ANY:监听本机所有网卡,必须套 htonl(INADDR_ANY)

3. connect 发起连接(TCP 客户端)

c 复制代码
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 功能:向服务端发起 TCP 连接请求,触发三次握手。
  • sockfd:客户端 socket fd
  • addr:目标服务端地址(IP、端口)
  • addrlen:地址结构体大小
  • 返回:成功返回 0;失败返回 -1

UDP 也可以调用 connect,但不会产生网络报文,仅仅在内核记录对端地址,后续可以直接 send/recv,不用 sendto/recvfrom。

4. send 发送数据(TCP)

c 复制代码
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • sockfd:通信文件描述符(accept 返回 cfd)
  • buf:待发送数据缓冲区首地址
  • len:想要发送的数据字节长度
  • flags:标志位,日常填 0
  • 返回值:成功返回实际发送字节数;失败 -1

send 不保证全部发完;返回值小于 len 属于正常现象,业务代码建议循环发送。

5. recv 接收数据(TCP)

c 复制代码
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • buf:接收缓冲区
  • len:缓冲区最大可接收字节
  • flags:默认 0
  • 返回:
    *

    0:收到的字节数量

    • 0:对端关闭连接(TCP 断开)
    • -1:出错

⚠️ 考试高频考点:recv 返回 0 代表对方关闭 socket,不是没有数据。

6. listen 设置监听(TCP 服务端)

c 复制代码
int listen(int sockfd, int backlog);
  • sockfd:已经 bind 过的 socket
  • backlog:未处理连接请求队列的最大长度,并发连接排队上限。
  • 返回:成功 0,失败 -1

socket→bind→listen 顺序不能颠倒。listen 把套接字从主动套接字变成被动监听套接字,等待别人来连接。

7. accept 接受客户端连接(TCP 服务端)

c 复制代码
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockfd:listen 之后的监听套接字
  • addr:输出参数,保存客户端的 IP 端口信息,不需要可以填 NULL
  • addrlen:输入输出参数,传入结构体大小;调用结束返回真实客户端地址长度
  • 返回值:成功返回新的通信文件描述符 cfd;失败 -1

重点区分两个 fd:

  • sockfd:监听套接字,一直用来等待新连接
  • cfd:accept 返回,专门和这一个客户端收发数据

三、UDP 专用接口 sendto / recvfrom

UDP 是无连接,没有 connect 建立连接,每次收发都要携带对方地址。

c 复制代码
// 发送
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

// 接收
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);
  • sendto:发送的时候带上目标 IP 端口
  • recvfrom:接收的时候,同时拿到发送方的地址信息
  • UDP 不存在返回 0 代表断开;UDP 无连接,对方退出你收不到任何通知。

如果你 UDP 调用 connect,之后可以直接使用 send/recv,不再需要 sendto/recvfrom。

四、地址结构体 sockaddr_in

c 复制代码
struct sockaddr_in {
    sa_family_t    sin_family;   // 协议族 AF_INET
    in_port_t      sin_port;     // 端口,**网络字节序 htons()**
    struct in_addr sin_addr;     // IPv4 地址结构体
    char           sin_zero[8];  // 占位填充,置 0
};

struct in_addr {
    in_addr_t s_addr; // 32 位 IP,网络大端
};

赋值示例

c 复制代码
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(8888);
ser.sin_addr.s_addr = inet_addr("192.168.1.100");

易错:inet_addr() 返回已经是网络大端,不要再套 htonl!

五、TCP 与 UDP 对比

项目 TCP UDP
连接 面向连接,三次握手建立连接 无连接,直接发数据包
可靠性 可靠,丢包重传、有序 不可靠,不保证到达、不保证顺序
数据边界 字节流,没有消息边界 数据报,保留消息边界
开销
典型 API socket-bind-listen-accept-connect-send-recv socket-bind-sendto-recvfrom

UDP 头部简要

UDP 头部 4 个字段:源端口、目的端口、数据包长度、校验和,之后直接携带业务数据。特点:机制简单,资源开销小,不可靠。

TCP 三次握手简要

  1. 客户端发送 SYN 报文,请求建立连接
  2. 服务端返回 SYN+ACK 报文
  3. 客户端回复 ACK,连接正式建立,之后传输业务数据

六、TCP 完整调用流程

服务端

text 复制代码
socket() → bind() → listen() → accept()阻塞等待连接 → send()/recv() → close()

客户端

text 复制代码
socket() → connect() → send()/recv() → close()

七、高频坑点总结

  1. TCP recv 返回 0 = 对方关闭连接;UDP recvfrom 永远不会返回 0 代表断开。
  2. sin_port 端口必须 htons() 转为网络字节序。
  3. accept 第二个参数是输出参数,用来拿到客户端 IP 端口,不需要可以填 NULL。
  4. listen 只是开启监听,不会接收连接;真正获取连接靠 accept。
  5. send 返回值不等于你传入 len,不一定全部发送完成,需要循环处理。
  6. UDP 无连接,对方进程退出,本程序不会收到任何通知。
相关推荐
一隅论数智1 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
未济1 天前
linux 配置环境变量
linux
傲世仙尊1 天前
目录即文件-Ext文件系统收尾篇
linux·c语言
XiHongShi20161 天前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
虎头金猫1 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
爱吃苹果的日记本1 天前
离散数学第六课
学习·离散数学
_艾伦 耶格尔.1 天前
进程间通信
linux
AI职业加油站1 天前
AI智能体应用工程师证书:政策红利下的职业新风口
大数据·运维·人工智能·学习·职场发展
旖旎夜光1 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控