WinCE socket select/recv fails when peer sends less than 4 bytes ?

G

Guest

I`ve written some TCP/IP socket communication client based on the BSD socket
API for WinCE .NET 4.20 (ws2.lib, using 2.2 for WSAstartup).
The problem I encounter is that the socket recv and select
does not seem to work correctly when the server sends less than 4 bytes in a
chunk. The WinCE client acknowledges this on TCP/IP level with a
delay ~3sec, but the application does not recv any data. When sending
several such small chunks with big delays (~10sec), the application gets
some received data, but there is data missing from the stream!
As soon as I send chunks>=4 bytes, select/recv seem to operate correctly.
Also no delay is visible on the TCP/IP ACK. Is this a bug in the WinCE socket
library?
 
P

Paul G. Tobey [eMVP]

Try this in C/C++ instead. I've never seen any such problem and I suspect
that your code is wrong, but the first step in localizing the problem is
eliminating WinSock itself.

Paul T.
 
G

Guest

Below is the relevant code snippet which is working now due to using 4 bytes
in the initial command message. If you change this code
and the peers one to send less than four bytes for the "cmd" token after
the connection is established, then the problem starts to occur. No problem
when doing this between SUNs/Linux server/clients. I initially did this with
one cmd byte as I only have a few commands I want to send to the client.

One thing to mention is probably that the network below is a ppp direct serial
connection which has come up right before (you plug in a scanner handeld
device
to its cradle and the network is established. This triggers the creation of
the
communication thread with the socket stuff below). I also placed some Sleeps
before to make sure the network layer is completely up and there is also
a network share connected and some files transferred before, so the network
layer is already working fine when coming to this point.



while ( bNetworkUp )
{
SOCKADDR_IN local_sin,destination_sin;
PHOSTENT phostent;

// create and bind socket
sock=socket(AF_INET,SOCK_STREAM,0);

local_sin.sin_family = AF_INET;
local_sin.sin_port = htons (0);
local_sin.sin_addr.s_addr = htonl (INADDR_ANY);
bind (sock, (struct sockaddr *) &local_sin, sizeof (local_sin));

// set to nonblocking operation
unsigned long noblock=1;
ioctlsocket( sock,FIONBIO,&noblock );

// prepare destination
destination_sin.sin_family = AF_INET;
//phostent = gethostbyname ("scanhost");

// Assign the socket IP address.
//memcpy ((char FAR *)&(destination_sin.sin_addr),
// phostent->h_addr,
// phostent->h_length);

char addr[4];
addr[0]=(char)192;
addr[1]=(char)169;
addr[2]=(char)1;
addr[3]=(char)1;

memcpy ((char FAR *)&(destination_sin.sin_addr),addr,4);
//memcpy ((char FAR *)&(destination_sin.sin_addr),
// phostent->h_addr,
// phostent->h_length);

destination_sin.sin_port = htons (16325);

// trigger connection attempt to remote host
connect (sock,
(PSOCKADDR) &destination_sin,
sizeof (destination_sin));


// wait for succesful connection and poll network status
BOOL connected=FALSE;

int attempts=5;
while ( bNetworkUp && connected==FALSE && attempts-- > 0)
{
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;

fd_set wr;
FD_ZERO(&wr);
FD_SET(sock,&wr);

int rc=select(0, NULL, &wr, NULL, &timeout);

if ( rc==1 && FD_ISSET(sock,&wr) )
connected=TRUE;

}


int collected=0;
char rxbuffer[4];

while ( connected && bNetworkUp )
{
fd_set rd;
FD_ZERO(&rd);
FD_SET(sock,&rd);

struct timeval timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 0;

int rc=select(0, &rd, NULL, NULL, &timeout);

if ( rc==1 && FD_ISSET(sock,&rd) )
{
int len=recv(sock,&rxbuffer[collected],4-collected,0);
if (len>0)
{
collected+=len;
if ( collected==4 )
{
unsigned long cmd=ntohl(*(unsigned long*)rxbuffer);
if ( cmd==0x01 )
transmit_codes(sock);

}
collected=0;
}
else
{
connected=FALSE;
}
}
}
closesocket(sock);
}
 
G

Guest

...any bugs in this code which could lead to the problem ?


emzi said:
Below is the relevant code snippet which is working now due to using 4 bytes
in the initial command message. If you change this code
and the peers one to send less than four bytes for the "cmd" token after
the connection is established, then the problem starts to occur. No problem
when doing this between SUNs/Linux server/clients. I initially did this with
one cmd byte as I only have a few commands I want to send to the client.

One thing to mention is probably that the network below is a ppp direct serial
connection which has come up right before (you plug in a scanner handeld
device
to its cradle and the network is established. This triggers the creation of
the
communication thread with the socket stuff below). I also placed some Sleeps
before to make sure the network layer is completely up and there is also
a network share connected and some files transferred before, so the network
layer is already working fine when coming to this point.



while ( bNetworkUp )
{
SOCKADDR_IN local_sin,destination_sin;
PHOSTENT phostent;

// create and bind socket
sock=socket(AF_INET,SOCK_STREAM,0);

local_sin.sin_family = AF_INET;
local_sin.sin_port = htons (0);
local_sin.sin_addr.s_addr = htonl (INADDR_ANY);
bind (sock, (struct sockaddr *) &local_sin, sizeof (local_sin));

// set to nonblocking operation
unsigned long noblock=1;
ioctlsocket( sock,FIONBIO,&noblock );

// prepare destination
destination_sin.sin_family = AF_INET;
//phostent = gethostbyname ("scanhost");

// Assign the socket IP address.
//memcpy ((char FAR *)&(destination_sin.sin_addr),
// phostent->h_addr,
// phostent->h_length);

char addr[4];
addr[0]=(char)192;
addr[1]=(char)169;
addr[2]=(char)1;
addr[3]=(char)1;

memcpy ((char FAR *)&(destination_sin.sin_addr),addr,4);
//memcpy ((char FAR *)&(destination_sin.sin_addr),
// phostent->h_addr,
// phostent->h_length);

destination_sin.sin_port = htons (16325);

// trigger connection attempt to remote host
connect (sock,
(PSOCKADDR) &destination_sin,
sizeof (destination_sin));


// wait for succesful connection and poll network status
BOOL connected=FALSE;

int attempts=5;
while ( bNetworkUp && connected==FALSE && attempts-- > 0)
{
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;

fd_set wr;
FD_ZERO(&wr);
FD_SET(sock,&wr);

int rc=select(0, NULL, &wr, NULL, &timeout);

if ( rc==1 && FD_ISSET(sock,&wr) )
connected=TRUE;

}


int collected=0;
char rxbuffer[4];

while ( connected && bNetworkUp )
{
fd_set rd;
FD_ZERO(&rd);
FD_SET(sock,&rd);

struct timeval timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 0;

int rc=select(0, &rd, NULL, NULL, &timeout);

if ( rc==1 && FD_ISSET(sock,&rd) )
{
int len=recv(sock,&rxbuffer[collected],4-collected,0);
if (len>0)
{
collected+=len;
if ( collected==4 )
{
unsigned long cmd=ntohl(*(unsigned long*)rxbuffer);
if ( cmd==0x01 )
transmit_codes(sock);

}
collected=0;
}
else
{
connected=FALSE;
}
}
}
closesocket(sock);
}


Paul G. Tobey said:
Try this in C/C++ instead. I've never seen any such problem and I suspect
that your code is wrong, but the first step in localizing the problem is
eliminating WinSock itself.

Paul T.
 
P

Paul G. Tobey [eMVP]

Nothing jumps out at me, but I've just written a simple socket receive
program and it works fine with Hyperterminal on the other end, where it's
hard to get it to send *more* than 4 bytes per packet. You might try
synchronous mode, rather than asynchronous. That should get you out of
using select(), although I've never had any problems with that call.

Paul T.

emzi said:
..any bugs in this code which could lead to the problem ?


emzi said:
Below is the relevant code snippet which is working now due to using 4
bytes
in the initial command message. If you change this code
and the peers one to send less than four bytes for the "cmd" token after
the connection is established, then the problem starts to occur. No
problem
when doing this between SUNs/Linux server/clients. I initially did this
with
one cmd byte as I only have a few commands I want to send to the client.

One thing to mention is probably that the network below is a ppp direct
serial
connection which has come up right before (you plug in a scanner handeld
device
to its cradle and the network is established. This triggers the creation
of
the
communication thread with the socket stuff below). I also placed some
Sleeps
before to make sure the network layer is completely up and there is also
a network share connected and some files transferred before, so the
network
layer is already working fine when coming to this point.



while ( bNetworkUp )
{
SOCKADDR_IN local_sin,destination_sin;
PHOSTENT phostent;

// create and bind socket
sock=socket(AF_INET,SOCK_STREAM,0);

local_sin.sin_family = AF_INET;
local_sin.sin_port = htons (0);
local_sin.sin_addr.s_addr = htonl (INADDR_ANY);
bind (sock, (struct sockaddr *) &local_sin, sizeof (local_sin));

// set to nonblocking operation
unsigned long noblock=1;
ioctlsocket( sock,FIONBIO,&noblock );

// prepare destination
destination_sin.sin_family = AF_INET;
//phostent = gethostbyname ("scanhost");

// Assign the socket IP address.
//memcpy ((char FAR *)&(destination_sin.sin_addr),
// phostent->h_addr,
// phostent->h_length);

char addr[4];
addr[0]=(char)192;
addr[1]=(char)169;
addr[2]=(char)1;
addr[3]=(char)1;

memcpy ((char FAR *)&(destination_sin.sin_addr),addr,4);
//memcpy ((char FAR *)&(destination_sin.sin_addr),
// phostent->h_addr,
// phostent->h_length);

destination_sin.sin_port = htons (16325);

// trigger connection attempt to remote host
connect (sock,
(PSOCKADDR) &destination_sin,
sizeof (destination_sin));


// wait for succesful connection and poll network status
BOOL connected=FALSE;

int attempts=5;
while ( bNetworkUp && connected==FALSE && attempts-- > 0)
{
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;

fd_set wr;
FD_ZERO(&wr);
FD_SET(sock,&wr);

int rc=select(0, NULL, &wr, NULL, &timeout);

if ( rc==1 && FD_ISSET(sock,&wr) )
connected=TRUE;

}


int collected=0;
char rxbuffer[4];

while ( connected && bNetworkUp )
{
fd_set rd;
FD_ZERO(&rd);
FD_SET(sock,&rd);

struct timeval timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 0;

int rc=select(0, &rd, NULL, NULL, &timeout);

if ( rc==1 && FD_ISSET(sock,&rd) )
{
int len=recv(sock,&rxbuffer[collected],4-collected,0);
if (len>0)
{
collected+=len;
if ( collected==4 )
{
unsigned long cmd=ntohl(*(unsigned long*)rxbuffer);
if ( cmd==0x01 )
transmit_codes(sock);

}
collected=0;
}
else
{
connected=FALSE;
}
}
}
closesocket(sock);
}


Paul G. Tobey said:
Try this in C/C++ instead. I've never seen any such problem and I
suspect
that your code is wrong, but the first step in localizing the problem
is
eliminating WinSock itself.

Paul T.

I`ve written some TCP/IP socket communication client based on the BSD
socket
API for WinCE .NET 4.20 (ws2.lib, using 2.2 for WSAstartup).
The problem I encounter is that the socket recv and select
does not seem to work correctly when the server sends less than 4
bytes in
a
chunk. The WinCE client acknowledges this on TCP/IP level with a
delay ~3sec, but the application does not recv any data. When sending
several such small chunks with big delays (~10sec), the application
gets
some received data, but there is data missing from the stream!
As soon as I send chunks>=4 bytes, select/recv seem to operate
correctly.
Also no delay is visible on the TCP/IP ACK. Is this a bug in the
WinCE
socket
library?
 
G

Guest

I finally found the problem (shame on me:). The server side is written in
Perl and
has the statement send($sock,$buffer,length($buffer),0); for sending the
data to the client. This reads o.k. when you look at the C BSD send interface.
Unfortunately, send is defined in Perl like this: send SOCKET,MSG,FLAGS,TO
So perl is not interested in the size of the buffer as it can retreive this
with
the length() function, but uses the third parameter for the FLAGS. What
happend
was simply that I have set some funny flags for the send call depending on
the length of the buffer. This resulted in the MSG_OOB flag being set, which
can
be seen as TCP/URGENT flag on the ethereal traces. So all the small data was
sent as OOB data while the WinCE client was just waiting for normal data.
After fixing this, everything works of course fine also with small chunks of
data...

There is also an interesting page regarding OOB implementation
incompatibilitie
http://msdn.microsoft.com/library/d...k/protocol_independent_out_of_band_data_2.asp

Thanks a lot for your support !

Michael
 
P

Paul G. Tobey [eMVP]

Wow! Now *that's* an obscure problem!

Paul T.

emzi said:
I finally found the problem (shame on me:). The server side is written in
Perl and
has the statement send($sock,$buffer,length($buffer),0); for sending the
data to the client. This reads o.k. when you look at the C BSD send
interface.
Unfortunately, send is defined in Perl like this: send
SOCKET,MSG,FLAGS,TO
So perl is not interested in the size of the buffer as it can retreive
this
with
the length() function, but uses the third parameter for the FLAGS. What
happend
was simply that I have set some funny flags for the send call depending on
the length of the buffer. This resulted in the MSG_OOB flag being set,
which
can
be seen as TCP/URGENT flag on the ethereal traces. So all the small data
was
sent as OOB data while the WinCE client was just waiting for normal data.
After fixing this, everything works of course fine also with small chunks
of
data...

There is also an interesting page regarding OOB implementation
incompatibilities
http://msdn.microsoft.com/library/d...k/protocol_independent_out_of_band_data_2.asp

Thanks a lot for your support !

Michael
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top