TCP与UDP通信

一、TCP与UDP

对比项 TCP(确保把信号送到) UDP(不管在不在都送出去)
特点 面向连接、可靠传输、流量控制、拥塞控制、全双工 无连接、尽力交付、无重传、无拥塞控制、低开销
优点 可靠性(ACK确认、超时重传)、顺序性 传输效率高(8字节首部)、实时性强
缺点 首部开销大(20字节)、连接管理复杂、延迟高 数据可能丢失、乱序
应用场景 HTTP/HTTPS、文件传输(FTP)、邮件(SMTP/IMAP) 视频流(RTP)、DNS查询、在线游戏、VoIP

TCP首部包含:源端口号;序列号;校验和

目标端口未监听;网络拥塞会导致TCP链接失效

二、TCP通信

2.1 TCP网络编程流程

步骤 服务器流程 函数
1 创建流式套接字 socket()
2 填充服务器的网络信息结构体 struct sockaddr_in
3 将套接字与服务器的网络信息结构体绑定 bind()
4 将套接字设置成被动监听状态 listen()
5 阻塞等待客户端连接 accept()
6 收发数据 recv() / send()
7 关闭套接字 close()

2.2 socket编程

2.2.1 socket函数

项目 内容
头文件 #include <sys/socket.h>
函数原型 int socket(int domain, int type, int protocol);
参数 - domain AF_INET(IPv4)、AF_INET6(IPv6)、AF_PACKET(原始套接字)、AF_UNIX(本地通信)、AF_LOCAL(本地通信)
参数 - type SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、SOCK_RAW(原始套接字)
参数 - protocol 无附加协议填 0(自动选择默认协议)
返回值 成功返回套接字(文件描述符,非负整数),失败返回 -1(并重置错误码)

2.2.2 bind函数

项目 内容
头文件 #include <sys/socket.h>
函数原型 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数 - sockfd 文件描述符(由 socket 函数返回)
参数 - addr 地址结构体指针,指向要绑定给 sockfd 的协议地址(以 IPv4 的 struct sockaddr_in 为例)
参数 - addrlen 地址的长度
返回值 成功返回 0,失败返回 -1(并重置错误码)
cs 复制代码
//addr 结构体
struct sockaddr_in { 
    sa_family_t sin_family; 
    in_port_t sin_port; 
    struct in_addr sin_addr; 
};
//sin_addr 结构体
struct in_addr { 
    uint32_t s_addr;
};

定义结构体后要用memset函数清空结构体

项目 内容
头文件 <string.h>
函数原型 void *memset(void *s, int c, size_t n);
作用 将指针 s 指向的内存区域的前 n 个字节,全部设置为 c 的值
返回值 返回指针 s(即填充后的内存起始地址)

也可以直接 struct sockaddr_in serverInfo = {0};

结构体的初始化

cs 复制代码
    //定义配置服务器地址结构体
    struct sockaddr_in serverInfo;
    //清空结构体
    memset(&serverInfo,0,sizeof(serverInfo));
    //初始化IPV4协议族
    serverInfo.sin_family = AF_INET;
    //初始化端口号 (主机字节序->网络字节序)
    serverInfo.sin_port = htons(8888);
    //初始化IP
    serverInfo.sin_addr.s_addr = inet_addr("192.168.23.100");
 

2.2.3 listen函数

项目 内容
头文件 #include <sys/socket.h>
函数原型 int listen(int sockfd, int backlog);
参数 - sockfd 表示要监听的 socket 套接字
参数 - backlog 表示半连接队列的长度(即 socket 可以排队的最大连接个数)
返回值 成功返回 0,失败返回 -1(并重置错误码)

把socket从主动变成被动监听状态

2.2.4 accept函数(阻塞函数)

项目 内容
头文件 #include <sys/socket.h>
函数原型 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数 - sockfd 表示处于监听状态的 socket
参数 - addr 用于保存客户端地址的结构体指针,如果不关心客户端的信息,可以传 NULL
参数 - addrlen 输入时为 addr 的缓冲区大小,输出时为实际地址长度,如果不关心客户端的信息,可以传 NULL
返回值 成功返回新的 socket 文件描述符(由内核生成,代表着与返回客户端的 TCP 连接,专用于与客户端通信),失败返回 -1(并重置错误码)

2.2.5 connect函数

项目 内容
头文件 #include <sys/socket.h>
函数原型 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数 - sockfd 表示客户端的 socket 套接字
参数 - addr 表示目标服务器的地址结构体
参数 - addrlen 地址结构体的长度
返回值 成功返回 0,失败返回 -1(并重置错误码)

2.2.6 recv函数

项目 内容
头文件 #include <sys/socket.h>
函数原型 ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数 - sockfd 表示客户端的 socket 套接字(connect_fd)
参数 - buf 用于存储接收的数据
参数 - len 数据大小
参数 - flags 控制选项(如 MSG_DONTWAIT 非阻塞,MSG_PEEK 窥视数据,0 阻塞)
返回值 成功返回实际接收数据字节数,0表示客户端断开链接,失败返回 -1(并重置错误码)

recv() 不是从 buf 里收数据 ,而是从内核的接收缓冲区里收数据

2.2.7 send函数

项目 内容
头文件 #include <sys/socket.h>
函数原型 ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数 - sockfd 表示客户端的 socket 套接字
参数 - buf 要发送的数据首地址
参数 - len 数据大小
参数 - flags 控制选项(如 MSG_DONTWAIT 非阻塞,MSG_PEEK 窥视数据,0 阻塞)
返回值 成功返回实际发送数据字节数,失败返回 -1(并重置错误码)

2.2.8 close函数

项目 内容
头文件 #include <unistd.h>
函数原型 int close(int fd);
参数 - fd 表示要关闭的套接字
返回值 成功返回 0,失败返回 -1

2.3 系统内核处理

函数 内核处理
socket() 创建套接字结构体,分配文件描述符
bind() 把IP和端口号绑定到套接字
listen() 创建半连接队列和全连接队列,转为监听状态
accept() 从全连接队列取出已完成连接的客户端,生成新套接字
connect() 发起三次握手,发送SYN包
recv() 从内核接收缓冲区拷贝数据到用户空间
send() 从用户空间拷贝数据到内核发送缓冲区
close() 释放套接字资源,发起四次挥手

2.4 TCP网络编程流程

步骤 客户端操作 调用的函数
1 创建流式套接字 socket()
2 填充服务器的网络信息结构体 struct sockaddr_in
3 与服务器建立连接 connect()
4 收发数据 send() / recv()
5 关闭套接字 close()

Telnet 是一种远程登录协议,用于通过网络连接到另一台计算机的终端。后接服务器IP

2.5 三次握手

确保双方都能正常收发数据,同步初始序号,避免历史连接请求造成混乱,为后面的可靠传输打好基础。

2.5.1 三次握手的状态变化

发送方 服务端状态变化 客户端状态变化 原因说明
客户端 --- CLOSED → SYN_SENT 客户端主动发起连接,发出同步请求,等待服务端确认
服务端 LISTEN → SYN_RCVD SYN_SENT(不变) 服务端收到 SYN,回复确认并携带自己的 SYN,表示愿意连接;自身进入半连接状态,等待客户端最终确认
客户端 SYN_RCVD → ESTABLISHED SYN_SENT → ESTABLISHED 客户端收到 SYN+ACK,确认连接可用,双方进入数据传输状态

2.5.2 两次握手不可以原因

问题类型 两次握手的问题 三次握手如何解决
数据包丢失 服务端发的 SYN+ACK 丢失后,客户端不知道、服务端空等,造成半开连接 客户端收到 SYN+ACK 后必须回复 ACK,服务端收到 ACK 才认为连接建立; 若 ACK 丢失会重发或超时关闭,避免空等
SYN 攻击风险 攻击者发一个 SYN,服务端就立即分配资源建立连接,极易耗尽内存 服务端发 SYN+ACK 后不分配应用资源,收到客户端的 ACK 确认后才真正建立连接,有效抵抗伪造 SYN 攻击

2.6 四次挥手

2.6.1 四次握手的状态变化

第几次挥手 发送方 状态变化 原因
第一次 客户端 ESTABLISHED → FIN_WAIT_1 客户端主动关闭,表示"我没数据要发了",进入等待服务端确认的状态
第二次 服务端 ESTABLISHED → CLOSE_WAIT 服务端收到 FIN,回复确认,表示"我知道你要关了";但服务端可能还有数据要发,所以先进入半关闭状态
--- 客户端 FIN_WAIT_1 → FIN_WAIT_2 收到服务端的 ACK 确认,进入 FIN_WAIT_2,等待服务端发 FIN
第三次 服务端 CLOSE_WAIT → LAST_ACK 服务端数据发完了,主动发送 FIN,表示"我也没数据要发了",等待客户端最后一次确认
第四次 客户端 FIN_WAIT_2 → TIME_WAIT 收到服务端的 FIN,回复确认,但不确定 ACK 是否丢失,所以等待 2MSL(保证服务端能收到 ACK)
--- 服务端 LAST_ACK → CLOSED 收到客户端的 ACK,连接彻底关闭
--- 客户端 TIME_WAIT → CLOSED 2MSL 时间到,没有收到重传的 FIN,确认服务端已收到 ACK,连接关闭

因为TCP的全双工特性,所以会有四次握手

2.6.2 四次挥手不可以合并成三次

服务端收到客户端的 FIN 后,可能还有数据没发完 ,所以先回复 ACK 表示"我知道你要关了",等数据发完再发 FIN。第二次和第三次挥手不能合并,因为中间可能有数据传输。

2.7 两种过程使用时间

过程 使用时机
三次握手 通信开始前,客户端和服务端建立连接时使用
四次挥手 通信结束后,客户端或服务端释放连接时使用

三、UDP通信流程

3.1 相关函数

3.1.1 recvfrom函数

项目 内容
头文件 #include <sys/socket.h> #include <sys/types.h>
函数原型 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
参数 - sockfd 表示客户端的 socket 套接字
参数 - buf 要接收的数据首地址
参数 - len 可接受数据的最大长度
参数 - flags 控制选项(如 MSG_DONTWAIT 非阻塞,MSG_PEEK 窥视数据,0 阻塞)
参数 - src_addr 源地址,获取发送方的信息
参数 - addrlen 地址长度
返回值 成功返回接收字节数,失败返回 -1(并重置错误码)

3.1.2 sendto函数

项目 内容
头文件 #include <sys/socket.h> #include <sys/types.h>
函数原型 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
参数 - sockfd 表示客户端的 socket 套接字
参数 - buf 要发送的数据首地址
参数 - len 数据大小
参数 - flags 控制选项(如 MSG_DONTWAIT 非阻塞,MSG_PEEK 窥视数据,0 阻塞)
参数 - dest_addr 目的地址,数据将要发向哪一个 IP 地址的主机
参数 - addrlen 地址长度
返回值 成功返回发送字节数,失败返回 -1(并重置错误码)

3.1.3 sendto函数

项目 内容
头文件 #include <sys/socket.h> #include <sys/types.h>
函数原型 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
参数 - sockfd 表示客户端的 socket 套接字
参数 - buf 要发送的数据首地址
参数 - len 数据大小
参数 - flags 控制选项(如 MSG_DONTWAIT 非阻塞,MSG_PEEK 窥视数据,0 阻塞)
参数 - dest_addr 目的地址,数据将要发向哪一个 IP 地址的主机
参数 - addrlen 地址长度
返回值 成功返回发送字节数,失败返回 -1(并重置错误码)

3.2 单播

单播:一对一通信,数据包从单一源地址发送到单一目标地址

层级 职责
应用层 调用 sendto() 发送数据到指定目标 IP 和端口 调用 recvfrom() 接收来自特定源的数据
传输层 封装 UDP 头部:源端口、目标端口、长度、校验和 不建立连接,直接发送数据报
网络层 封装 IP 头部:源 IP、目标 IP(单播地址,如 192.168.1.100) 根据目标 IP 查找路由表,选择下一跳
链路层 根据目标 IP 的 MAC 地址(通过 ARP 解析)封装以太网帧 通过物理网络设备(如网卡)发送到目标主机

3.3 组播

组播:一对多通信,数据包发送到一个组播组,组内所有成员均可接收

层级 职责
应用层 发送端:调用发送数据到组播地址(如 239.255.0.1) 接收端:调用加入组播组(IP_ADD_MEMBERSHIP)
传输层 封装 UDP 头部:目标端口为组播端口(如 12345) 组播成员无需提前建立连接
网络层 封装 IP 头部:目标 IP 为组播地址(D 类地址,224.0.0.0~239.255.255.255) 接收端通过 IGMP 报文通知路由器加入/离开组播组 路由器维护组播组成员列表,仅向存在成员的子网转发数据
数据链路层 组播 MAC 地址映射:将 IP 组播地址转换为以太网组播 MAC(如 01:00:5E:XX:XX:XX) 交换机/路由器:根据组播 MAC 地址复制数据包到多个端口

3.4 setsockopt函数

项目 内容
头文件 #include <sys/socket.h>
函数原型 int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
参数 - sockfd 目标套接字描述符,表示需要配置的套接字
参数 - level 选项层级(如 SOL_SOCKET、IPPROTO_IP)
参数 - optname 选项名称(如 SO_REUSEADDR、IP_ADD_MEMBERSHIP)
参数 - optval 指向选项值的指针,类型和长度需与 optname 匹配
参数 - optlen optval 指向的数据长度
返回值 成功返回 0,失败返回非 0 数据

3.5 广播

广播:一对所有通信,数据包发送到同一网络内的所有主机

层级 职责
应用层 发送端:调用 setsockopt() 启用广播选项(SO_BROADCAST) 调用 sendto() 发送数据到广播地址(如 255.255.255.255)
传输层 封装 UDP 头部:目标端口为广播端口(如 9999) 接收端无需加入组,但需监听指定端口
网络层 封装 IP 头部:目标 IP 为广播地址(受限广播 255.255.255.255 或定向广播 192.168.1.255) 受限广播仅在本局域网内传播,路由器默认不转发 定向广播可跨子网(需路由器支持,通常被禁用)
数据链路层 广播 MAC 地址:FF:FF:FF:FF:FF:FF 交换机将广播包泛洪到所有端口(除源端口)
相关推荐
Ether IC Verifier1 小时前
RDMA常用缩写及应用详解
网络·网络协议·tcp/ip·计算机网络·dpu
傻啦嘿哟2 小时前
本地部署 vs 云服务器部署:IP环境对采集成功率的影响有多大
运维·服务器·tcp/ip
TechWayfarer2 小时前
IP归属地API接入实战指南:3天内安全上线的评估与落地方法
网络·tcp/ip·安全
Dontla2 小时前
OSI七层网络模型(OSI Model:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层)物链网传会表应、TCP/IP模型
网络·网络协议·tcp/ip
IpdataCloud2 小时前
不同业务如何选IP查询更新频率?离线与在线协同策略
前端·网络协议·tcp/ip·html
Chengbei112 小时前
FOFA高级会员、DayDaymap、360Quake、Hunter测绘搜索引擎高级会员免费使用最大1W条查询
网络·安全·web安全·搜索引擎·网络安全·金融·系统安全
positive_zpc2 小时前
计算机网络——应用层
服务器·网络·计算机网络
a里啊里啊2 小时前
软考-软件评测师:知识点整理(四)——信息安全知识
服务器·网络·计算机网络·php·哈希算法·软考·加密算法
minji...2 小时前
Linux 网络套接字编程(三)UDP服务器与客户端实现:Windows与Linux通信,新增字典翻译功能的 UDP 通信
linux·服务器·开发语言·网络·windows·算法·udp