点击上方“分布式实验室”关注公众号

回复“1”抽取纸质技术书

今天这篇文章,想用一个故事和你讲讲端口监听是怎么回事。耐心往下看。

在学生会大楼的角落里,有一家咖啡店,在咖啡店的角落里有两个学生。利兹敲打着她哥哥在她搬到大学时给她的那台破旧的手摇MacBook的键盘。在她左边的长椅上,蒂姆在一个装订成卷的笔记本上写着公式。他们之间有一杯半空的常温咖啡,莉兹不时地喝上一口以保持清醒。

蒂姆写到一半就停笔了,把这张纸从笔记本上撕下来,揉成一团,放在其他揉成一团的小纸片旁边。

“Shit,现在几点了?”他问到。

利兹看了看她笔记本上的时钟,“刚过两点”。

蒂姆打了个哈欠,又开始在新的一页上面涂鸦,但利兹打断了他。

“蒂姆”

“什么?!”,蒂姆回答说,夸张地表达了他对刚开始写就被打断的恼怒。

“在一个端口上监听是什么意思?”

“呃嗯……”

“我必须为net写这个网络服务器的东西”,net是Computer Networks 201的缩写,这是蒂姆在上学期上的一门课。

“是的,我记得那门课。”

“所以我在一个端口上监听连接。”

“80端口”,蒂姆自信地回答,希望通过抢先回答她的问题来缩短谈话时间。

“实际上,我们应该监听8080,这样它就可以在没有root的情况下运行,但这不是重点。”

“哦,对了。那是什么?”

“好吧,监听一个端口是什么意思?”

“它意味着其他进程可以在该端口上连接到它。”蒂姆对这个问题显得很困惑。

“是的,我知道这一点,但怎么做?”

蒂姆考虑了几秒钟才回答。

“我猜操作系统有一个大的端口表,以及在这些端口上监听的进程。当你绑定到一个端口时,它就会在该表中放一个指向你的套接字的指针。”

“是的,我猜。”利兹说,语气中带着犹豫和不满意。

两人回到了他们各自的工作中。沉默了一段时间后,蒂姆小声嘀咕了一句胜利的 “是的!”,并在一张打印的纸上划掉了一个数字。他终于找到了他在微积分作业中一直纠结的一个证明。

利兹趁机再次引起他的注意。

“嘿,蒂姆,看,我正在同时运行绑定在同一端口的两个进程。”

她调整了两个包含Python代码的窗口的大小。

# server1.py
import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', 8080))
sock.listen()
print(sock.accept())

然后在它旁边是另一个程序:

# server2.py
import socketsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('127.0.0.1', 8080))
print(sock.recv(1024))

然后她向他展示了这两个程序在各自的终端窗口中运行,通过Shell连接到大学的cslab3Debian服务器。

蒂姆将笔记本电脑转向自己。他打开第三个终端,停顿了一会儿,搜索他疲惫的大脑,然后输入netcat 127.0.0.1 8080。

netcat 运行后立即退出。在另一个终端窗口中,正在运行的 python server1.py 程序退出,打印。

(<socket.socket fd=4, family=AddressFamily.AF_INET,
type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080),
raddr=('127.0.0.1', 59558)>, ('127.0.0.1', 59558))

他边研究server1.py代码,边自言自语。

“好的,服务器绑定了一个端口,接受了第一个连接它的套接字,然后退出。我明白了,所以它打印的元组是accept调用的结果,然后它立即退出。但是现在......”,将鼠标光标移到显示 server2.py 的编辑器上,“……这一个甚至在听吗?”

他在与之前相同的终端中再次运行netcat 127.0.0.1 8080 -v,结果打印出来如下:

netcat: connect to 127.0.0.1 port 8080 (tcp) failed: Connection refused

“看”,他说,“你的代码中存在一个错误。server2仍在运行,但你从未调用listen。它实际上没有对8080端口做任何事情。”

“当然是,看”,利兹说,抢回了她的笔记本电脑。

她在“netcat”命令的末尾加了一个-u,然后点击回车。这一次,它没有给出一个错误或立即退出,而是等待键盘输入。她对Tim这么快就认为她的代码有问题感到恼火,她敲出了timmy,知道这个绰号让他很不爽。

netcat会话无声无息地结束了,同时,python server2.py程序退出打印。

b'timmy\n'

蒂姆意识到利兹试图与他作对,但没有理会,不想让她满足于对他的挑衅。他向键盘做了个手势。利兹把笔记本扭向他的方向,他输入man netcat',调出netcat的手册,其中描述该工具为“TCP/IP瑞士军刀”。他向下滚动到-u标志,文件将其简单描述为 “UDP模式”。

“啊”,他说,因为他突然想起了什么。“我明白了,server1是通过TCP监听,server2是通过UDP监听。这一定是SOCK_DGRAM的意思。所以它们是不同的协议。我猜操作系统为每个端口都有一个单独的表格。我没想到net涵盖了UDP,直到后来。”

“是的,我提前读了。”

“当然。你怎么会有时间提前阅读,却没有时间在早晨到期前完成这些作业呢?”

“我也可以问你关于Counter Strike的问题”,莉兹反问道。

蒂姆哼了一声。

他们又继续默默地工作了几分钟,然后莉兹打破了沉默。

“嘿,蒂姆,看看这个。我可以在同一个端口上监听两个进程,即使它们都是TCP。”

蒂姆从他的工作中抬起头来。这次利兹在屏幕上只有一个Python程序,而且是在两个终端中运行:

# server3.py
import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
sock.bind(('127.0.0.1', 8080))
sock.listen()
print(sock.accept())

利兹解释说:“看,这个命令显示什么进程正在监听一个端口”。她输入了lsof -i:8080,然后点击回车。

程序打印:

> lsof -i:8080
COMMAND    PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
python3 174265 liz     3u  IPv4 23850797      0t0  TCP localhost:http-alt (LISTEN)
python3 174337 liz     3u  IPv4 23853188      0t0  TCP localhost:http-alt (LISTEN)

“当你连接到它时会发生什么?”,蒂姆问道,这次他的声音中带着一点真正的好奇心。

“看吧。”

利兹运行了一次netcat localhost 8080,其中一个服务器进程退出,而另一个则继续运行。然后她再次运行,另一个进程退出。

蒂姆的注意力转到了代码上,他把手指放在屏幕附近,读了一遍。莉斯讨厌被弄脏的屏幕,她说:“别紧张!”并把他的手推了回去。“我不会碰它”,他抗议道。他做了一个夸张的表演,让自己的手保持一个安全的距离,他指着setsockopt一行,问道:“嘿,这是什么巫术?”

“那是设置一个套接字选项,允许端口被重复使用。”

“哼,这在教科书上有吗?”

“不知道,我在Stack Overflow上找到的。”

“我不知道你可以这样重复使用一个端口。”

“我也不知道”,她停顿了一下,考虑了一下。“所以操作系统不能只是有一个端口到套接字的表格,它必须是一个端口到套接字的列表的表格。然后为UDP建立第二个表格。也许还有其他协议的。”

“是的,这听起来很对”,蒂姆同意。

“嗯”,利兹说,突然听起来不太确定。

“什么?”

“呃,没关系”,她说,她开始认真地敲打。

蒂姆回到他的任务上,几分钟后,他又划掉了一个问题。他快要完成了,他的神情也放松了一些。利兹将她的笔记本电脑向他倾斜,说“看看这个”。她给他看了两个程序:

# server4.py
import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.2', 8080))
sock.listen()
print(sock.accept())

在它的旁边。

# server5.py
import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.3', 8080))
sock.listen()
print(sock.accept())

“这些不是一样的吗?”蒂姆问道,一边研究它们。

“看一下绑定的IP。”

“哦,所以你是在同一个端口上监听,但有两个不同的IP。这能行吗?”

“似乎是的。而且我可以连接到他们两个。”

利兹运行netcat 127.0.0.2,然后netcat 127.0.0.3,给他看。

蒂姆思考了一下。“所以让我看看。操作系统必须有一个表,从每个端口和IP组合,到一个套接字。实际上,有两个:一个用于TCP,另一个用于UDP。”

“是的”,利兹点点头。“而不是只有一个套接字,可以是多个。但要注意这个。”她把服务器代码中的IP改为 0.0.0.0。

# server6.py
import socketsock socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', 8080))
sock.listen()
print(sock.accept())

“现在,当我运行绑定到127.0.0.2的服务器时,我得到了这个”,她继续说。

Traceback (most recent call last):File "server5.py", line 4, in <module>s.bind(('127.0.0.2', 8080))
OSError: [Errno 99] Cannot assign requested address

“但是”,她总结道,“如果我运行netcat 127.0.0.2 8080,就会连接到0.0.0.0上的服务器”,并给他看。

“对,0.0.0.0意味着'绑定所有本地IP',讲课时没有讲到吗?而以127.开头的地址是本地回环IP,所以它们被它绑定是有道理的。”

“是的,但它是如何工作的?大约有1600万个IP是以127.开头的。它不会用所有的人做一个大表,对吗?”

“我猜不是。”他没有答案,于是改变了话题。“那么无论如何,HTTP服务器的情况如何?”这是个反问句,他知道她没有写过一行实际的任务代码。

“是的,是的”,她回答说,已经潜心于另一个实验。

又过了一段时间。蒂姆刚刚完成他的任务,闲来无事地查看他手机上的时间。他考虑回家去睡他那凹凸不平的宿舍床垫。他感觉了一下,觉得长椅也差不多舒服,于是把头向后仰,靠在高高的垫子椅背上。

他正盯着天花板,半梦半醒间,莉兹捅了捅他,说:“蒂姆,看看这个”。

她给他看了另一个程序。

# server7.py
import socketsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.bind(('::', 8080))
sock.listen()
print(sock.accept())

“看看这个。这是一个IPv6服务器。”

蒂姆打了个哈欠,靠了过来。此时,早晨的阳光已经开始透过他们所坐的长椅后面的窗户出现。另外两个学生在凌晨时分已经悄悄地离开了,店里今天的第一位顾客已经到了,正在等待她的外带咖啡。

“冒号是什么来着?”蒂姆问道。

“这是IPv6中八个零的简称,与IPv4中的 0.0.0.0 含义相同”。

“所以这是说要监听所有本地的IPv6 IP?IPv6是这样工作的吗?”

“是的,基本上是这样。”

她输入netcat "::1" 8080 -v,解释说:“::1是IPv6的回环地址。它就像'家'。”

“所以就像常规IP中的127.0.0.1”

“IPv4。是的,没错。但要注意这个。根据lsof,我只在IPv6上收听,看到了吗?”利兹运行lsof -i :8080,打印出一行。

COMMAND    PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
python3 455017 liz     3u  IPv6 25152485      0t0  TCP *:http-alt (LISTEN)

“但是",利兹继续说,“我可以通过一个IPv4的IP连接到它。”

netcat 127.0.0.1 8080 -v

“哼”,蒂姆喃喃道。“那另一种方式呢?你能从一个IPv6 IP连接到一个IPv4服务器吗?”

“不,看这个。”

她运行了python3 server6.py,然后netcat "::1" 8080 -v,打印出了:

netcat: connect to ::1 port 8080 (tcp) failed: Connection refused

蒂姆问:“如果你试图在IPv6上开始监听8080,而那个IPv4服务器仍在运行,会发生什么?”

利兹给他看,运行python server7.py。

Traceback (most recent call last):File "server7.py", line 4, in <module>s.bind(('::', 8080))
OSError: [Errno 98] Address already in use

“但看看这个”,她说,拉出了另一个代码列表。

# server8.py
import socketsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
sock.bind(('::', 8080))
sock.listen()
print(sock.accept())

她指着setsockopt一行,解释说:“当我添加这个时,我可以从不同的进程监听同一端口上的IPv6和IPv4。”

她运行python server8.py,然后lsof -i :8080。

COMMAND    PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
python3 460409 liz     3u  IPv6 25188010      0t0  TCP *:http-alt (LISTEN)
python3 460813 liz     3u  IPv4 25191765      0t0  TCP *:http-alt (LISTEN)

蒂姆清点了利兹给他看的东西。“所以当你在一个端口上监听时,你实际上是在监听一个端口、一个IP、一个协议、和一个IP版本的组合?”

“是的,除非你在所有的本地IP上监听。如果你在所有IPv6 IP上监听,你也会在所有IPv4 IP上监听,除非你在调用绑定之前特别要求不要这样做。”

“对。因此,操作系统必须有一个从端口和IP对到套接字的哈希图,用于TCP或UDP、IPv4或IPv6的每个组合。”

“到一个套接字的列表”,利兹纠正说。“还记得我是如何监听不止一个的吗?”

“哦,是的。”

“但它还必须处理对所有'家庭'IP的监听,并且能够从一个IPv4 IP上找到一个监听IPv6的套接字。”

“不管怎么说,我得把这个交上去”,蒂姆说着,指了指他手中松散的文件集。“你打算在交稿前完成那个HTTP服务器吗?”

利兹耸耸肩:“我有一个空闲的晚间时间可以利用。”

蒂姆摇了摇头,像极了老父亲般的不赞成。

丽兹翻了个白眼,说:“走吧,蒂姆。”

“下周同一时间?”

“是的。”

推荐阅读:《案例:vivo基于Java技术栈的实时监控系统》


点击下方卡片关注分布式实验室,和我们一起

关注分布式最佳实践

 点击上方卡片关注分布式实验室,掌握前沿分布式技术

新的一年,想一起学习K8s、考CKA证书吗?来,这里有最好的学习方案,线下3天封闭式培训,15人小班课,考不过免费复训。Kubernetes实战班上海站3月25日开班,扫描下方二维码了解详情。

常说的监听某个端口,是什么意思?怎么理解?相关推荐

  1. 6月27日任务 配置Tomcat监听80端口、配置Tomcat虚拟主机、Tomcat日志

    2019独角兽企业重金招聘Python工程师标准>>> 16.4 配置Tomcat监听80端口 1. vim /usr/local/tomcat/conf/server.xml Co ...

  2. Linux 使用NC命令永久监听本地端口

    Linux可以使用nc命令来测试网络端口是否正常,类似于telnet命令,但也可以用nc命令来监听本地端口,支持TCP.UDP协议. 使用案例如下: 1.测试TCP端口 nc -vz ip tcp-p ...

  3. 配置Tomcat监听80端口 配置Tomcat虚拟主机 Tomcat日志

    配置Tomcat监听80端口 • vim /usr/local/tomcat/conf/server.xml Connector port="8080" protocol=&quo ...

  4. 配置ssl证书_Nginx监听443端口配置SSL证书

    1.先去宝塔:https://www.bt.cn/申请一个免费SSL证书:2.点支付订单后,还需要再列表 验证域名,验证域名需要在自己的域名解析配置txt类型字符串值如下,解析值在宝塔SSL申请列表详 ...

  5. Tomcat(1)介绍、jdk安装、安装Tomcat ​ 配置Tomcat监听80端口

    Tomcat介绍 (使用yum 下载安装的openjdk下载以后就可以使用) 以下实验用的是在官网下载程序包. Tomcat是一个中间键,要解析java相关的东西,需要先下载JDK 下载jdk 1.下 ...

  6. 16.4 配置Tomcat监听80端口 16.5/16.6/16.7 配置Tomcat虚拟主机16.8 Tomcat日志

    2019独角兽企业重金招聘Python工程师标准>>> 16.4 配置Tomcat监听80端口 直接访问,使用默认的web服务,需要改动端口为80,如果不是80端口那么访问页面的时候 ...

  7. SNMP功能开发简介 四 net-snmp动态监听自定义端口

    SNMP项目有个需求,就是能够动态改变net-snmp监听的端口而不需要重启设备.关于这个功能,一开始想的是如果端口变更了,那就直接使用pthread_kill 关闭原来的代理线程,然后重新执行线程. ...

  8. 多线程使用SO_REUSEPORT来实现多个socket监听同一个端口

    在十几年前的 FreeBSD 中就存在 SO_REUSEPORT 参数来实现多个 socket 监听同一个端口,来提升服务器的负载,在 Linux 3.9 开始也引入了这个功能,下面就看一下例子. # ...

  9. 配置Tomcat监听80端口配置Tomcat虚拟主机Tomcat日志

    2019独角兽企业重金招聘Python工程师标准>>> 16.4 配置Tomcat监听80端口 Tomcat默认监听8080.8005以及8009端口,日常进行浏览器访问时,需要输入 ...

  10. java监听某端口和ip_Java 通过Socket监听指定服务器(IP)的指定端口,及向指定服务器的指定端口发送信息...

    客户端:向指定端口发送信息 package com.jszc.lottery.modules.longpay.util; import java.io.BufferedReader; import j ...

最新文章

  1. RS485 串口调试如何操作
  2. gitlab不小心把sign-in取消了怎么恢复
  3. 因为权限缺乏导致SAP CRM AET字段删除失败
  4. 管理角色认知-工程师到管理者角色发生了哪些变化?
  5. Flask爱家租房--订单支付(支付过程)
  6. 中国基金投顾蓝皮书2022
  7. linux之at,crontab
  8. 冷知识 —— 容易读错的发音(英文)
  9. 微软邀请IT管理人员及开发人员参加用户体验在线调研
  10. 你需要知道的关于铁氧体磁珠的一切
  11. ZT:【搞笑】某大学生毕业自我鉴定
  12. 计算机体系结构(国防科大)-第四章-指令级并行
  13. DC-DC升压IC测试及EN脚讲解
  14. hdu 4960 Another OCD Patient(动态规划)
  15. python手机触屏代码_PyGame任何触摸屏
  16. 区块链开发中使用最流行的编程语言
  17. Soul源码总结-01-15
  18. logo计算机编程简单指令,pc logo 基本绘图命令
  19. sublime text3解决Gosublime无法自动补全代码
  20. python荣联云通讯短信平台

热门文章

  1. 什么叫状态服务器 博客,pending是什么意思?HTTP Status pending (进程信号的未决状态)详解...
  2. APP在推广之渠道为王(二 )
  3. 数据销毁、硬盘销毁的方法及安全性分析
  4. 阿里Leader都干些啥
  5. Routh-Hurwitz Criterion 劳斯稳定判据
  6. facebook登陆ios
  7. 广州物流展相关的个人总结
  8. 统一身份认证(CAS)中文文档 请多指教
  9. 四千个厂商默认账号密码 默认登录凭证
  10. 模拟器使用Fiddler代理后,浏览器报错【该网站的安全证书有问题】解决方法