Blocking or nonblocking doesn't matter. You have made one of the most common errors of
socket programming, which is the assumption that if I send AAABBBCCC, I will receive only
AAABBBCCC; in fact, the socket levels, blocking or not, are free to return the data sent
in an substream, so you might receive AA ABB BCC CC or any other combination. This is how
TCP/IP is defined to work, and you will have to deal with it. Changing the socket to be a
blocking socket will not solve this problem, but will lead to other problems that are not
worth trying to solve.
The only the wrong with your code is that it makes a fallacious assumption that has no
basis for being made. Assume that messages will be split up arbitrarily in ways you can
neither control nor predict. Given that, you have to come up with a way to determine where
a message boundary is. Some people put a length value in front of the message (and note
that if the length is longer than one byte, you can't even get the length all at once!);
others read until they get a specific terminator (newline is the most common). Using a FSM
model in your OnReceive handler to decide what to receive next is usually suitable. One
algorithm is:
state = READ_0;
switch(state)
{ /* state */
case READ_0: // read 2-byte length
n = Receive(buf, sizeof(WORD));
switch(n)
{ /* READ0_n */
case SOCKET_ERROR: // -1, I think this is the symbol
... deal wth error
case 1:
length = (BYTE)buf[0];
state = READ_1;
return;
case 2:
length = (WORD)&buf[0];
length = ntohs(length); // if big-endian
state = READ_DATA;
pos = 0;
return;
default:
ASSERT(FALSE);
return;
} /* READ0_n */
return;
case READ_1: // read second byte of 2-byte length
n = Recieve(buf, sizeof(BYTE));
switch(n)
{ /* READ1_n */
case SOCKET_ERROR:
... deal with error ...
case 1:
length = length << 8 | buf[0]; // big-endian
// OR
length = buf[0] << 8 | length; // little-endian
state = READ_DATA;
pos = 0;
return;
default:
ASSERT(FALSE);
return;
} /* READ1_n */
return;
case READ_DATA:
n = Receive(&buf[pos], length);
pos += n;
length -= n;
if(length == 0)
{ /* done */
state = READ_0; // reset for next message
...handle message, post notification, whatever...
return;
} /* done */
return;
default:
ASSERT(FALSE); // bad state
} /* state */
There are several equivalent approaches, but I find the FSM the easiest to implement. Note
that the length could be sent in native x86 order, which is little-endian, but it is
considered better practice to send the length in network byte order (htons(length) before
sending the bytes) so it is platform-independent.
Think of the problem of receiving TCP/IP to be identical to the problem of reading from a
serial port. There are no message boundaries, the message splitup is at the whim of the
sender, the receiver, and every router in between, and never, ever think that a write of
8K bytes will be received in its entirety by a read of 8K bytes. Assume this is impossible
(in some rare networks it will happen, but not, for example, on Ethernet, where the
longest packet is 1456 bytes, so you will get chunks no larger than 1456-overhead bytes),
and Receive is free to return after any packet is received. Packets might be shorter,
depending on buffer issues, network technology between sender and receiver, and, of
course, short packets that are the end of the transmission.
joe
On Mon, 12 Jan 2004 18:04:45 -0800, "dev" <
anonymous@discussions.microsoft.com>wrote:
Quote
Hi all,
I posted a message earlier regarding using CAsyncSocket
but the received messages are appended or chopped up. I
was suggested to use blocking sockets (by both this
newsgroup and outside this newsgroup) to solve this
problem, but the problem persisted.
What's wrong with my code? Is it doing blocking receive?
How come I still get incomplete messages?
Thanks!!
Here's my code:
#include "stdafx.h"
#include <winsock2.h>
int main(int argc, char* argv[])
{
int SOCK;
struct sockaddr_in pin;
struct timeval tv;
int port = 4;
int ret = SOCKET_ERROR;
char buf[7100];
WSADATA wsaData;
if (NO_ERROR != WSAStartup(MAKEWORD(2,2), &wsaData))
printf("Error at WSAStartup()\n");
pin.sin_family = AF_INET;
pin.sin_addr.s_addr = inet_addr( "1.2.3.4" );
pin.sin_port = htons(port);
SOCK = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);
printf("Connecting to server\n");
if ( connect(SOCK, (SOCKADDR*)&pin, sizeof(pin)) ==
SOCKET_ERROR)
{
printf( "Failed to connect. Error Code = %d\n",
WSAGetLastError() );
WSACleanup();
return -1;
}
printf("Connected.\n");
tv.tv_sec = 3000; setsockopt (SOCK, SOL_SOCKET,
SO_RCVTIMEO, (char*)&tv, sizeof(tv));
while ((0 != ret) || (WSAECONNRESET == ret))
{
if (WSAECONNRESET == WSAGetLastError())
break;
ret = recv(SOCK, buf, 7100, 0);
printf("here");
if ( ret == 0 || ret == WSAECONNRESET )
{
printf( "Connection Closed.\n");
break;
}
printf( "Bytes Recv: %ld\n", ret );
}
WSACleanup();
return 0;
}
The message that I posted earlier:
I am using CAsyncSocket with SOCK_STREAM option (using
TCP). I noticed that in most cases the messages I received
from the other end were ok, but sometimes two messages
were appended together, and sometimes a message was
chopped into two separate messages.
Is that a problem of using CAsyncSocket? Anyway I can fix
it? Do I get the same problem if I don't use CAsyncSocket?
Any suggestions?
Joseph M. Newcomer [MVP]
email:
newcomer@flounder.com
Web:
www.flounder.com">
www.flounder.com
MVP Tips:
www.flounder.com/mvp_tips.htm">
www.flounder.com/mvp_tips.htm
-