烦请楼主告知,知乎提供https://www.zhihu.com服务的服务器进程号(Process ID)是多少?
大概率无法告知,因为进程号是操作系统给服务器进程动态分配的(Process ID 0 使用fork创建 ),当前的进程号可能是1949,一旦重启可能就变成2024。而且服务器集群(Cluster)每一个服务器使用的进程号大概率不一样,究竟用哪一个进程号识别应用程序?
不需要知道服务器的进程号,照样可以和它通信,因为它使用著名端口号443,准确的说是TCP协议的443端口。
浏览器或者APP拿到这个URI= https://www.zhihu.com,立马就解析出Port =443。至于怎么解析,上篇文章有详细介绍。
通过端口号来识别应用程序,无论服务器如何重启、无论集群里有多少服务器,只要一个端口号443就可以访问它们,是不是方便多了?!
Q2: 网络通信中为何要通过绑定端口号来识别应用程序?
通过IANA这个全球组织,将一些端口预留给互联网服务,方便客户端与服务器勾连起来(Socket Pair)。
如果把服务器的端口号443、以及IP=1.1.1.1、通过bind命令在Listening_Socket上:
Listening_Socket.local_ip =1.1.1.1;
Listening_Socket.local_port =443;
来自远方的客户端IP= 2.2.2.2,Connect上述的服务器,操作系统分配端口号 =9999,Client_Socket绑定的transport信息如下:
Client_Socket.local_ip =2.2.2.2;
Client_Socket.local_port =9999;
经过大名鼎鼎的TCP三次握手,TCP连接成功建立,则双方的Socket信息update如下:
Listening_Socket.local_ip =1.1.1.1;
Listening_Socket.local_port =443;
Listening_Socket.peer_ip =2.2.2.2;
Listening_Socket.peer_port =9999;
Client_Socket.local_ip =2.2.2.2;
Client_Socket.local_port =9999;
Client_Socket.peer_ip =1.1.1.1;
Client_Socket.peer_port =443;
如果把高亮的服务器Socket比作插座,Client Socket比作插头,一旦Socket Pair(4行信息)配对成功,一个TCP连接就成功建立了。
通信双方就可以在Socket Pair上发送(send)、接收(receive)数据了。
TCP仅仅是依赖于端口号(local)来识别应用程序的吗?
TCP依赖于Local IP + Local Port来识别应用程序的吗?
不完全是。仅仅适用于接收Client第一个SYN连接报文。
TCP依赖于Local IP + Local Port+ Peer IP + Peer Port来识别应用程序,即上文双方的Socket Pair 各自4行信息。
这里的识别程序可以理解为,网卡每秒接收到几百万的Packet,途经TCP的处理之后,将Packet放入 which socket的receive buffer?
无论Client还是Server端,依据的是Socket Pair 4元组信息,参照上文的详细描述。
通俗地说,Socket Pair 4元组信息,其实就是需要同时满足的4个约束条件的渔网,将属于服务器/客户端的鱼(packet)捞上来,做进一步的处理。
再让目光聚焦于“仅仅适用于接收Client第一个SYN连接报文“,什么意思?
服务器的Listening Socket仅仅用于TCP连接建立,准确地说,专门用于等待Client的SYN报文的到来,类似于姜太公钓鱼。
在Client的SYN报文到达之前,服务器是无法知晓Cient的transport连接信息的(Peer IP + Peer Por)的,
Listening_Socket.local_ip =1.1.1.1;
Listening_Socket.local_port =443;
Listening_Socket.peer_ip =0;
Listening_Socket.peer_port =0;
故服务器无法使用4个约束条件过滤网捞鱼。而只能使用2个条件捞鱼:
Listening_Socket.local_ip =1.1.1.1;
Listening_Socket.local_port =443;
捞上来的Packet自然由服务器进程处理。服务器一旦接收客户端的SYN,立马就获知了Client的IP、端口号,服务器就有了以下信息:
-
Listening_Socket.local_ip =1.1.1.1;
-
Listening_Socket.local_port =443;
-
Listening_Socket.peer_ip =2.2.2.2;
-
Listening_Socket.peer_port =9999;
如果服务器真的这么做,那么服务器只能连接一个client(2.2.2.2),来自其它客户端的Packet是无法捞进Listening Socket的。
一旦收到client 的SYN报文,按照Listening Socket模板创建一个新的socket, 假设名字为Connected_Socket,则有
-
Connected_Socket.local_ip =1.1.1.1;
-
Connected _Socket.local_port =443;
-
Connected _Socket.peer_ip =2.2.2.2;
-
Connected _Socket.peer_port =9999;
并fork一个Child Process与这个Connected_Socket关联起来。换句话说,满足该4元组约束条件的packet将交由Child Process处理。
而原先的服务器进程继续在Listening Socket继续Listening新的TCP连接请求。
-
Listening_Socket.local_ip =1.1.1.1;
-
Listening_Socket.local_port =443;
-
Listening_Socket.peer_ip =0;
-
Listening_Socket.peer_port =0;
这里的‘0‘代表wildcard,表示为完全匹配。类似于默认路由的0.0.0.0,匹配所有路由。
上文的关键词,Listening Socket与Connected Socket的区别与联系。
一个进程号(Process ID)唯一一个进程(Process),一一映射关系。
一个进程可以通过socket接口创建任意多个socket,一个socket可以bind一个端口。比较恰当的例子就是http 1.0客户端,一个TCP连接只能用于一次HTTP交易,为了加速下载网页,同时建立多个TCP连接,并行传输http页面。这里是一对多映射关系。
对于UDP来说,可以一对一,也可以一对多,一个进程可以创建任意多个Socket,并bind不同的端口号。也可以多对一,即多个进程号映射一个端口号。比如多个进程共同接收特定UDP端口号的组播。
对于TCP来说,可以一对一,也可以一对多(参考Q3)。
-
Client_Socket.local_ip =2.2.2.2;
-
Client_Socket.local_port =9999;
-
Client_Socket.peer_ip =1.1.1.1;
-
Client_Socket.peer_port =443;
-
Client_Socket.local_ip =2.2.2.2;
-
Client_Socket.local_port =9999;
-
Client_Socket.peer_ip =8.8.8.8;
-
Client_Socket.peer_port =443;
一个连接1.1.1.1 ,另外一个连接8.8.8.8,即使使用共同的端口号9999,渔网一样可以区分出来。
-
如果2个客户端进程让操作系统分配端口号,操作系统会避免使用相同端口号。
-
如果2个客户端进程使用bind强制绑定相同端口号9999,则第一个bind的进程会成功,第二个bind的进程会失败。背后的逻辑是,万一第二个进程连接相同服务器、相同端口号,则2者的4元组则一模一样,无法区分彼此,故拒绝!