连接失败后,socket 是否会变得无法使用?

9
在Steven的《The Socket: Networking API, Third Edition》第4章第4.3段中,作者指出如下内容:
如果连接失败,则该套接字将不再可用,并且必须关闭。我们不能再次在该套接字上调用connect。
有人知道上述声明背后的原因吗?
在我的实验中,我编写了一个简单的TCP客户端,在主机A上运行,以及一个简单的TCP服务器,在主机B上运行。 TCP客户端会一直尝试连接到主机B上的TCP服务器。
所以,我启动了主机B上的服务器。从主机中拔出网络线。然后我在主机A上启动了客户端。在同一套接字上进行了大约9次不成功的连接尝试之后,我只需将网络线插回服务器主机即可。客户端成功连接并快乐地以80K / sec发送消息。
在另一个实验中,在初始成功连接和交换数百万条消息后,我从服务器主机中拔出电缆。然后,几分钟后,我连接了电缆,消息流在同一套接字上恢复。

但是你在第一种情况下是否在同一个套接字上调用了多次connect()?你在连接建立后拉电缆的第二个示例与你的问题完全无关。 - user207421
5个回答

8

POSIX 2001在一篇信息性的文章中表示:

如果connect()失败,套接字的状态是未指定的。符合规范的应用程序应该在尝试重新连接之前关闭文件描述符并创建一个新的套接字。

因此,您引用的段落与该规范相一致。它在您的计算机上运行并不意味着您的程序具有可移植性。


我的帖子是关于“有人知道上述声明背后的原因吗”... 规格可能与Steven一致,但是背后的原因是什么? - Jimm
1
@Jimm 谁知道呢?规范大多数情况下只是编码了当时存在的实现的最小公共因子。可能有一些网络堆栈不允许在失败后重新发出connect()调用,因此规范的编写方式没有提供任何保证。 - Artefacto
我认为你对最小公倍数理论是正确的。由于套接字是协议无关的构造,它遵循LCD理论。因此,即使底层协议(在这种情况下是TCP)可能不会使套接字处于不一致状态,但其他协议可能会... - Jimm
@HenningMakholm 我很想看到这个失败。失败意味着API按照作者的期望工作。我希望在你的例子中,API会给出一些错误,说明SYN计数器已达到最大值。但是,让我困扰的是,规范会说一件事,但实现却完全相反。 - Jimm
3
@Jimm:你并没有看到你正在使用的实现“完全相反地工作”。未指定行为没有可能有“完全相反”的行为 - 当规范没有说明当connect()被调用两次时实现必须如何响应时,实现可以选择忘记先前的连接尝试,或者失败并出现错误消息、段错误,或者终止所有进程并删除您的主目录,或者连接到另一个服务器,或者似乎在随机破坏您的地址空间的同时工作。这取决于它本身而不是你! - hmakholm left over Monica
显示剩余2条评论

3

回答您的具体问题...

简单来说,有很多TCP实现。虽然有些支持在一个连接失败后进行另一个connect()调用,但其他一些将具有状态信息,这样做会变得不可靠。

为了安全起见,必须有某种reset()操作,可以将套接字返回到原始状态。由于这未包含在最初(或任何后续)的TCP实现中,唯一剩下的选择是关闭并重新打开。

因此,POSIX标准(以及您的书籍,该书籍可能使用POSIX标准作为参考)告诉您要按照这样做,以便能够与所有支持TCP/IP的操作系统一起使用。否则会使一些现有实现失效。

此外,新的实现可以通过不必担心在尝试失败后启动新连接来简化其实现;这会导致更少的代码和更少的错误产生的机会。


2
这可能源于 connect 的 man 页面,其中写到:

一般来说,基于连接的协议套接字只能通过 connect() 进行一次成功连接;而基于非连接的协议套接字则可以使用 connect() 进行多次连接以更改它们的关联。

这意味着您不能仅仅重新连接一个基于连接(即 TCP)的套接字。但是,我没有看到它表示任何关于失败的 connect() 意味着我们不能回收 FD。
如果连接中断,则 TCP 会尝试恢复连接,这是 TCP 的特性。

我猜测手册中的关键词是“更改它们的关联”。通过关联,我理解为服务器地址和端口。只要您尝试重新连接到相同的地址和端口,这就不应该成为问题。 - Jimm

1

你确定你正在使用相同的套接字,而不是尝试连接到与之前相同地址的套接字吗?

即使这就是你正在做的事情,你实验的特定操作系统允许重用已经无法连接的套接字,也并不意味着所有其他实现套接字API的操作系统(或同一操作系统的早期/后期版本)都会同样宽容,因此你有可能产生微妙的非可移植代码。

当你做一些API合同没有承诺可以工作的事情时,通常不知道会发生什么。其中一种可能的反应是它将似乎可以工作——直到一个付费客户尝试在他的机器上运行你的代码的那一刻。

在一个相当罕见的错误情况下,为了避免这种风险,你认为值得花费关闭套接字和重新创建套接字的代价吗?


0

在 macOS Sierra 上,Python 2 中的套接字会变得无法使用。

$ python
>>> import socket
>>> s = socket.socket()
>>> s.connect(('127.0.0.1', 8888))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 61] Connection refused

>>> s.connect(('127.0.0.1', 8888))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 22] Invalid argument

>>> s.close()
>>> s.connect(('127.0.0.1', 8888))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 174, in _dummy
    raise error(EBADF, 'Bad file descriptor')
socket.error: [Errno 9] Bad file descriptor

如果您正在通过循环连接到服务器等待其启动,则可能会出现这种情况。在这种情况下,在 macOS 上,您无法重用单个套接字来执行此操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接