Unix Socket sending/receiving long messages - c

I am writing a simple application layer protocol using tcp and I encounter a problem. I want to make fragmentation in message sending because messages are so long. But I cannot synchronize the process and the client reads empty buffer before the server writes the data. The messages are approximately 4mb. How can I write these methods?
For client
void send_message(string message);
string receive_message()
For server
void send_message(int sock,string message)
string receive_message(int sock)
My functions are below
void send_fragment(char* buffer,int length){
int n = write(sockfd, buffer, length);
if (n < 0)
{
perror("ERROR writing to socket");
exit(1);
}
}
string receive_fragment(){
char buffer[FRAGMENT_LENGTH];
bzero(buffer,FRAGMENT_LENGTH);
int n = read(sockfd, buffer, FRAGMENT_LENGTH-1);
if (n < 0)
{
perror("ERROR reading from socket");
exit(1);
}
return string(buffer);
}
void send_message(string message){
char buffer[FRAGMENT_LENGTH];
bzero(buffer,FRAGMENT_LENGTH);
int message_length = message.length();
//computes the number of fragment
int number_of_fragment = ceil((double)message_length / FRAGMENT_LENGTH);
sprintf(buffer,"%d",number_of_fragment);
//sends the number of fragment
send_fragment(buffer,strlen(buffer));
for(int i=0;i<number_of_fragment;++i){
bzero(buffer,FRAGMENT_LENGTH);
//fragment interval
int start = i*FRAGMENT_LENGTH;
int end = (i+1)*FRAGMENT_LENGTH;
if(i==number_of_fragment-1){
end = min(end,message_length);
}
//creates a fragment
const char* fragment = message.substr(start,end).c_str();
sprintf(buffer,"%s",fragment);
//sends the fragment
send_fragment(buffer,strlen(buffer));
}
}
string receive_message(){
//receive and computes the number of fragment
string number_of_fragment_string = receive_fragment();
int number_of_fragment = atoi(number_of_fragment_string.c_str());
string message ="";
for(int i=0;i<number_of_fragment;++i){
//concatenating fragments
message += receive_fragment();
}
return message;
}

You have to implement the framing in your own code. TCP is a "stream" meaning it just sends bytes without any sort of start/end indication. (UDP is packet-based but not suitable for packets of your size.)
The simplest method would be to write a 4-byte length to the socket and have the receiving side read those bytes, remembering that endianess is an issue (use htonl() and ntohl() to convert local representations to "network order").
Then proceed to read that number of bytes. When that is done, you've received your message.
If you use blocking reads, it'll be fairly simple -- if you get less then the connection has broken. If you use non-blocking reads, you have to assemble the pieces you get (you could even get the length in pieces, though unlikely) back with each read call.
There are other ways of framing your data but this is the simplest.

You're ignoring the count returned by recv(). Instead of constructing a string with the entire buffer, construct it from only that many bytes of the buffer.

1)Create send_message() and receive_message() using send() and recv().
2)Select appropriate flags in recv() Read recv() man page for flags . http://linux.die.net/man/2/recv.
3)Use some delimiter at the start and end of the message transmitted at each time to mark the beginning and end so that check can be made at receiver side.

Related

TCP server using select()

I need to write a TCP server that can handle multiple connections; I followed this guide and wrote up the following program:
static void _handle_requests(char* cmd,int sessionfd){
//TODO: extend
printf("RECEIVED: %s\n",cmd);
if (!strcmp(cmd,BAR)){
barrier_hit(&nodebar,sessionfd);
}else if (!strcmp(cmd, BYE)){
}else if (!strcmp(cmd, HI)){
}
}
void handle_requests(void){
listen(in_sock_fd,QUEUELEN);
fd_set read_set, active_set;
FD_ZERO(&active_set);
FD_SET(in_sock_fd, &active_set);
int numfd = 0;
char cmd[INBUFLEN];
for (;;){
read_set = active_set;
numfd = select(FD_SETSIZE,&read_set,NULL,NULL,NULL);
for (int i = 0;i < FD_SETSIZE; ++i){
if (FD_ISSET(i,&read_set)){
if (i == in_sock_fd){
//new connection
struct sockaddr_in cliaddr;
socklen_t socklen = sizeof cliaddr;
int newfd = accept(in_sock_fd,(struct sockaddr*)&cliaddr, &socklen);
FD_SET(newfd,&active_set);
}else{
//already active connection
read(i,cmd,INBUFLEN);
_handle_requests(cmd,i);
}
}
}
}
}
..and a single client that connect() to the server and does two consecutive write() calls to the socket file descriptor.
n = write(sm_sockfd, "hi", 3);
if (n < 0) {
perror("SM: ERROR writing to socket");
return 1;
}
//...later
n = write(sm_sockfd, "barrier", 8);
if (n < 0) {
perror("SM: 'barrier msg' failed");
exit(1);
}
The thing is, the server only picks up the first message ("hi"); afterwards, the select call hangs. Since the write ("barrier") on the client's end succeeded, shouldn't that session file descriptor be ready for reading? Have I made any glaring mistakes?
Thanks; sorry if this is something obvious, I'm completely unfamiliar with C's networking library, and the project is due very soon!
You have a misunderstanding of how TCP sockets work. There is no message boundary in TCP, i.e. if you send first "hi" and then "barrier", you can't expect the corresponding receives to return "hi" and "barrier". It's possible that they return "hibarrier". It's also in theory possible (although very rare) that they would return "h", "i", "b", "a", "r", "r", "i", "e", "r".
You really need to consider how you delimit your messages. One possibility is to send the length of a message as 32-bit integer in network byte order (4 bytes) prior to the message. Then when you receive the message, you first read 4 bytes and then read as many bytes as the message length indicates.
Do note that TCP may return partial reads, so you need to somehow handle those. One possibility is to have a buffer which holds the bytes read, and then append to this buffer when more bytes are read and handle the contents of the buffer when the first four bytes of the buffer (i.e. the message length) indicate that you have the full message.
If you want a sequential packet protocol that preserves packet boundaries, you may want to consider SCTP. However, it's not widely supported by operating system kernels currently so what I would do is the 32-bit length trick to have a packet-oriented layer on top of TCP.
Do this :
int nbrRead = read(i,cmd,INBUFLEN);
and print out the value of nbrRead. You will see that you received everything in one go. TCP is a streaming protocol, if you do 3 or more sequential sends the chance is very high that you will receive them all at once.
Also make sure that INBUFLEN is large enough 2048 will be more than enough for your example.

Maximum data size that can be sent and received using sockets, at once?(TCP socket)

I am designing a game which has master and multiple players. They send and receive data using TCP sockets.
Players transfer character strings between themselves via TCP sockets.The programs are being executed in red hat linux 6 os .
The character string transferred between players is of the type
char chain[2*hops+10];
The player code on sender side is
len = send(to,chain,sizeof(chain),0);
if (len != sizeof(chain)) {
perror("send");
exit(1);}
The code where player receives the data is like this :
char chain[2*hops+10];
len = recv(current,chain,sizeof(chain),0);
The value of hops is same for both the players.
For hops value till around 8000 it is working fine, but once the hops value crosses some point, the same program is not working. I believe data is not transferred in one go.
Is there a maximum buffer size for send and recv buffer?
Note: The sockets between them are opened using this code:
s = socket(AF_INET, SOCK_STREAM, 0);
and then the usual connect and bind sockets on both sides.
TCP is a stream-oriented protocol (as implied by SOCK_STREAM). Data that an application sends or receives (in [maximum-sized] chunks) is not received or sent in same-sized chunks. Thus one should read from a socket until enough data to be processed have been received, then attempt to process said data, and repeat:
while (true) {
unsigned char buffer [4096] = {};
for (size_t nbuffer = 0; nbuffer < sizeof buffer
; nbuffer = MAX(nbuffer, sizeof buffer)) { /* Watch out for buffer overflow */
int len = recv (sockd, buffer, sizeof buffer, 0);
/* FIXME: Error checking */
nbuffer += len;
}
/* We have a whole chunk, process it: */
;
}
You can also handle partial sends on the other side as described here, much better than I ever would.

(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 ----------------------------------------
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.
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.
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..).

How recv() function works when looping?

I read in MSDN about the send() and recv() function, and there is one thing that I'm not sure I understand.
If I send a buffer of size 256 for example, and receive first 5 bytes, so the next time I call the recv() function, it will point to the 6th byte and get the data from there?
for example :
char buff[256];
memcpy(buff,"hello world",12);
send(sockfd, buffer, 100) //sending 100 bytes
//server side:
char buff[256];
recv(sockfd, buff, 5) // now buffer contains : "Hello"?
recv(socfd, buff,5) // now I ovveride the data and the buffer contains "World"?
thanks!
The correct way to receive into a buffer in a loop from TCP in C is as follows:
char buffer[8192]; // or whatever you like, but best to keep it large
int count = 0;
int total = 0;
while ((count = recv(socket, &buffer[total], sizeof buffer - total, 0)) > 0)
{
total += count;
// At this point the buffer is valid from 0..total-1, if that's enough then process it and break, otherwise continue
}
if (count == -1)
{
perror("recv");
}
else if (count == 0)
{
// EOS on the socket: close it, exit the thread, etc.
}
You have missed the principal detail - what kind of socket is used and what protocol is requested. With TCP, data is octet granulated, and, yes, if 256 bytes was sent and you have read only 5 bytes, rest 251 will wait in socket buffer (assuming buffer is larger, which is true for any non-embedded system) and you can get them on next recv(). With UDP and without MSG_PEEK, rest of a single datagram is lost, but, if MSG_PEEK is specified, next recv() will give the datagram from the very beginning. With SCTP or another "sequential packet" protocol, AFAIK, the same behavior as with UDP is got, but I'm unsure in Windows implementation specifics.

select()ed socket fails to to recv() complete data

With the following pseudo-Python script for sending data to a local socket:
s = socket.socket(AF_UNIX, SOCK_STREAM)
s.connect("./sock.sock")
s.send("test\n")
s.send("aaa\0")
s.close()
My C program will randomly end up recving the following buffers:
test\n
test\n<random chars>
test\naaa (as expected)
The socket is being recv()'d after select() points that the socket is readable. Question is, how to avoid the first two cases?
And side question: Is it possible to send the following two messages from that script:
asd\0
dsa\0
And have select() to show the socket as readable on each of those sends, or will it only do that if I run the script again (restarting the socket client connection) and sending a message for each connect?
At a guess, the len argument to recv specifies a maximum amount of data to read, not the precise amount to be returned. recv is free to return any amount of data up to len bytes instead.
If you want to read a specific number of bytes, call recv in a loop.
int bytes = 0;
while (bytes < len) {
int remaining = len - bytes;
int read = recv(sockfd, buf+bytes, remaining, 0);
if (read < 0) {
// error
break;
}
bytes += read;
}
As noted by junix, if you'll need to send unpredictable amounts of data, consider defining a simple protocol that either starts each message with a note of its length or ends with a particular byte or sequence of bytes.

Resources