Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Windows 为什么套接字可以连接()到它自己的临时端口?_Windows_Sockets_Port_Winsock - Fatal编程技术网

Windows 为什么套接字可以连接()到它自己的临时端口?

Windows 为什么套接字可以连接()到它自己的临时端口?,windows,sockets,port,winsock,Windows,Sockets,Port,Winsock,如果我使用自动分配的临时端口(5000–65534)范围内的端口连接到本地主机,我可以可靠地将Winsock套接字连接到自身。具体来说,Windows似乎有一个系统范围的滚动端口号,这是它将尝试分配为客户端套接字的本地端口号的下一个端口。如果我创建套接字,直到分配的端口号刚好低于目标端口号,然后重复创建一个套接字并尝试连接到该端口号,我通常可以让套接字连接到自身 我首先让它发生在一个应用程序中,该应用程序反复尝试连接到本地主机上的某个端口,当服务未侦听时,它很少成功地建立连接并接收它最初发送的消

如果我使用自动分配的临时端口(5000–65534)范围内的端口连接到本地主机,我可以可靠地将Winsock套接字连接到自身。具体来说,Windows似乎有一个系统范围的滚动端口号,这是它将尝试分配为客户端套接字的本地端口号的下一个端口。如果我创建套接字,直到分配的端口号刚好低于目标端口号,然后重复创建一个套接字并尝试连接到该端口号,我通常可以让套接字连接到自身

我首先让它发生在一个应用程序中,该应用程序反复尝试连接到本地主机上的某个端口,当服务未侦听时,它很少成功地建立连接并接收它最初发送的消息(这恰好是一个Redis
PING
命令)

例如,在Python中(运行时不侦听目标端口):

导入套接字
目标端口=49400
def mksocket():
返回socket.socket(socket.AF_INET、socket.SOCK_流、socket.IPPROTO_TCP)
尽管如此:
sock=mksocket()
袜子绑定('127.0.0.1',0))
主机,端口=sock.getsockname()
如果端口>目标端口-10和端口<目标端口:
打破
打印端口
当端口<目标端口时:
sock=mksocket()
错误=无
尝试:
sock.connect(('127.0.0.1',目标端口))
除socket.error外,e:
err=e
主机,端口=sock.getsockname()
如果出现错误:
打印“无法连接到端口%d,使用的本地端口%d:%s%”(目标端口,端口,错误)
其他:
打印“已连接到端口%d,已使用本地端口%d”(目标端口,端口)
在我的Mac电脑上,这最终会终止,因为
无法连接到端口49400,使用本地端口49400
。在我的Windows 7计算机上,已成功建立连接,并打印连接到端口49400的
,使用本地端口49400
。生成的套接字接收发送给它的任何数据

这是Winsock中的错误吗?这是我代码中的错误吗

编辑:以下是的屏幕截图,其中显示了有问题的连接:


这是代码中的一个逻辑错误

首先,只有更新版本的Windows使用5000–65534作为临时端口。旧版本使用的是1025-5000

您正在创建多个显式绑定到随机临时端口的套接字,直到绑定的套接字小于目标端口10个端口为止。但是,如果这些套接字中的任何一个碰巧实际绑定到了实际的目标端口,则忽略该端口并继续循环。因此,您可能会或可能会以绑定到目标端口的套接字结束,并且您可能会或可能不会以实际小于目标端口的最终
port
值结束

在此之后,如果
端口
恰好小于目标端口(这是不保证的),则在调用
connect()
(如果
bind()在内部执行一个隐式的
bind()
)时,您将创建更多的套接字,这些套接字将隐式地绑定到不同的随机可用临时端口
尚未调用),因为这些端口已在使用中,无法再次使用,因此它们都不是显式绑定到的临时端口

在任何时候,您都没有从临时端口连接到同一临时端口的给定套接字。除非另一个应用程序恰好绑定到您的目标端口,并且正在该端口上进行侦听,否则
connect()
无法成功连接到您创建的任何套接字上的目标端口,因为它们都未处于侦听状态。并且
getsockname()
在未绑定的套接字上无效,并且如果
connect()
失败,则不保证绑定连接套接字。因此,考虑到您所展示的代码,您认为正在发生的症状实际上是不可能的。你的日志只是做出了错误的假设,因此记录了错误的事情,给了你一种虚假的存在状态

尝试类似的方法,您将看到真正的端口是什么:

import socket

TARGET_PORT = 49400

def mksocket():
    return socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)

while True:
    sock = mksocket()
    sock.bind(('127.0.0.1', 0))
    host, port = sock.getsockname()
    print 'Bound to local port %d' % (port)
    if port > TARGET_PORT - 10 and port < TARGET_PORT:
        break

if port >= TARGET_PORT:
    print 'Bound port %d exceeded target port %d' % (port, TARGET_PORT)
else:
    while port < TARGET_PORT:
      sock = mksocket()
      # connect() would do this internal anyway, so this is just to ensure a port is available for logging even if connect() fails
      sock.bind(('127.0.0.1', 0))
      err = None
      try:
          sock.connect(('127.0.0.1', TARGET_PORT))
      except socket.error, e:
          err = e
      host, port = sock.getsockname()
      if err:
          print 'Unable to connect to port %d using local port %d' % (TARGET_PORT, port)
      else:
          print 'Connected to port %d using local port %d' % (TARGET_PORT, port)
导入套接字
目标端口=49400
def mksocket():
返回socket.socket(socket.AF_INET、socket.SOCK_流、socket.IPPROTO_TCP)
尽管如此:
sock=mksocket()
袜子绑定('127.0.0.1',0))
主机,端口=sock.getsockname()
打印“绑定到本地端口%d%”(端口)
如果端口>目标端口-10和端口<目标端口:
打破
如果端口>=目标端口:
打印“绑定端口%d超出目标端口%d%”(端口,目标端口)
其他:
当端口<目标端口时:
sock=mksocket()
#connect()无论如何都会在内部执行此操作,因此这只是为了确保即使connect()失败,端口也可用于日志记录
袜子绑定('127.0.0.1',0))
错误=无
尝试:
sock.connect(('127.0.0.1',目标端口))
除socket.error外,e:
err=e
主机,端口=sock.getsockname()
如果出现错误:
打印“无法使用本地端口%d%”(目标端口,端口)连接到端口%d”
其他:
打印“使用本地端口%d%”(目标端口,端口)连接到端口%d”

这似乎是一个“同时启动”,如第3.4节所述。参见图8。请注意,任何一方在任何阶段都不处于监听状态。在您的情况下,两端都是相同的:这将导致它完全按照RFC中的描述工作。

当套接字被覆盖时,Python正在关闭套接字,因此理论上可以重用端口(尽管它们最终处于TIME\u WAIT状态,并且可能无论如何都不会被重用)。我知道它是未定义的,如果这个脚本每次都执行我希望它执行的操作,它通常的行为就像我描述的那样。至少在Windows上,我无法在连接之前调用
getsockname()
,但我可以将其称为
import socket

TARGET_PORT = 49400

def mksocket():
    return socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)

while True:
    sock = mksocket()
    sock.bind(('127.0.0.1', 0))
    host, port = sock.getsockname()
    print 'Bound to local port %d' % (port)
    if port > TARGET_PORT - 10 and port < TARGET_PORT:
        break

if port >= TARGET_PORT:
    print 'Bound port %d exceeded target port %d' % (port, TARGET_PORT)
else:
    while port < TARGET_PORT:
      sock = mksocket()
      # connect() would do this internal anyway, so this is just to ensure a port is available for logging even if connect() fails
      sock.bind(('127.0.0.1', 0))
      err = None
      try:
          sock.connect(('127.0.0.1', TARGET_PORT))
      except socket.error, e:
          err = e
      host, port = sock.getsockname()
      if err:
          print 'Unable to connect to port %d using local port %d' % (TARGET_PORT, port)
      else:
          print 'Connected to port %d using local port %d' % (TARGET_PORT, port)