9

In Chapter 4, paragraph 4.3 of Steven's "The Socket: Networking API, Third Edition", the author states the following

If connect fails, the socket is no longer usable and must be closed. We cannot call connect again on the socket.

Does anyone know the reason behind the above statement?

In my own experiments, i wrote a simple TCP client, that would run on host A and a simple TCP server, that would run on host B. The TCP client would attempt to connect to the TCP server on Host B forever.

So, I started the server on host B. Pulled the network wire from the host. Then I started the client on host A. After about 9 unsuccessful connect attempts on the same socket, I simply plugged the network wire back into the server host. The client connected successfully and happily sends messages at 80K/sec.

In yet another experiment, I pulled the wire from the server host, after a initial successful connect and few million messages exchanges after. Then, after few minutes, I connected the wire and message flow resumed on the same socket.

1
  • But did you call connect() more than once on the same socket, in the first case? Your second example of pulling the cable after the connection was made is completely irrelevant to your question. Commented Oct 28, 2012 at 20:51

5 Answers 5

8

POSIX 2001 says in an informative section:

If connect() fails, the state of the socket is unspecified. Conforming applications should close the file descriptor and create a new socket before attempting to reconnect.

So the passage you quote is congruent with this specification. The fact it works on your machine does not mean your program is portable.

Sign up to request clarification or add additional context in comments.

7 Comments

My post is about "Does anyone know the reason behind the above statement"... Specs maybe congruent with steven, but what is the reason behind it?
@Jimm Who knows? Specifications most of the time just codify the least common denominator of the implementations that exist at the time. Possibly, there were some networking stacks that did not allow connect() calls to be reissued after a failure and so the specification was written in a way it gave no guarantees.
I think you are correct with the least common denominator theory. As socket is protocol independent construct, it adheres to the LCD theory. So, even though the underlying protocol, TCP in this case, would perhaps not leave socket in an inconsistent state, some other protocol might...
@Jimm: Why do you say that TCP "would not leave the socket in an inconsistent state"? It is entirely plausible, for example, that an implementor of the socket API would only initialize its "number of SYN packets already sent" counter when the socket is created -- since he is being promised that you will not call connect() twice -- and therefore an attempt to connect() after this counter had already maxed out would fail immediately without any network traffic.
@Jimm: You're not seeing the implementation you're using "working the exact opposite". There is no possible "opposite" of unspecified behavior -- when the spec does not say anything about how the implementation must react when connect() is called twice, the implementation is perfectly allowed to choose to forget the earlier connection attempt. Or it might fail with an error message, or segfault, or terminate all your processes and delete your home directory, or connect to a different server, or appear to work while randomly clobbering your address space. That's up to it, not you!
|
3

To answer your specific question...

Quite simply, there are a great number of TCP implementations out there. While some may support doing another connect() call after one fails, others will have state information that would make doing such unreliable.

To be safe, there would have to be some sort of reset() operation that would return the socket to a pristine state. Since this was not included in the original (or any succeeding) TCP implementation, the only remaining option is to close and re-open.

The POSIX standard (and your book, which probably uses the POSIX standard as a reference) thus tell you to do exactly that in order to be be able to work with all TCP/IP supporting operating systems. To have done otherwise would have invalidated some number of existing implementations.

In addition, new implementations are free to simplify their implementation by not having to worry about initiating a new connection after a failed attempt; this results in less code and less chance of bugs creeping in.

Comments

2

This may be rooted in the manpage of connect, where it says:

Generally, connection-based protocol sockets may successfully connect() only once; connectionless protocol sockets may use connect() multiple times to change their association.

That would imply that you cannot just re-connect a connection-based (read, TCP) socket. However, I cannot see it saying anything about a failing connect() implying we cannot recycle the FD.

Resuming connections if the connection got interrupted is a feature of TCP, which will generally try to do that.

1 Comment

I am guessing that the keyword in the manpage is "Change their association". By Association, i am reading it as server address and port. As long as you try to re-connect to the same address and port, it should not be an issue.
1

Are you sure you're using the same socket, rather than a new socket that you try to connect to the same address as before?

Even if this is what you're doing, the fact that the particular OS you're experimenting on allows reuse of a socket that has failed to connect doesn't mean that all other OSes that implement the socket API (or earlier/later versions of the same OS) will be similarly lenient, so you risk producing subtly non-portable code.

When you do things that the API contract does not promise you will work, there is in general no knowing what will happen. One possible reaction is that it will appear to work -- right until the moment a paying customer tries to run your code on his machine.

Is that risk really worth the cost of a close(socket); socket=socket(...); in a fairly rare error situation?

Comments

0

The socket does become unusable in Python 2 on macOS Sierra

$ 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

The situation can arise if you are e.g. waiting for a server to come up by connecting to it in a loop. In that case, on macOS you cannot reuse single socket to do it.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.