Reputation: 8991
I'm getting very strange behavior with my TCP client socket code, so I wrote up a simple test program.
The code below is meant to constantly retry connecting to a server (127.0.0.1:36000
) until it connects. It is being compiled with g++ 4.8.5
with -std=c++98
(C++03/11 cannot be used).
#include <arpa/inet.h>
#include <cctype>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int sd = -1;
while (true)
{
close(sd);
std::cout << "trying to connect" << std::endl;
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
std::cerr << strerror(errno) << std::endl;
continue;
}
std::cout << "socket created " << sd << std::endl;
struct sockaddr_in addr;
memset((char *)&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
if (!inet_aton("127.0.0.1", &addr.sin_addr))
{
std::cerr << strerror(errno) << std::endl;
continue;
}
std::cout << "translated ip" << std::endl;
addr.sin_port = htons(36000);
if (connect(sd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
{
std::cerr << "Error connecting socket" << std::endl;
continue;
}
std::cout << "connected " << inet_ntoa(addr.sin_addr) << ":" << ntohs(addr.sin_port) << std::endl;
std::cout << "write " << write(sd, "from client", 11) << std::endl;
char buf[1024] = {0};
int size;
while ((size = read(sd, buf, 1024, 0, NULL, NULL)) > -1)
{
buf[size] = 0;
std::cout << "received " << size << " [" << buf << "]" << std::endl;
}
std::cout << "stopped" << std::endl;
}
return 0;
}
Despite the fact that there is no server or any other program using port 36000, the connect
call succeeds, as will the write
. I'm getting output that looks like:
trying to connect
socket created 3
translated ip
Error connecting socket
trying to connect
socket created 3
translated ip
Error connecting socket
...
...
...
trying to connect
socket created 3
translated ip
connected 127.0.0.1:36000
write 11
received 11 [from client]
If the port is changed to any other unused port (35999, 36001, etc), connect
will only fail (connection refused). Other ports never seem to connect.
The arguments recvfrom
are not modified if they are present.
Additionally, it only happens when the socket is not timing out. If the socket is still timing out, connect
will fail.
Running netstat -an
while the read
is blocked will return
tcp 0 0 127.0.0.1:36000 127.0.0.1:36000 ESTABLISHED
This happens on CentOS 6.7 and 7 vms.
What is happening? Is there something strange about port 36000
? Is this a Centos/connect
bug? Am I doing something silly?
This is a partial duplicate of How can you have a TCP connection back to the same port? but that still doesn't explain why it is only happening on port 36000.
Upvotes: 0
Views: 638
Reputation: 36
When I execute your program with tcpdump running I get some interesting output.
sudo tcpdump -i lo
...
14:51:16.477170 IP localhost.35988 > localhost.36000: Flags [S], seq 3826079620, win 65495, options [mss 65495,sackOK,TS val 95720198 ecr 0,nop,wscale 7], length 0
14:51:16.477173 IP localhost.36000 > localhost.35988: Flags [R.], seq 0, ack 3826079621, win 0, length 0
14:51:16.477203 IP localhost.35990 > localhost.36000: Flags [S], seq 2431563950, win 65495, options [mss 65495,sackOK,TS val 95720198 ecr 0,nop,wscale 7], length 0
14:51:16.477206 IP localhost.36000 > localhost.35990: Flags [R.], seq 0, ack 2431563951, win 0, length 0
14:51:16.477247 IP localhost.35992 > localhost.36000: Flags [S], seq 3688613148, win 65495, options [mss 65495,sackOK,TS val 95720198 ecr 0,nop,wscale 7], length 0
14:51:16.477250 IP localhost.36000 > localhost.35992: Flags [R.], seq 0, ack 3688613149, win 0, length 0
14:51:16.477282 IP localhost.35994 > localhost.36000: Flags [S], seq 1503921089, win 65495, options [mss 65495,sackOK,TS val 95720198 ecr 0,nop,wscale 7], length 0
14:51:16.477285 IP localhost.36000 > localhost.35994: Flags [R.], seq 0, ack 1503921090, win 0, length 0
14:51:16.477315 IP localhost.35996 > localhost.36000: Flags [S], seq 2868111150, win 65495, options [mss 65495,sackOK,TS val 95720198 ecr 0,nop,wscale 7], length 0
14:51:16.477318 IP localhost.36000 > localhost.35996: Flags [R.], seq 0, ack 2868111151, win 0, length 0
14:51:16.477348 IP localhost.35998 > localhost.36000: Flags [S], seq 281293569, win 65495, options [mss 65495,sackOK,TS val 95720198 ecr 0,nop,wscale 7], length 0
14:51:16.477351 IP localhost.36000 > localhost.35998: Flags [R.], seq 0, ack 281293570, win 0, length 0
14:51:16.477381 IP localhost.36000 > localhost.36000: Flags [S], seq 3196081163, win 65495, options [mss 65495,sackOK,TS val 95720198 ecr 0,nop,wscale 7], length 0
14:51:16.477386 IP localhost.36000 > localhost.36000: Flags [S.], seq 3196081163, ack 3196081164, win 65495, options [mss 65495,sackOK,TS val 95720198 ecr 95720198,nop,wscale 7], length 0
14:51:16.477422 IP localhost.36000 > localhost.36000: Flags [.], ack 1, win 512, options [nop,nop,TS val 95720198 ecr 95720198], length 0
This output shows that the socket is actually performing the TCP 3-way handshake with itself. For some reason (in at least CentOS 6.7) It appears to be incrementing the source port by 2 thus preventing the issue on port 35999, 36001.
In RFC 793 it talks about this type of connection as a simultaneous connection.
Upvotes: 2