ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux下 传输层协议UDP详解(含底层源码解析)

Linux下 传输层协议UDP详解(含底层源码解析) 欢迎来到我的频道【点击跳转专栏】本文所有代码已托管至码云【点此转跳】文章目录1. 传输层2. 再谈端⼝号2.1 端⼝号范围划分2.2 认识知名端⼝号2.3 两个问题3. UDP协议3.1 UDP协议端格式3.2 几个结论和问题重点封装和解包本质3.3 【结合源码解析】当我们收到一个报文的时候我们是如何做到以文件管理的原理读到数据到应用层的超级大重点一定要读3.4 UDP的特点3.5 ⾯向数据报3.6 既然不可靠为什么还要有UDP3.7 UDP的缓冲区3.8 UDP使⽤注意事项3.9 补充基于UDP的应⽤层协议1. 传输层负责数据能够从发送端传输接收端。2. 再谈端⼝号端⼝号(Port)标识了⼀个主机上进⾏通信的不同的应⽤程序在TCP/IP协议中, ⽤ “源IP”, “源端⼝号”, “⽬的IP”, “⽬的端⼝号”, “协议号” 这样⼀个五元组来标识⼀个通信(可以通过netstat -n查看);2.1 端⼝号范围划分0‑1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的.1024‑65535: 操作系统动态分配的端口号.客户端程序的端口号, 就是由操作系统从这个范围分配的.2.2 认识知名端⼝号有些服务器是非常常用的,为了使用方便,人们约定一些常用的服务器,都是用以下这些固定的端口号:ssh服务器,使用22端口ftp服务器,使用21端口telnet服务器,使用23端口http服务器,使用80端口https服务器,使用443执行下面的命令,可以看到知名端口号1 cat /etc/services我们自己写一个程序使用端口号时,要避开这些知名端口号.2.3 两个问题一个进程可以绑定多个端口号吗可以一个端口号可以绑定多个进程吗不可以3. UDP协议3.1 UDP协议端格式16位UDP⻓度, 表⽰整个数据报(UDP⾸部UDP数据)的最⼤⻓度;如果校验和出错, 就会直接丢弃;3.2 几个结论和问题重点封装和解包本质为什么我们写socket的时候port是16位的因为UDP协议规定报头和有效载荷如何分离UDP是采用固定长度的报头8字节其中有一个固定位置是16位UDP长度因为我们有它的长度所以我们就能很轻松的将报头和正文内容进行分离那么有效载荷是怎么交付给上层的呢因为有16位目的端口号就能得到交付给哪个端口的进程那么UDP是否存在粘包问题UDP叫用户数据报协议长度固定好收一个报文得一个报文所以不存在粘包问题我们是如何理解UDP协议的报头的呢OS内核是C语言写的协议本质是一个结构体在OS内部双方是能互传结构体变量天然可以做反序列化如图所以我们该如何理UDP解封装过程当我们需要发送数据了在OS内部会给我们开辟一段缓冲区会把上层数据拷贝下来并将指针向前移动UPD报头的位置然后以访问结构体的方式通过指针将UDP属性部分填充进去封装的本质 就是对结构体的拷贝报文如何在协议层从一层到另一层从底层到上层你就是在做回调从上层传下层你就是调用下层函数本质都是传参的方式进行协议栈式的流通3.3 【结合源码解析】当我们收到一个报文的时候我们是如何做到以文件管理的原理读到数据到应用层的超级大重点一定要读首先OS内部可能会同时存在多个报文那么就不可避免的需要对报文进行管理OS报文的在内核表达的本质是一个struct sk_buff结构体所谓管理就是将收到的报文以某种数据结构假设是链表进行管理head:一般指向数据的最开头。end指向结尾边界后面有些属性字段也要被管理不用管data在应用层指向应用层数据开头到传输层移动到TCP协议头开头到网络层移动到IP协议头依次类推而这就是封装的本质即不断入栈的形式对指针进行移动解包就是反过来tail指向应用层数据结尾结论报文贯穿协议栈封装和解包的过程最核心的其实就是移动指针就可以了知道报文数据的存储结构体接着将视线移动到进程内部进程OS会创建对应的task_struct结构体同时会会创建一个files_struct里面有个struct file* fd_arrary[]的数组负责该进程所创建的文件以下标fd的方式进行管理当创建对应套接字的时候就会创建对应的struct file结构体但是怎么标识套接字的特殊型于是会创建一个struct socket的结构体通过file内部的万能指针void* private_data指向对应的struct socket至此就完成了文件到套接字的转换接着把视角放到socket内部socket被private_data指向同时socket内部还有个struct file * file回指struct file由于一切皆文件当我们通过read、recv等接口进行读写的时候进程回被阻塞进程就会把PCB列入wait_queue_head_t wait中呆着当数据有了就会再次把进程唤醒同时 还有个结构叫struct sock * sk指向的就是套接字内部的属性了将视线继续往下看在struct sock中有两个属性struct sk_buff_head就是对应的 缓冲队列这一块我写过 为了文章完整性 我把关键内容截取下来了当然光有sock还不够上层它还套了层struct inet_sock:里面第一个成员就是struct sock,这里面的daddr、rcv_saddr、dport等就是目的地址、原地址、端口号等即 套接字的绑定信息就是填到这里的我们真实在内核创建套接字并持有信息的结构体其实是inet_sock!而不是struct sock只是其第一个成员是 struct scok那么怎么区分是UDP还是TCP呢其实利用多态的思想 本质上就是在struct inet_sock上再套上一层struct udp_sock里面第一个属性就是struct inet_sock inet未来换成TCP也是同理而里面的struct sock就是最原始的基类网络传送接收的数据就是通过该基类管理的后面不管是套上什么样的子类都可以利用多态思想 通过指针的强转 然后利用文件的wirte、read接口读取写入网络缓冲区里面的东西至此 文件和套接字网络就关联起来了3.4 UDP的特点UDP传输的过程类似于寄信⽆连接:知道对端的IP和端⼝号就直接进⾏传输, 不需要建⽴连接;不可靠没有确认机制, 没有重传机制; 如果因为⽹络故障该段⽆法发到对⽅, UDP协议层也不会给应⽤层返回任何错误信息UDP本身不可靠但不排除上层代码维护可靠性⾯向数据报不能够灵活的控制读写数据的次数和数量3.5 ⾯向数据报应⽤层交给UDP多⻓的报⽂, UDP原样发送, 既不会拆分, 也不会合并⽤UDP传输100个字节的数据:如果发送端调⽤⼀次sendto, 发送100个字节, 那么接收端也必须调⽤对应的⼀次recvfrom, 接收100个字节; ⽽不能循环调⽤10次recvfrom, 每次接收10个字节3.6 既然不可靠为什么还要有UDP所谓的不可靠不是数据传不过去而是丢包了我们不关心现阶段网络环境已经很少丢包了所谓的不可靠不代表不可用可靠意味着需要做更多工作就相对更慢而不可靠就意味着相对快一点使用更简单意味着在不重要的场合可以更简单更快速进行传输3.7 UDP的缓冲区虽然UDP也是全双工但是 UDP没有真正意义上的发送缓冲区. 调⽤sendto会直接交给内核, 由内核将数据传给⽹络层协议进⾏后续的传输动作因为UDP是不可靠的直接发出去就行不存在TCP那种如果传输失败需要缓冲区数据作为备份再次发送;UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序⼀致;如果缓冲区满了, 再到达的UDP数据就会被丢弃;3.8 UDP使⽤注意事项我们注意到, UDP协议⾸部中有⼀个16位的最⼤⻓度.也就是说⼀个UDP能传输的数据最⼤⻓度是64K(包含UDP⾸部)然⽽64K在当今的互联⽹环境下, 是⼀个⾮常⼩的数字.如果我们需要传输的数据超过64K, 就需要在应⽤层⼿动的分包, 多次发送, 并在接收端⼿动拼装3.9 补充基于UDP的应⽤层协议NFS:网络文件系统TFTP:简单文件传输协议DHCP:动态主机配置协议BOOTP:启动协议(用于无盘设备启动)DNS:域名解析协议当然,也包括你自己写UDP程序时自定义的应用层协议类似动不动10w的直播视频都是UDP的因为一个一个建立连接服务器成本太高了
返回列表