TCP与UDP
TCP与UDP对比
TCP
特点:面向连接、可靠传输、流量控制、拥塞控制、全双工
优点:可靠性(ACK确认、超时重传)、顺序性
缺点:首部开销大(20字节)、连接管理复杂、延迟高
应用场景:HTTP/HTTPS、文件传输(FTP)、邮件(SMTP/IMAP)
UDP
特点:无连接、尽力交付、无重传、无拥塞控制、低开销
优点:传输效率高(8字节首部)、实时性强
缺点:数据可能丢失、乱序
应用场景:视频流(RTP)、DNS查询、在线游戏、VoIP
| 协议 | 默认传输层协议 | 端口号 | 说明 |
|---|---|---|---|
| HTTP | TCP | 80 | 超文本传输协议,基于 TCP 保证可靠传输,如网页浏览 |
| FTP | TCP | 20(数据)、21(控制) | 文件传输协议,需要可靠连接和顺序传输 |
| DNS | UDP | 53 | 域名系统,查询响应通常很小,UDP 开销小、速度快;当响应超过 512 字节或需要可靠传输时(如区域传输),也会使用 TCP |
| SMTP | TCP | 25 | 简单邮件传输协议,邮件传输不能丢失数据,必须基于 TCP |
TCP通信
TCP网络编程流程
服务器流程:
1.创建流式套接字--socket()
2.填充服务器的网络信息结构体--struct sockaddr_in
3.将套接字于服务器的网络信息结构体绑定--bind()
4.将套接字设置成被动监听状态--listen()
5.阻塞等待客户端连接--accept()
6.收发数据—recv()/send()
7.关闭套接字--close()
socket编程
socket函数
socket()函数是网络编程中最基础的一个函数,它的作用就像是在你的程序里“打开一个网络收发室”——创建一个用于网络通信的“端点”,后续通过这个端点发送或接收数据。
头文件:#include <sys/socket.h>
使用 socket 编程必须包含这个头文件,它定义了 socket 相关的类型、常量和函数原型。
函数原型:int socket(int domain, int type, int protocol);
参数:
•domain就是通信域,也可以说是协议族,常见通信域如下:
| 常见值 | 含义 | 通俗解释 |
|---|---|---|
AF_INET | IPv4 网络通信 | 使用互联网上常见的 IP 地址(如 192.168.1.1),适用于跨网络通信。 |
AF_INET6 | IPv6 网络通信 | 下一代互联网协议,地址更长,解决 IPv4 不够用的问题。 |
| 本地通信(同一台机器内) | 两个程序在同一台电脑上通过文件系统进行高效通信,不经过网卡。 |
AF_PACKET | 原始套接字(链路层) | 直接访问网卡,可以收发最原始的以太网帧,常用于抓包工具(如 tcpdump)。 |
AF_表示“地址族”,有时也用PF_(协议族),但在 Linux 中两者其实是一样的。
•type表示socket的类型,常用的socekt类型如下:
| 常见值 | 特点 | 使用场景 | 比喻 |
|---|---|---|---|
SOCK_STREAM | 面向连接、可靠、有序、无边界 | TCP 协议 | 打电话:先拨号建立连接,对方应答后开始说话,你说一句,对方听到完整的一句,顺序不会乱。 |
SOCK_DGRAM | 无连接、不可靠、无序、有边界 | UDP 协议 | 寄信:直接把信扔进邮筒,不用通知对方,可能丢信、顺序错乱,但速度快。 |
SOCK_RAW | 原始套接字 | 构造特殊网络包(如 ping、traceroute) | 直接操作信件的原始内容,甚至自己写信封的地址格式。 |
•protocol表示附加协议无附加协议填0(自动选择默认协议)
在domain和type已经确定的前提下,如果还有多种可选协议,就靠这个参数指定。绝大多数情况下,填 0 表示自动选择默认协议。
返回值:成功返回套接字(文件描述符-非负整数),失败返回-1(并重置错误码)
bind函数
头文件:#include <sys/socket.h>
函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
•sockfd就是文件描述符(由socket函数返回)
•addr表示地址结构体指针(const struct sockaddr*),指向要绑定给sockfd的协议地址,该结构体根据创建socket时的协议族不同而不同,这里以IPV4为例:
struct sockaddr_in { sa_family_t sin_family; // 地址族 in_port_t sin_port; // 16位端口号 struct in_addr sin_addr; // 32位IP地址 }; struct in_addr { uint32_t s_addr; // 32位IPv4地址 };•addrlen表示地址的长度
返回值:成功返回0,失败返回-1(并重置错误码)
listen函数
头文件:#include <sys/socket.h>
函数原型:int listen(int sockfd, int backlog);
参数:
•sockfd表示要监听的socket套接字
•backlog表示半连接队列的长度(即socket可以排队的最大连接个数)
返回值:成功返回0,失败返回-1(并重置错误码)
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(并重置错误码)
connect函数
头文件:#include <sys/socket.h>
函数原型:int connect(int sockfd, const struct sockaddr*addr, socklen_taddrlen);
参数:
•sockfd表示客户端的socket套接字
•addr表示目标服务器的地址结构体
•addrlen地址结构体的长度
返回值:成功返回0,失败返回-1(并重置错误码)
recv函数
头文件:#include <sys/socket.h>
函数原型:ssize_t recv(int sockfd, void *buf, size_tlen, int flags);
参数:
•sockfd表示客户端的socket套接字
•buf用于存储接收的数据
•len数据大小
•flags控制选项(如MSG_DONTWAIT非阻塞,MSG_PEEK窥视数据,0阻塞)
返回值:成功返回实际接收数据字节数,失败返回-1(并重置错误码)
send函数
头文件:#include <sys/socket.h>
函数原型:ssize_tsend(int sockfd, const void *buf, size_tlen, int flags);
参数:
•sockfd表示客户端的socket套接字
•buf要发送的数据首地址
•len数据大小
•flags控制选项(如MSG_DONTWAIT非阻塞,MSG_PEEK窥视数据,0阻塞)
返回值:成功返回实际发送数据字节数,失败返回-1(并重置错误码)
close函数
头文件:#include <unistd.h>
函数原型:int close(int fd);
参数:
•fd表示要关闭的套接字
返回值:成功返回0,失败返回-1
TCP网络编程流程
客户端流程:
1.创建流式套接字--socket()
2.填充服务器的网络信息结构体--struct sockaddr_in
3.与服务器建立连接--connect()
4.收发数据—recv()/send()
5.关闭套接字--close()
完整TCP连接建立
完整TCP连接建立过程
四次挥手
UDP通信
UDP 广播是一种将数据报同时发送给同一广播域内所有主机的传输方式
UDP(用户数据报协议)是一种无连接的、不可靠的传输层协议。其头部非常简单,固定长度为8 字节,包含以下四个字段:
| 字段 | 长度(bit) | 说明 |
|---|---|---|
| 源端口 | 16 | 发送方的端口号(可选,不用时可置0) |
| 目的端口 | 16 | 接收方的端口号 |
| 长度 | 16 | UDP头部+数据的字节数(最小8) |
| 校验和 | 16 | 可选(IPv4下为0表示未使用),对伪头部、UDP头部、数据进行校验 |
UDP头部中没有序号(Sequence Number)字段,也没有确认号、标志位、窗口等字段。
UDP通信流程
服务器端:
socket()→bind()→ 循环recvfrom()/sendto()→close()客户端:
socket()→sendto()→ 可选recvfrom()→close()
socket():创建套接字
sendto():数据发送
recvfrom():数据接收
sendto/recvfrom
recvfrom
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(并重置错误码)
sendto
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(并重置错误码)
单播
单播:一对一通信,数据包从单一源地址发送到单一目标地址
流程与各层职责:
应用层
•调用sendto()发送数据到指定目标IP和端口
•调用recvfrom()接收来自特定源的数据
传输层
•封装UDP头部:源端口、目标端口、长度、校验和
•不建立连接,直接发送数据报
网络层
•封装IP头部:源IP、目标IP(单播地址,如192.168.1.100)
•根据目标IP查找路由表,选择下一跳
数据链路层
•根据目标IP的MAC地址(通过ARP解析)封装以太网帧
•通过物理网络设备(如网卡)发送到目标主机
组播
组播:一对多通信,数据包发送到一个组播组,组内所有成员均可接收
流程与各层职责:
应用层
•发送端:调用发送数据到组播地址(如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地址复制数据包到多个端口
setsockopt
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匹配
•optlenoptval指向的数据长度
返回值:成功返回0,失败返回非0数据
广播
广播:一对所有通信,数据包发送到同一网络内的所有主机
流程与各层职责:
应用层
•发送端:调用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
•交换机将广播包泛洪到所有端口(除源端口)
| 特性 | 单播 | 组播 | 广播 |
|---|---|---|---|
| 目标地址 | 单个主机的 IP 地址(如 192.168.1.100) | 组播 IP 地址(224.0.0.0 ~ 239.255.255.255) | 广播地址(如 255.255.255.255 或子网广播地址 192.168.1.255) |
| 接收者数量 | 仅一个指定主机 | 一组加入了该组播组的主机 | 同一广播域(子网)内的所有主机 |
| 数据复制 | 发送端为每个接收者单独发送一份数据 | 发送端只发一份数据,由网络设备复制给所有组成员 | 发送端只发一份数据,由链路层复制给所有主机 |
| 是否支持跨路由 | 是(普通路由) | 需要组播路由协议(如 PIM、IGMP)支持 | 否(路由器默认不转发广播) |