user2209521
user2209521

Reputation: 125

(C Socket Programming) Seperate send() calls from server ending up in same client recv() buffer

I was wondering if anyone could shed any light as to why two seperate send() calls would end up in the same recv() buffer using the loopback address for testing yet once switched to two remote machines they would require two recv() calls instead? I have been looking at the wireshark captures yet cant seem to make any sense as to why this would be occuring. Perhaps someone could critique my code and tell me where im going wrong. The two incoming messages from the server is of an undetermined length to the client. By the way i'm using BSD sockets using C in Ubuntu.

In the example shown below im parsing the entire buffer to extract the two seperate messages from it which i'll admit isn't an ideal approach.

-------SERVER SIDE--------

// Send greeting string and receive again until end of stream
ssize_t numBytesSent = send(clntSocket, greeting, greetingStringLen, 0);
if (numBytesSent < 0)
  DieWithSystemMessage("send() failed");

//-----------------------------Generate "RANDOM" Message -----------------------

srand(time(NULL)); //seed random number from system clock

size_t randomStringLen = rand() % (RANDOMMSGSIZE-3); //generates random num
                                                     // betweeen 0 and 296

char randomMsg [RANDOMMSGSIZE] = "";

// declare and initialize allowable characteer set for the
const char charSet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (randomStringLen) {
    --randomStringLen;
    for (size_t i = 0; i < randomStringLen; i++) {
        int p = rand() % (int) (sizeof charSet - 1);
        randomMsg[i] = charSet[p];
    }
    randomStringLen = strlen(randomMsg);
    printf("Random String Size Before newline: %d\n", (int)randomStringLen);

strcat(randomMsg,"\r\n");
}

randomStringLen = strlen(randomMsg);

printf("Random String: %s\n", randomMsg);

//-----------------------------Send "RANDOM" Message ---------------------------

// Send greeting string and receive again until end of stream
numBytesSent = send(clntSocket, randomMsg, randomStringLen, 0);
if (numBytesSent < 0)
  DieWithSystemMessage("send() failed");

//------------------------------------------------------------------------------

------CLIENT SIDE-------

//----------------------------- Receive Server Greeting ---------------------------

char buffer[BUFSIZE] = ""; // I/O buffer
// Receive up to the buffer size (minus 1 to leave space for
// a null terminator) bytes from the sender
ssize_t numBytesRcvd = recv(sock, buffer, BUFSIZE - 1, 0);

if (numBytesRcvd < 0)
  DieWithSystemMessage("recv() failed");
buffer[numBytesRcvd] = '\0'; //terminate the string after calling recv()

printf("Buffer contains: %s\n",buffer); // Print the buffer

//printf("numBytesRecv: %d\n",(int)numBytesRcvd); // Print the buffer


//------------------------ Extracts the random message from buffer ---------------------------

char *randomMsg = strstr(buffer, "\r\n"); // searches from first occurance of substring
char randomMessage [BUFSIZE] = "";
strcat(randomMessage, randomMsg+2);

int randomStringLen = strlen(randomMessage)-2;

printf("Random Message: %s\n",randomMessage); // Print the buffer

char byteSize [10];
sprintf(byteSize,"%d", randomStringLen);
printf("ByteSize = %s\n",byteSize);

//----------------------- Send the number for random bytes recieved -------------------------

size_t byteStringLen = strlen(byteSize); // Determine input length

numBytes = send(sock, byteSize, byteStringLen, 0);
if (numBytes < 0)
  DieWithSystemMessage("send() failed");
else if (numBytes != byteStringLen)
  DieWithUserMessage("send()", "sent unexpected number of bytes");

shutdown(sock,SHUT_WR); // further sends are disallowed yet recieves are still possible

//----------------------------------- Recieve Cookie ----------------------------------------

Upvotes: 0

Views: 1332

Answers (3)

xbug
xbug

Reputation: 1472

You're experiencing a TCP congestion avoidance optimization commonly referred to as the Nagle algorithm (named after John Nagle, its inventor).

The purpose of this optimization is to reduce the number of small TCP segments circulating over a socket by combining them together into larger ones. When you write()/send() on a TCP socket, the kernel may not transmit your data immediately; instead it may buffer the data for a very short delay (typically a few tens of milliseconds), in case another request follows.

You may disable Nagle's algorithm on a per-socket basis, by setting the TCP_NODELAY option.

It is customary to disable Nagle in latency-sensitive applications (remote control applications, online games, etc..).

Upvotes: 0

user207421
user207421

Reputation: 310913

TCP is a byte-stream protocol, not a message protocol. There is no guarantee that what you write with a single send() will be received via a single recv(). If you need message boundaries you must implement them yourself, e.g. with a length-word prefix, a type-length-value protocol, or a self-describing protocol like XML.

Upvotes: 1

Evan Dark
Evan Dark

Reputation: 1341

On Unix systems recv and send are just special cases of the read and write that accepts additional flags. (Windows also emulates this with Winsock).

You shouldn't assume that one recv corresponds to one send because that's generally isn't true (just like you can read a file in multiple parts, even if it was written in a single write). Instead you should start each "message" with a header that tells you how long the message is, if it's important to know what were the separate messages, or just read the stream like a normal file, if it's not important.

Upvotes: 2

Related Questions