-
socket question
A general sockets question. Is there a parameter(s) that I need to set in order for this code to execute without the need for the Sleep() function? Without the Sleep(), the server starts to miss messages.
Both the client and server are running on the same 2Ghz machine so I would think that the computer is plenty fast.
Client code:
Code:
for (x = 1 ; x < 99 ; x++)
{
sprintf(buffer, "%ld", CLIENT_REC);
strcpy(Client.FName, "Joe");
strcpy(Client.LName, "Public");
Client.Age = 21;
memcpy(buffer+4, &Client, sizeof(Client));
RecSize = sizeof(Client) + sizeof(CLIENT_REC);
if (send(s, buffer, /*strlen(buffer)*/ RecSize, 0) == -1)
{
printf("Can't send message\n");
sockEnd();
return 3;
}
Sleep(55); /*any value less than this, the server starts missing them */
}
Server code:
Code:
do
{
MsgLen = recv(sSocket, buffer, BUFLEN, 0);
if (MsgLen <= 0)
{
break;
}
buffer[MsgLen] = '\0';
strcat(msg,buffer);
strncpy(message, buffer, 4);
message[4] = '\0';
MsgType = atol(message);
switch(MsgType)
{
case CLIENT_REC:
/* pull the client structure out of the buffer */
memcpy(&Client, buffer+4, sizeof(Client));
fprintf(fp, "%d: Client: [%s] [%s] [%d]\n", pid, Client.FName, Client.LName, Client.Age);
break;
default:
printf("Unknown Message Type\n");
break;
}
memset(msg,0,MSG_SIZE);
memset(buffer,0,BUFLEN);
} while(1);
-
Your problem is this:
- without the delay, the client program hammers messages down the socket at a high speed. The OS and TCP/IP stack can and will collate messages into single a single packet if two or more are received within a small time difference.
- the server performs a read on the socket and gets given whatever is available at the time, which might be more than one of your "records". It will only read upto the number of bytes you specify as the 3rd parm of the socket() call.
- The result is the read is probably reading more than one "record" without you realising, and is only treating it as one.
To prove the theory, here's some output from a modified version of your program. All I have given here is the number of bytes received from the read() call, when the client sends it's 98 records:
Code:
Server received 28 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 56 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 100 bytes
Server received 60 bytes
In this example the read buffer was set to 100. This is an example of the same thing, but with the buffer set to 1000 bytes length
Code:
Server received 28 bytes
Server received 1000 bytes
Server received 456 bytes
Server received 1000 bytes
Server received 260 bytes
As you can see there were only 5 read()'s needed to get all the data.
-
Thanks Hammer, I can see that in the debugger now. Ok, now what is the solution (ie. how is it done on systems that MUST scale very high)?
A). send a small message that says the next message is the size of the record such as:
Lets say the client program just sent the value 1000 (which represents a client record).
/* all records are #defined as a long value */
/* this, in effect, is saying "the next message will be msgSize bytes" */
msgSize = recv(ss, buffer, sizeof(long), 0)
msgSize is 4 bytes (buffer has the value "1000")
if (msgSize > 0)
{
msgSize = atol(buffer);
/* read the client record */
msgSize = recv(ss, buffer, msgSize, 0)
}
or
B). Handle the packets within the server by concatinating the recv buffer with a "holding buffer" and picking off the "long (record type) value" and the sizeof the record sent.
or
C) ??
-
I would stick a couple of bytes on the front of every record denoting its length. Then make the read buffer as big as you like, being able to cope with multiple records of varying length.
Once the read is successful, interogate the first two bytes to get the length, the move that many bytes out of the buffer to where ever you want. If there's still data in the buffer, it must be from the next record, so start this step over again. If you get to the end of a buffer half way through a record, you know that you're going to get the rest of it on the next read (unless it disconnects for some reason), so you must concatenate the next read with this one.
This is just one method... you can choose to ignore this if you want. :)