再说说TCP和UDP源端口的确定
到达杭州已经两周了,基本已经适应了新环境的工作节奏,在生活上依然有些许困难会感到无助,但相信所有问题在不久终究会解决的,遇到困难的时候就是成长的时候,比如这两周我学会了识别洗发露和护发素,比如我学会了用支付宝扫码坐公交车,等等…
本周来说一个老话题,即 一个TCP连接如何确定自己的源端口。这个问题在几年前就分析过,正好前些天一个朋友又问了,我就又进一步进行了思考,觉得正好可以作为本周的话题来讨论一下。
可以先看看我之前的分析:
TCP源端口选择算法与列维模型:https://blog.csdn.net/dog250/article/details/14054987
简单谈一点linux内核中套接字的bind机制–数据结构以及端口确定:https://blog.csdn.net/dog250/article/details/5303572
先不要管所谓的什么 列维模型,它只是一个大自然中高效的聚集搜索模式,类似幂律一样普遍,这个后面再说,几年前的事了,少时不知愁滋味,纸上谈兵。
现在只看算法就足够了。
当然,上面这两篇文章并没有描述更多的细节,所以我打算在本文中补充一二。
先看内核是如何组织TCP源端口号数据结构,我依然用一个图示表达,这比代码更加清晰一些:
以上这个结构在内核中叫做bhash,是TCP协议实现中3个核心hash之一,这3个hash结构分别是:
- bhash:维护连接的源端口号,以源端口号计算hash值
- ehash:维护establish连接,以四元组计算hash值
- lhash:维护侦听TCP,以{srcIP,srcPORT}二元组计算hash值
显然,关于如何确定源端口的问题就转化为了上述数据结构的查询,插入的问题,这个问题的解法是明确的,即:
为新的待确定源端口的连接socket查询到一个最优的插入位置并插入。
我们非常明确的一个目标就是:维持四元组的唯一性!
这无疑是一个搜索结构的操作问题。哈哈,又是一道面试题咯。
接下来要解决的是,在两个不同的场景下,如何操作以上这个数据结构。两个场景分别如下:
- TCP socket在bind的时候
- TCP socket在connect的时候
显然,在TCP进行bind的时候,由于此时并不确定目标是谁,无论是目标IP还是目标端口都不确定,甚至不晓得这个TCP是不是一个Listener,那么四元组的唯一性约束显然强化了不少,即: 必须保证{srcIP,srcPORT}二元组的唯一性! 事实上此时我们要保证的是{srcIP,srcPORT,0,0}元组的唯一性。
与bind场景不同的是,如果一个socket事先没有bind,直接调用了connect,当我们调用connect的时候,此时确定的是{dstIP,dstPORT}元组,那么此时的约束就松了不少,也就是说要想保证四元组唯一性,这种场景下给我们的机会会更多一些。
具体来讲,我给出一个流程:
有了connect的场景分析,bind场景就再简单不过了,省略下面ehash的部分即可:
理清了关系之后,很简单是吧。这里讲的是TCP,对于UDP而言也一样适用,只不过UDP有更简单的解法。
以上就是确定源端口的基本原则,然而在具体操作过程中,还有一个原则,即维护数据结构的平衡型,我们不希望单独的hash冲突链表过长,因为遍历一个冲突链表的时间复杂度是O(n),这显然毫无可扩展性,所以在插入过程中需要 尽量插入到短的hash bucket链表中。
这就涉及到另一个问题,即:
图示中初始的探测端口port=pn如何选择的问题!
这里就是列维模型在起作用了!
物理类聚,这是普世真理。Linux内核在实现这个确定源端口的过程中,经过了几次进化,但万变不离其宗,对于TCP而言,Linux从一个随机确定的hash bucket开始探测,然后环状遍历所有的bhash bucket,对每一个bucket执行上面图示里的算法。
对于UDP而言,我劝大家review一下Linux内核2.6.18,3.10,4.9+的代码,这代表了三个进化阶段,起初在2.6.18版本时,UDP维护了一个全局的 udp_port_rover 变量,指示下一次探测可用源端口时从哪里开始,然而到了3.10,4.x版本,实现方式便起了变化,不再通过链表数据结构进行多次广度优先遍历,而是采用深度优先原则使用位图来实现,但这并没有改变实质。
代码并不难懂,相对于像屎一样的TCP拥塞控制算法的代码,这个要好很多,找 get_port 回调函数就好,然后看看 tcp_v4_connect 函数,大概10分钟应该可以读懂,我这里就不再赘述细节,记住一个原则,如果你要实现自己的算法,优先找最短的冲突链表进行遍历插入,你稍微费点事,带来的是整个系统性能的提升,可谓人人为我,我为人人。
最后,我还想说说列维模型。
列维模型最初是2004年由一个名为Dirk Brockmann 的德国物理学家发现的一个普世模型,我最开始看到后简直深陷而不可自拔!
我发现这个简单的模型竟然能解释人类文明的形成,我是多么喜欢历史学,我发现这个模型竟然能解释它,并且能预测未来!
当我看完了《三体》三部曲之后,这更加加深了我对列维模型的好感和狂热!
盗自己之前文章一张图吧,实在是忍着饥饿在写这段文字:
这就是列维模型!TCP和UDP确定源端口的算法绝对符合这个模型,不信你接受10w+的连接后导出数据画成图标看看,我试过,你也试试,非常壮美。
你会发现,自然界,我们生活的空间,没有什么东西是平铺平淡的,正是聚集造就了我们美好的世界,不然一堆原子均匀的分布在宇宙空间,那才没有任何意义,然而墒总是在增加,宇宙趋向于无序,这也是宇宙最终的结局,不禁令人感到悲哀!
我一直在关注幂律,如何结合列维模型,我发现列维模型是导致幂律的根本缘由,然而进一步问,列维模型的原因是什么?
我的答案就是 惰性!
就我自己而言,我喜欢喝各种新上市的饮料,酒类,我会不断尝试新的东西,但是最终我总是会聚焦到某一个牌子,从此不再过问别的,只买这个牌子,比如真露烧酒,比如百事可乐,比如红标威士忌… 我不换别的牌子不是因为我喜欢这些牌子,而是因为我习惯了这些牌子, 仅此而已,我的惰性让我在选饮料选酒时,给了真露,百事,红标更多的权重,仅此而已。
进一步思考,这是为什么?
也许就是我大脑的cache吧!我的大脑并不能随时拥抱变化,你的也不行,再强大的大脑都不行,所以我觉得,分级cache是现代计算机科学中非常非常非常重要的一个设计!
拥抱变化,成了一个多么优秀且不可及的潜质,因为没人想拥抱变化!
幂律是什么导致的,我觉得就是惰性的影响导致的聚集效应导致的。
好了,本文该结束了。现在,温州老板正在香港进货。
再说说TCP和UDP源端口的确定相关推荐
- Linux - lsof显示 tcp,udp 的端口和进程
文章目录 功能 语法 示例 lsof -i 显示 tcp,udp 的端口和进程等相关 查看服务器 80 端口的占用情况 使用 -p 查看指定进程打开的文件 更多命令 功能 lsof(list open ...
- 计算机网络 之 TCP和UDP的端口号解析
前言:今天了解一下tcp和udp报文的端口.发现一直以来都只是知道端口用于区分同一IP的服务器的不同服务,已经端口的大小.在查找traceroute的资料的时候,才了解到一些之前没注意到的东西. (一 ...
- TCP、UDP常用端口(转自wiki)
List of TCP and UDP port numbers From Wikipedia, the free encyclopedia This is a list of
- 常用对照表之TCP及UDP常见端口参照
著名端口 端口号码 / 层 名称 注释 1 tcpmux TCP 端口服务多路复用 5 rje 远程作业入口 7 echo Echo 服务 9 discard 用于连接测试的空服务 11 systat ...
- Linux网络知识详解以及demo(Centos6、7)——OSI、TCP、UDP、IP、子网掩码/划分、网关、路由、广播、虚拟网络、网卡、交换机、DNS、ARP
ip地址:网络通讯标识信息 子网掩码:在局域网中可以有多少个主机 网关:从一个局域网到另一个局域网的必经之路 网络号:主机位全为0 广播地址:主机位全为1 子网掩码:网络位全为1,主机位全为0 虚拟软 ...
- 【网络篇】第三篇——源端口号和目的端口号
端口号的定义 理解源端口号和目的端口号 PORT VS PID 端口号如何确定? 端口号于协议 认识TCP协议和UDP协议 端口号的定义 数据链路层的地址是MAC地址,用来识别同一链路中不同的计算机 ...
- (6)TCP与UDP之端口号
1.端口号的定义 数据链路层的地址是MAC地址,用来识别同一链路中不同的计算机 网络层(IP)的地址是IP地址,用来识别TCP/IP网络中互连的主机和路由器 传输层中类似于地址的概念就是端口号 端口号 ...
- 网络通信协议,TCP和UDP 的区别
1.网络通信 互联网本质就是一系列的网络通信,互联网协议的功能是定义计算机如何介入internet,以及介入internet的计算机通信的标准.互联网协议按照功能不同分为osi7层或tcp/ip五层或 ...
- Python网络编程-一文厘清socket、TCP和UDP那点事
文章目录 网络基础 网络协议 IP地址与端口 socket套接字 概念 Python中socket模块 TCP下的服务器与客户端 TCP工作原理 TCP服务器的实现 TCP客户端的实现 UDP下的服务 ...
最新文章
- poj2367 Genealogical tree
- applicationcontext理解使用
- Unity5和WebGL移植指南的一些总结
- 推荐8个最佳的jQuery移动开发插件
- eclipse自动为变量生成Get/Set函数
- 人工智能(3)---未来已来,如何成为一名人工智能产品经理
- Nginx负载均衡的详细配置及使用案例详解.
- USACO Training3.3亚瑟王的宫殿【搜索】By cellur925
- windows下替代SSH,Xshell软件的mobaxterm
- 使用泛型, 写一个为任意类型的动态数组添加元素的方法
- Waiting Processed Cancelable ShowDialog (Release 2)
- linux看定时任务命令,linux命令-定时任务at
- matlab2016b慢,Matlab 2016a/b中调用GPU速度巨慢的解决办法
- 2017互联网寒冬程序员求职随感
- 初等变换和阶梯矩阵【】
- 用芯弹一首《大加洛普舞曲》:从AI-ISP,透视vivo的双芯之路
- 【思维进阶】如果回到十年前你会做哪些事情?
- 大海捞针 Skia(C++) 第 1 期:Skia 环境搭建
- GPIO 配置之ODR, BSRR, BRR 详解
- SwiftUI中如何使用App Tracking Transparency Framework