热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

网络编程(一):端口那些事儿

TCP和UDP协议都存在一个叫做端口的东西,但端口却不是IP协议的一部分。端口被设计出来主要是为了给协议栈和应用对应:协议栈用端口号将数据分配给不同

TCP和UDP协议都存在一个叫做端口的东西,但端口却不是IP协议的一部分。

端口被设计出来主要是为了给协议栈和应用对应:

  • 协议栈用端口号将数据分配给不同的应用层程序
  • 应用层程序用端口号去区分不同的连接,参见之前提到过的“四元组”

TCP和UDP协议都使用了端口号(Port number)的概念来标识发送方和接收方的应用层。 对每个TCP连接的一端都有一个相关的16位的无符号端口号分配给它们。 即使是UDP这种没有连接的协议,依旧有一个16位的无符号端口号。 可能的、被正式承认的端口号有 2^16 -1 = 65535 个。

三类端口

端口被分为三类:著名端口、监听端口和动态端口。

  • 著名端口是由因特网赋号管理局(IANA)来分配的,并且通常被用于系统进程。 IANA对于端口号的分配见这里 Service Name and Transport Protocol Port Number Registry 。 系统的/etc/services也有相应端口和服务名的对应,主要是用来给netstat、nmap 等系统命令做端口名反解用。

    著名的应用程序作为服务器程序来运行,并侦听经常使用这些端口的连接。 这些端口的一个显著特征就是限定在0~1023,并且在Linux、UNIX平台均需要 Root权限才能监听这些端口。

    在UNIX刚刚兴起的年代,服务器资源是十分稀缺的, 通常一台服务器上会有很多的用户,同时这台服务器往往还兼任一个学院、公司的邮件、 网站等服务。为了保证这些服务的端口不被普通用户占用, 当时UNIX的设计者就把使用这些端口的权限限制在系统管理员(Root)手里。

    常见的`著名端口`有:FTP:21、SSH:22、SMTP:25、HTTP:80、HTTPS:443等。

  • 监听端口通常被用来运行各种用户自己写的服务,服务监听在这些端口下不需要特别的权限。

    • BSD使用的监听端口范围是1024到4999。
    • IANA建议49152至65535作为“监听端口”。
    • 许多Linux内核使用32768至61000范围。 配置文件 /proc/sys/net/ipv4/ip_local_port_range 有当前系统设定。
  • 动态端口通常被用来在主动发起连接时随机分配使用,在任何特定的TCP连接外不具有任何意义。 这是由于TCP等协议是通过四元组来区分不同的网络连接。 当本机主动发起TCP连接的时候如果目的IP、目的端口、本地IP都是一样的, 只能通过占用不同的本地端口来区分不同的连接。

    0~65535除去上述著名端口、监听端口两种端口号,剩下的端口都是备用的动态端口。 所以在某些特殊用途的需要主动发起大量连接的服务器上(例如:爬虫、代理), 需要调整 /proc/sys/net/ipv4/ip_local_port_range 的数值,来保留更多的 动态端口以供使用。


0号端口

端口号里有一个极为特殊的端口,各种文档书籍中都鲜有记载,就是0号端口。

在IANA官方的标准里0号端口是保留端口。

也就是说无论是TCP还是UDP网络通信,0号端口都是不能使用的。

然而,标准归标准,在UNIX/Linux网络编程中0号端口被赋予了特殊的涵义:

如果在bind绑定的时候指定端口0,意味着由系统随机选择一个可用端口来绑定。

用Python实现一个获取可用监听端口的示例:

def findFreePort():"""函数返回值是当前可用来监听的一个随机端口。"""import sockets = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.bind(('localhost', 0))# 用getsockname来获取我们实际绑定的端口号addr, port = s.getsockname()# 释放端口s.close()return port

网络地址转换NAT

既然说到了端口,不得不提一下NAT。

NAT是"Network Address Translation"的缩写,直译就是网络地址转换。 1990年代中期,为了应对IPv4地址短缺,NAT技术流行起来。

WikiPedia的解释为:

在一个典型的配置中,一个本地网络使用一个专有网络的指定子网 (比如192.168.x.x或10.x.x.x)和连在这个网络上的一个路由器。 这个路由器占有这个网络地址空间的一个专有地址(比如192.168.0.1), 同时它还通过一个或多个因特网服务提供商提供的公有的IP地址(叫做“过载”NAT) 连接到因特网上。当信息由本地网络向因特网传递时,源地址被立即从专有地址转换为公用地址。 由路由器跟踪每个连接上的基本数据,主要是目的地址和端口。当有回复返回路由器时, 它通过输出阶段记录的连接跟踪数据来决定该转发给内部网的哪个主机; 如果有多个公用地址可用,当数据包返回时,TCP或UDP客户机的端口号可以用来分解数据包。 对于因特网上的一个系统,路由器本身充当通信的源和目的地址。

这个技术能够被广泛使用还要感谢当时端口号的记录字段是2Bytes而不是1Byte。

NAT技术的广泛应用也给很多应用带来了极大的麻烦: 处于NAT网络环境内的服务器很难被外部的网络程序主动连接,受这一点伤害最大的莫过于: 点对点视频、语音、文件传输类的程序。

当然我们聪明的工程师经过长时间的努力,发明了“NAT打洞”技术,一定程度上解决了此类问题。

如果没有他们的努力,我们现在各种QQ视频、微信实时语音、网络电话都是需要用户连接到 服务商的服务器上进行数据传输。这样对服务商的网络消耗将是十分巨大的, 服务质量也是很难以提高的,具体的技术实现,我们以后再表。

多进程端口监听

我们都有一个计算机网络的常识:不同的进程不能使用同一端口。

如果一个端口正在被使用,无论是TIME_WAIT、CLOSE_WAIT、还是ESTABLISHED状态。 这个端口都不能被复用,这里面自然也是包括不能被用来LISTEN(监听)。

但这件事也不是绝对的,之前跟大家讲进程的创建过程提到过一件事: 当进程调用fork(2)系统调用的时候,会发生一系列资源的复制,其中就包括句柄。 所以,在调用fork(2)之前,打开任何文件,监听端口产生的句柄也将会被复制。

通过这种方式,我们就可以达成"多进程端口监听"。

但,这又有什么用呢?

我们大名鼎鼎的Nginx就是通过这种手法让多个进程同时监听在HTTP的服务端口上的, 这么做的好处就在于,当外部请求到达,Linux内核会保证多个进程只会有一个accept(2) 成功,这种情况下此端口的服务可用性就和单个进程存在与否无关。 Nginx正是利用这一点达成“不停服务reload、restart”的。

SO_REUSEADDR

要说SO_REUSEADDR,我们需要先需要说一段历史: 记得大学的时候面试我们学校的“星辰工作室”,有一个问题就是

为什么有时候重启Apache会失败,报“Address already in use”?

当时答得不太好,不太明白这个问题的关键点在哪里,后来逐渐明白了。

TCP的原理会导致这样的一个结果:

主动close socket的一方会进入TIME_WAIT,这个状况持续的时间取决于三件事:

  • TCP关闭连接的五次挥手包什么时候到达
  • SO_LINGER的设置
  • /proc/sys/net/ipv4/tcp_tw_recycle 和 /proc/sys/net/ipv4/tcp_tw_reuse 的设置

总之默认情况下,处于TIME_WAIT状态的端口是不能用来LISTEN的。 这就导致,Apache重启时产生80端口TIME_WAIT,进而导致Apache再次尝试LISTEN失败。

在很多开源代码里我们会看到如下代码:

int reuseaddr = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int));

有了上面这段神奇的代码,就不会出现上面的惨剧。但SO_REUSEADDR的作用不仅限于上述

Linux 的 SO_REUSEADDR 设置为 1 有四种效果:

  1. 当端口处在TIME_WAIT时候,可以复用监听。

  2. 可以允许多个进程监听同一端口,但是必须不同IP。

    这里说的比较隐晦,如果进程A监听0.0.0.0:80,B进程可以成功监听127.0.0.1:80, 顺序反过来也是可以的。

  3. 允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的IP地址不同。

  4. 使用UDP时候,可以允许多个实例或者单进程同时监听同个端口同个IP。


推荐阅读
  • 开发笔记:Python之路第一篇:初识Python
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python之路第一篇:初识Python相关的知识,希望对你有一定的参考价值。Python简介& ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • 本文介绍了网络编程的要点,包括InetAddress类获取IP地址的方法,IP地址的定义和表示方法,IPv4和IPv6的区别,以及网络通信协议中的端口和协议类型。 ... [详细]
  • 1、Ipv4只能用于内网,外网只能用2、DNS:把域名解析成ip地址3、MAC地址就是物理地址(网卡序列号)   IP地址:电脑序列号4、不同电脑,微信之间互相通信,靠的是端口;  ... [详细]
author-avatar
低调pasta_730
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有