一、先理解一个核心概念
在 Linux 中:
Socket 本质上是一个文件描述符(fd)
所以它和文件一样:
1、用一个整数表示
2、用 read/write 思想操作
3、最后要 close()
二、头文件分别是干什么的
cpp
#include <sys/socket.h>
提供:
socket()
bind()
listen()
accept()
send()
recv()
cpp
#include <netinet/in.h>
提供:
sockaddr_in(IPv4地址结构)
端口、IP相关定义
cpp
#include <arpa/inet.h>
提供:
IP字符串转换函数:
inet_pton()(字符串 → 二进制)
inet_ntop()(二进制 → 字符串)
字节序转换:
htons()、htonl()
ntohs()、ntohl()
cpp
#include <unistd.h>
提供:
close()
三、socket() ------ 创建通信端点
函数:
cpp
int socket(int domain, int type, int protocol);
参数解释:
1. domain(地址族)常用AF_INET

2. type(协议类型)

TCP 必须用:
SOCK_STREAM
3. protocol
一般写:0
系统自动选择 TCP 或 UDP。
返回值:
fd >= 0 成功
fd == -1 失败
四、bind() ------ 给socket分配地址
cpp
int bind(int fd, const sockaddr *addr, socklen_t len);
作用:
告诉系统:这个socket用哪个IP和端口
地址结构(IPv4)
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

为什么要 htons?
因为:网络统一使用 大端序
五、listen() ------ 进入监听状态
cpp
int listen(int fd, int backlog);
作用:
告诉系统:我这个socket是服务器,要等待客户端连接
参数:
backlog = 等待队列长度
例如:
listen(fd, 5);
最多排队5个未处理连接。
六、accept() ------ 接受连接(关键)
cpp
int accept(int fd, sockaddr *client, socklen_t *len);
作用:
从等待队列中取出一个客户端连接
重要理解:
socket() 创建的是 监听socket
accept() 返回的是 新的连接socket
所以:
listen_fd ------ 用来监听
conn_fd ------ 用来通信
七、connect() ------ 客户端发起连接
cpp
int connect(int fd, sockaddr *server, len);
作用:
向服务器发起TCP三次握手
成功后:
fd 就变成一个已连接socket
八、send() ------ 发送数据
cpp
ssize_t send(int fd, const void *buf, size_t len, int flags);

返回值:
大于0 实际发送字节数
0 对方关闭
-1 错误
注意:
TCP 可能 没有一次发送完,要循环发送。
九、recv() ------ 接收数据
cpp
ssize_t recv(int fd, void *buf, size_t len, int flags);

TCP 是字节流 :
一次recv不一定是完整消息
十、close() ------ 关闭socket
cpp
close(fd);
作用:
1、释放资源
2、发送 FIN,关闭TCP连接
十一、完整流程图(非常重要)
服务器
socket()
↓
bind()
↓
listen()
↓
accept()
↓
send()/recv()
↓
close()
客户端
socket()
↓
connect()
↓
send()/recv()
↓
close()
十二、一个底层理解(系统角度)
当客户端连接时:
客户端 socket
⇅
TCP三次握手
⇅
服务器 accept() 返回 conn_fd
系统内核维护:
1、发送缓冲区
2、接收缓冲区
3、重传机制
4、拥塞控制
应用层只负责:
send / recv