Python套接字的端口是随机的吗?
我正在学习Python的网络编程,但对网站上示例代码的结果感到很困惑(在这里找到)。
我唯一做的修改是把服务器中的socket.gethostname()
替换成我服务器的本地IP,这样我就可以在两台电脑上运行这个程序。
当我尝试连接时,按照示例连接到12345端口,我得到了这个输出:
Got connection from ('10.0.1.10', 37492)
这让我觉得它是连接在37492端口上。我希望它能连接到我指定的端口,这样我就可以进行端口转发。我是不是理解错了,还是需要额外的命令来指定端口?
编辑:我正在上传我的代码:
Client.py
#!/usr/bin/python # This is client.py file
import socket # Import socket module
s = socket.socket() # Create a socket object
host = socket.gethostname() # Get local machine name
port = 12345 # Reserve a port for your service.
s.connect(("10.0.1.42", port))
print s.recv(1024)
s.close # Close the socket when done
Server.py
import socket
s = socket.socket() # Create a socket object
host = "10.0.1.42" # Get local machine name
port = 12345 # Reserve a port for your service.
s.bind((host, port)) # Bind to the port
s.listen(5) # Now wait for client connection.
while True:
c, addr = s.accept() # Establish connection with client.
print 'Got connection from', addr
c.send('Thank you for connecting')
c.close() # Close the connection
4 个回答
这里的关键词是“from”。这表示客户端连接的端口,而12345是你的服务器正在监听的端口,也是客户端要连接的端口。
出现的这个信息是来自服务器的。它只是告诉你,客户端的端口37492已经成功连接上了。
事情是这样的:
你的服务器(server.py)在12345端口上监听。你的客户端(client.py)连接到了服务器的12345端口。TCP连接总是发生在两个端口之间——一个是源端口,一个是目标端口。换句话说,从客户端的角度来看,12345是目标端口,而37492是源端口。也就是说,客户端是从它本地的37492端口连接到远程服务器的12345端口。
如果你想设置端口转发,完全可以做到,因为服务器监听的端口是固定的(12345),而客户端的源端口在这种情况下并不重要。
你现在到了需要了解协议复用的阶段,真不错。
想象一下TCP/IP协议栈。一个应用程序通过把应用层的数据传递给传输层(也就是端到端的层),再由传输层传递给网络层(也叫互联网层)。网络层会尽力把数据包送到目标IP地址,但并不能保证一定能到达。这一过程涉及到多个路由器的合作,它们会通过相互交流来动态更新自己的路由表。每个路由器之间的交流都是通过某种物理传输方式进行的(比如ISDN、以太网、PPP等)。在TCP/IP中,创建数据包和传输相应的比特流被视为一个“子网络”层,但在需要区分OSI模型的物理层(第一层)和数据链路层(第二层)时,这个层次会被拆分开来,比如在DHCP协议中。
当TCP和UDP被设计出来时,设计者设想每个服务器会在特定的端口上监听。这通常会有一个固有限制,就是一个端口只能处理一种版本的服务协议(不过像HTTP这样的协议会特别注意向后兼容,以便旧的服务器和客户端能够与新的版本互通)。通常会有一个叫做portmapper的服务在111端口上运行,它允许服务器注册它们正在使用的端口号,客户端可以通过服务(程序)编号和协议版本来查询注册的服务器。这是由Sun公司设计的RPC协议的一部分,目的是扩展监听端口的范围,而不仅仅是那些预先分配的端口。因为预分配的端口是从1到1023编号的,而这些端口通常(在合理的操作系统上)需要较高的权限,所以RPC还允许非特权的服务器进程,并且让服务器能够响应多个版本的网络应用协议,比如NFS。
无论服务器端是如何工作的,网络层必须有某种方式来决定把特定的数据包送到哪个TCP连接(或UDP监听器)。对于传输层也是一样(这里我只考虑TCP,因为它是面向连接的——UDP也是类似的,但它不在乎丢包)。假设我是一台服务器,收到了来自同一台机器上两个不同客户端进程的连接。如果客户端使用的是同一版本的协议,或者服务只在一个端口上监听,那么目标(IP地址,端口号)就是相同的。
服务器的网络层查看到达的IP数据报,发现它是发给特定服务器端口的。于是它把数据包交给传输层(网络层上面的那一层)。由于这个服务器很受欢迎,可能会有来自同一台机器上不同客户端进程的多个连接。这就是短暂端口的魔力所在。
当客户端请求一个端口来连接服务时,TCP层会保证在客户端进程使用这个端口期间,机器上的其他进程(技术上说是那个接口,因为不同接口有唯一的IP地址,但这是个细节)不会被分配到同样的端口号。
所以,协议复用和分离依赖于五个信息:
(发送者IP,发送者端口,协议,接收者IP,接收者端口)
协议是IP头中的一个字段,源IP和目标IP地址也是。发送和接收的端口号在传输层的段头中。
当一个数据包到达时,来自同一客户端(端点)的不同短暂端口的唯一性使得传输层能够区分来自同一客户端IP地址和端口的不同连接到同一服务器IP地址(这是分离的最坏情况),通过它们的源IP地址和端口来区分。协议也被包括在内,以确保TCP和UDP的流量不会混淆。TCP/UDP对短暂端口唯一性的限制保证了任何服务器只能接收来自特定组合的(IP地址,端口号)
的一个连接,这就是允许来自同一台机器的连接被分离成对应于不同来源的独立流的原因。
在Python中,当你将一个套接字连接到远程端点时,socket.accept()
调用会返回远程端点的(IP地址,端口号)对。你可以用这个来发现是谁在和你通信,但如果你只是想回应,可以直接用write()
这个套接字。