Inconsistency in TCP sockets behavior between Windows and Linux



Hi,
There is an inconsistency in TCP sockets behavior between Windows and Linux,
and I think that Linux behavior is more reasonable.
There are client and server applications.
Server creates a listening socket and waits for incoming TCP connection.
Client connects to the server, sends some amount of data (say 100 bytes) and
then abortively closes the connection. This is done by setting linger (ON,
delay 0) option on client side socket.
Server accepts the connection but 'recv' fails with 10054 (CONNRESET)
although network sniffer (ethereal) shows the data has successfully arrived
at the server side and TCP ACK was sent (from Server to Client).
If Client would delay a bit (say Sleep for a second) it works fine, the
'recv' succeeds to receive the sent buffer. On Linux the same program works
fine; that is the sent buffer can be read regardless the reset connection.
I think it is unreasonable that 'recv' operation works in different way
depending on the time when it was called, before or after TCP RST has
arrived.

The programs demonstrating this behavior follows. The same program works as
client and server. Server binds to port 23456 and should be called without
arguments. In order to run the program as a client, run it with the following
arguments: -c -d server_ip_address.
This program should compile on windows and Linux.

#ifndef _WIN32
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#else
#include "windows.h"
#include "winsock.h"
#endif
#include <string.h>
#include <stdio.h>

int isServ = 1;
char *destIp = "";
unsigned short port = 23456;

/*
* usage
* to run as server: -s
* to run as client: -c -d server_ip_address
*/


#ifndef _WIN32
#define closesocket close
#define Sleep(n) usleep(n*1000)
#endif

int main(int argc, char** argv)
{
struct sockaddr_in sa;
int s;
int cnt, ret, addrLen;
char buff[100];


#if _WIN32
{
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = 0x0101;
WSAStartup(wVersionRequested, &wsaData);
}
#endif

for (cnt = 1; cnt < argc; cnt++)
{
if (!strcmp(argv[cnt],"-s"))
isServ = 1;
else if (!strcmp(argv[cnt],"-c"))
isServ = 0;
else if (!strcmp(argv[cnt],"-d"))
destIp = argv[++cnt];
else
printf("wrong usage\n");
}

memset(&sa,0,sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
if (isServ)
sa.sin_addr.s_addr = INADDR_ANY;
else
sa.sin_addr.s_addr = inet_addr(destIp);
addrLen = sizeof(sa);

s = socket(AF_INET,SOCK_STREAM,0);
if (s < 0)
{
printf("failed to create socket \n");
exit(0);
}

if (isServ)
{
int ns,err;
struct sockaddr_in nsa;

printf("running as server\n");

if (bind(s,(struct sockaddr*)&sa, addrLen) < 0)
{
printf("failed to bind the socket\n");
exit(0);
}

if (listen(s,10) < 0)
{
printf("failed to listen on the socket\n");
exit(0);
}

addrLen = sizeof(nsa);
ns = accept(s,(struct sockaddr*)&nsa, &addrLen);
if (ns < 0)
{
printf("failed to accept\n");
exit(0);
}

printf("accepted client connection from %s:%d\n",
inet_ntoa(nsa.sin_addr),ntohs(nsa.sin_port));


/* sleep for a while; lets other side to reset the connection */
printf("going to sleep for a while ...\n");
Sleep(1000);

printf("awake, try to read from the socket ...\n");
ret = recv(ns,buff,sizeof(buff),0);
#ifdef _WIN32
err = WSAGetLastError();
#else
err = errno;
#endif
printf("Recv returns with %d, error %d\n",ret,err);
closesocket(ns);
closesocket(s);
}
else
{
struct linger l;

/* set the linger, for abortive close */
l.l_onoff = 1;
l.l_linger = 0;
if (setsockopt(s,SOL_SOCKET,SO_LINGER,(char*)&l,sizeof(l)) < 0)
{
printf("failed to to set linger\n");
exit(0);
}

if (connect(s,(struct sockaddr*)&sa, addrLen) < 0)
{
printf("failed to to connect\n");
exit(0);
}

ret = send(s,buff,50,0);
if (ret != 50)
{
printf("failed to send\n");
exit(0);
}

/* if client Sleep, server running on Windows works fine, otherwise
server 'recv' fails */
/*Sleep(3000);*/

closesocket(s);

printf("connected, sent and closed the socket\n");
}

return 0;

}

.



Relevant Pages

  • Re: Socket switch delay
    ... both at the client and at the server (and why ... would you set the send buffer size to zero on a non-overlapped ... One glaring error is your client does ... So when you use a single socket, ...
    (microsoft.public.win32.programmer.networks)
  • Re: Locking on async calls
    ... you must synchronize the entire SendMessage routine as an atomic ... operation to prevent mixed messages from being transmitted to the server. ... You are correct that read and write on the socket do not interfere with each ... If you want to handle multiple client connections from one server object ...
    (microsoft.public.dotnet.general)
  • Re: Design issue with WinSock/GetQueuedCompletionStatus
    ... delegate that to a shutdown routine called after all worker threads ... The application I've created is a server accepting connections on a few ... different TCP/IP ports and then lets the client run different commands. ... a TCP/IP socket can be closed for 2 different reasons: ...
    (microsoft.public.win32.programmer.networks)
  • Re: socket communication: socket doesnt connect
    ... Microsoft MVP, MCSD ... As far as your server code goes, ... accept the listening socket. ... Client client = new Client; ...
    (microsoft.public.vc.language)
  • Re: TCP server stop receiving new connections
    ... reset the event mask of your listening socket each time you ... I have a strange problem in my class library used by all our client ... server applications. ... incomming connections, but keeps current connections. ...
    (microsoft.public.win32.programmer.networks)