到达杭州已经两周了,基本已经适应了新环境的工作节奏,在生活上依然有些许困难会感到无助,但相信所有问题在不久终究会解决的,遇到困难的时候就是成长的时候,比如这两周我学会了识别洗发露和护发素,比如我学会了用支付宝扫码坐公交车,等等…

本周来说一个老话题,即 一个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查询到一个最优的插入位置并插入。

我们非常明确的一个目标就是:维持四元组的唯一性!

这无疑是一个搜索结构的操作问题。哈哈,又是一道面试题咯。

接下来要解决的是,在两个不同的场景下,如何操作以上这个数据结构。两个场景分别如下:

  1. TCP socket在bind的时候
  2. 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源端口的确定相关推荐

  1. Linux - lsof显示 tcp,udp 的端口和进程

    文章目录 功能 语法 示例 lsof -i 显示 tcp,udp 的端口和进程等相关 查看服务器 80 端口的占用情况 使用 -p 查看指定进程打开的文件 更多命令 功能 lsof(list open ...

  2. 计算机网络 之 TCP和UDP的端口号解析

    前言:今天了解一下tcp和udp报文的端口.发现一直以来都只是知道端口用于区分同一IP的服务器的不同服务,已经端口的大小.在查找traceroute的资料的时候,才了解到一些之前没注意到的东西. (一 ...

  3. TCP、UDP常用端口(转自wiki)

    List of TCP and UDP port numbers From Wikipedia, the free encyclopedia     This is a list of

  4. 常用对照表之TCP及UDP常见端口参照

    著名端口 端口号码 / 层 名称 注释 1 tcpmux TCP 端口服务多路复用 5 rje 远程作业入口 7 echo Echo 服务 9 discard 用于连接测试的空服务 11 systat ...

  5. Linux网络知识详解以及demo(Centos6、7)——OSI、TCP、UDP、IP、子网掩码/划分、网关、路由、广播、虚拟网络、网卡、交换机、DNS、ARP

    ip地址:网络通讯标识信息 子网掩码:在局域网中可以有多少个主机 网关:从一个局域网到另一个局域网的必经之路 网络号:主机位全为0 广播地址:主机位全为1 子网掩码:网络位全为1,主机位全为0 虚拟软 ...

  6. 【网络篇】第三篇——源端口号和目的端口号

    端口号的定义 理解源端口号和目的端口号 PORT VS PID 端口号如何确定? 端口号于协议 认识TCP协议和UDP协议 端口号的定义 数据链路层的地址是MAC地址,用来识别同一链路中不同的计算机 ...

  7. (6)TCP与UDP之端口号

    1.端口号的定义 数据链路层的地址是MAC地址,用来识别同一链路中不同的计算机 网络层(IP)的地址是IP地址,用来识别TCP/IP网络中互连的主机和路由器 传输层中类似于地址的概念就是端口号 端口号 ...

  8. 网络通信协议,TCP和UDP 的区别

    1.网络通信 互联网本质就是一系列的网络通信,互联网协议的功能是定义计算机如何介入internet,以及介入internet的计算机通信的标准.互联网协议按照功能不同分为osi7层或tcp/ip五层或 ...

  9. Python网络编程-一文厘清socket、TCP和UDP那点事

    文章目录 网络基础 网络协议 IP地址与端口 socket套接字 概念 Python中socket模块 TCP下的服务器与客户端 TCP工作原理 TCP服务器的实现 TCP客户端的实现 UDP下的服务 ...

最新文章

  1. poj2367 Genealogical tree
  2. applicationcontext理解使用
  3. Unity5和WebGL移植指南的一些总结
  4. 推荐8个最佳的jQuery移动开发插件
  5. eclipse自动为变量生成Get/Set函数
  6. 人工智能(3)---未来已来,如何成为一名人工智能产品经理
  7. Nginx负载均衡的详细配置及使用案例详解.
  8. USACO Training3.3亚瑟王的宫殿【搜索】By cellur925
  9. windows下替代SSH,Xshell软件的mobaxterm
  10. 使用泛型, 写一个为任意类型的动态数组添加元素的方法
  11. Waiting Processed Cancelable ShowDialog (Release 2)
  12. linux看定时任务命令,linux命令-定时任务at
  13. matlab2016b慢,Matlab 2016a/b中调用GPU速度巨慢的解决办法
  14. 2017互联网寒冬程序员求职随感
  15. 初等变换和阶梯矩阵【】
  16. 用芯弹一首《大加洛普舞曲》:从AI-ISP,透视vivo的双芯之路
  17. 【思维进阶】如果回到十年前你会做哪些事情?
  18. 大海捞针 Skia(C++) 第 1 期:Skia 环境搭建
  19. GPIO 配置之ODR, BSRR, BRR 详解
  20. SwiftUI中如何使用App Tracking Transparency Framework

热门文章

  1. LayUI之CRUD
  2. 【MySQL】多表查询策略(多表联查子查询)
  3. PS鼠绘教程:PS鼠绘炫酷红色保时捷跑车
  4. 项目管理中什么最重要?
  5. 微信中打开外部浏览器下载安装包
  6. Intellij idea 第一天
  7. Win7笔记本电脑启用虚拟WIFI共享上网
  8. 适合小白的大白话讲解---Git与Github的区别
  9. vin-slam中调用ceres库内部代码分析与性能优化
  10. 清除微信公众号缓存方法(安卓手机+苹果手机)