How to properly concatenate a string received in parts? - c

I'm using sockets in C, I have a client that will send a message and the size of the message and the server that will receive this message in buffers of shorter size. The message is being sent correctly, but I'm having problems when I try to concatenate all the buffers in a single string.
The client:
char *buffer = "Hello from the client you're receiving this message";
int bytes_sent;
long msgSize = strlen(buffer);
printf("Buffer Size: %ld\n", msgSize);
int msgFileSize = send(SocketFD, &msgSize, sizeof(msgSize),0);
bytes_sent = send(SocketFD, buffer, strlen(buffer), 0);
The server:
char buffer[8];
ssize_t recsize;
long msgSize;
int msize = recv(ConnectFD, &msgSize, sizeof(msgSize), 0);
int total_byt = 0;
printf("Full Message Size: %ld\n", msgSize);
char full_msg[msgSize];
while (total_byt < msgSize) {
recsize = recv(ConnectFD, (void*)buffer, sizeof buffer, 0);
total_byt += recsize;
strcat(full_msg, buffer);
if (recsize < 0) {
fprintf(stderr, "%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("recsize: %d\n ", (int)recsize);
sleep(1);
printf("datagram: %.*s\n", (int)recsize, buffer);
}
printf("full message: %s\n", full_msg);
The output of the server looks like this:
Full Message Size: 51
recsize: 8
datagram: Hello fr
recsize: 8
datagram: om the c
recsize: 8
datagram: lient yo
recsize: 8
datagram: u're rec
recsize: 8
datagram: eiving t
recsize: 8
datagram: his mess
recsize: 3
datagram: age
full message: ��9�Hello from the client you're receiving this message mess

Few problems here.
strlen returns length excluding the null char.
long msgSize = strlen(buffer);
Thus you need to have 1 additional place to hold null char.
char full_msg[msgSize+1];
full_msg[0] = '\0';
There is undefined behavior with strcat as buffer is not null terminated.
recsize = recv(ConnectFD, (void*)buffer, sizeof buffer, 0);
strcat(full_msg, buffer);
use
strncpy(full_msg, buffer, sizeof buffer);
Finally null terminate the full_msg after loop to be sure.
while (total_byt < msgSize) {
….
}
full_msg[total_byt] = '\0';

It looks like your full message variable is not initialized. It is starting out with random data in it, which is causing your unexpected results. Use the following code to initialize it:
char full_msg[msgSize] = "";
Also, using strcat in the way you are is not safe. You must keep track of how much space is left in full_msg or you will have a buffer overrun situation. strncat is the function that can solve this problem.

There's absolutely no point reading into a separate buffer and then concatenating that into another buffer. Just read directly into full_msg.
Even if you were going to append block by block, strcat is not the right way to do it. strcat needs to start by finding the current end of the output buffer, which it can only do by sequentially scanning from the beginning looking for a NUL byte. As the buffer gets more and more data, those scans get longer and longer, leading to quadratic time complexity. This particular unnecessarily quadratic append is often called a "Schlemiel the Painter" algorithm.
The scan is unnecessary because you already know where the new data should be appended, since you carefully keep track of how many bytes you've already read. So each successive block should be placed total_byt bytes past the beginning of the buffer. (That is, at location full_msg+totalbyt.) You also know how long the data to append is, so you can use memcpy to put the newly-read chunk in the right place.
Uding memcpy will also avoid the problems which will occur if data can contain NUL bytes. (Since strcat returns immediately when it encounters a NUL, your current code will not work on messages which do include NUL.)
Note thatrecv does not NUL-terminate the input received, so your strcat will also do the wrong thing if the recv doesn't fill the buffer (and it only works with the buffer as written because it happens to be the case that
There is at least one other problem with your code: nothing guarantees that recv will stop reading at the end of a message, since the sockets library doesn't know where TCP messages end. (UDP message endpoints are marked, but you can't receive a UDP message in multiple chunks.) Again, since you keep track of the number of bytes read, and you know the length of the message (once you've read that data, at least), you can easily work out what the maximum number of bytes to read is.

Related

C Socket Reading TOO MUCH Data

I am making a server that should be able to accept requests from multiple clients. To ensure I am reading large requests properly, I made the below code segment.
Requests come in the form <START_REQUEST>a long message<END_REQUEST>
read(fd, buffer, BUFFER_SIZE);
// Keep Reading If Entire Message Not Recieved
int buffer_len = strlen(buffer);
char *end_tag = &buffer[buffer_len-strlen("<END_REQUEST>")];
while(strcmp(end_tag, "<END_REQUEST>") != 0) {
char *temp_buffer;
temp_buffer = malloc(BUFFER_SIZE);
valread = read(fd, temp_buffer, BUFFER_SIZE);
strcat(buffer, temp_buffer);
free(temp_buffer);
buffer_len = strlen(buffer);
end_tag = &buffer[buffer_len-strlen("<END_REQUEST>")];
}
However, sometimes (very often) the contents of buffer are something like:
<START_REQUEST>a long message<END_REQUEST>somegarbagedataheremaybefromanotherequest?
and thus the loop never terminates.
Why might this be happening?
How are you expecting strcat to know how many bytes to append onto the buffer?
valread = read(fd, temp_buffer, BUFFER_SIZE);
strcat(buffer, temp_buffer);
After the call to read, valread holds the number of bytes you read and it's the only thing that holds this information. However, you attempt to append data read onto the existing buffer without using this value -- so there is no possible way strcat could conceivably know how many bytes to append onto the buffer. It's no wonder you append junk that you read before.
Similar problem here:
read(fd, buffer, BUFFER_SIZE);
// Keep Reading If Entire Message Not Recieved
int buffer_len = strlen(buffer);
Here you ignore the return value of read, so you have no way to know how many bytes you read. How are you expecting strlen to figure out how many bytes read put into the buffer?

How to send and receive newline character over sockets in a client server model?

I am trying to learn client server model in Linux and I have setup two C files namely server.c and client.c. These are the code snippets that I seem to have problems with.
server.c code snippet
char* message = "<query>\n";
write(client_socket_filedescriptor, message, sizeof(message));
client.c code snippet
char* message = "<query>\n";
read(socket_filedescriptor, buffer, sizeof(buffer));
printf("%s", buffer);
printf("\n\n");
printf("%s", message);
Now when I run my server and then when I run my client, I expect the printf statements to print the same strings that is <query>\n, but I keep getting different outputs for buffer and message variables.
The output looks a bit like this when I run client code.
Output image
As you see, these two strings are different. I am trying to simulate a typical TCP handshake and I want to make sure that these two strings are same and then client will start writing or doing something with that server. But I am having this trivial problem. Could anyone tell my how to resolve it? I plan to use strcmp to compare buffer and message variables, but as it stands now, strcmp doesn't return 0 since these are different strings afterall.
You are ignoring the count returned by read(). It can be -1, indicating an error, or zero, indicating end of stream, or a positive number, indicating how many bytes were received. You cannot assume that read() fills the buffer, or that a single send() or write() corresponds to a single recv() or read().
In detail:
write(client_socket_filedescriptor, message, sizeof(message));
You are only sending four bytes, the size of the pointer. And you're ignoring the return value. It should be
int count = write(client_socket_filedescriptor, message, strlen(message));
if (count == -1)
perror("write"); // or better
char* message = "<query>\n";
read(socket_filedescriptor, buffer, sizeof(buffer));
That should be
int count = read(socket_filedescriptor, buffer, sizeof(buffer));
if (count == -1)
perror("read"); // or better
else if (count == 0)
; // end of stream: the peer has disconnected: close the socket and stop reading
else
Back to your code:
printf("%s", buffer);
That should be
printf("%.*s", count, buffer);
I plan to use strcmp()
You should plan to use strncmp(), with count above as the length parameter. In any case you can't assume the input ends with a null unless you (a) ensure you send the null, which you aren't, and (b) write a read loop that stops when you've read it.

C: tcp recv not clearing old data

I have the following code in server.c
while (1) {
char msg[1024];
recv(fd, msg, 1024, 0);
}
From client, first, I sent "hello world".
And then I received "hello world" in server.
I then sent "hexx" to server.
But I received "hexxo world" on server.
It seems as if msg wasn't cleared fully.
Please let me know what I'm doing wrong.
Cheers.
You are allocating 1024 bytes for message and never zeroing that data. You are just reading into the same buffer over and over. First pass it reads "hello world" in the second pass you are putting "hexx" in the same starting address resulting in "hexxo world"
As DoxyLover mentions you can null terminate the newly read string and get what you are looking for.
tcp recv not clearing old data
Who said it would? Not that it should be necessary. You're ignoring the count returned by recv(). After calling recv(), there are three possibilities:
Return value of -1: an error; call perror().
Return value of 0: end of stream: close the socket and exit the read loop.
Return value is positive: you have received exactly that many bytes into your buffer, and you should not assume that any data beyond that count is valid.
At present you're doing none of these things correctly.
read() does not null terminate the buffer. Therefore, you are seeing the old data left over in the buffer.
What you want is something like:
while (1) {
char msg[1024];
ssize_t n = recv(fd, msg, 1023, 0);
if (n >= 0)
msg[n] = '\0';
}
Note that I am limiting the read to one less than the size of the buffer to allow space for the null byte. Obviously, this only works with text data. With binary data, you need to record the return value from read() and use that as a byte count when processing the buffer. For example, to copy data from one socket to another:
while (1) {
char msg[1024];
ssize_t n = recv(fd, msg, 1024, 0);
if (n > 0)
send(second_fd, msg, n, 0);
}
All of this is very simplified - you need error checking, check the return from read() to make sure any bytes were received, check the return from send() to make sure all of the byte were sent, etc.

How to send and receive bytes with socket apis?

Hi i have written a server application which accepts a name from the client which usually is a file name.It opens the file ,reads the contents into a buffer and then transmits the buffer over the ethernet using send().But the problem arises in the client side where all the bytes are not received successfully.I receive only a part of what i send.
For your reference ,here's the code snippet for the server side:
Server:
fp = fopen(filename,"r+");
strcpy(str,"");
fseek(fp, 0L, SEEK_END);
size = ftell(fp);
fseek(fp, 0L, SEEK_SET);
fread(str, size, 1,fp);
fclose(fp);
printf("Size of the file is : %d\n",size);
sprintf(filename, "%d", size);
n = send(nsd, filename, strlen(filename), 0);
while(size > 0){
n = send(nsd, str, strlen(str), 0);
printf("%d bytes sent successfully\n",n);
if(n == 0) break;
sentbytes = sentbytes + n;
size = size - sentbytes;
}
Please help me with writing the client app.I am currently confused about how to go about writing it.Shall i place the recv() part in a while(1) loop so that the client keeps running until all the bytes have been received successfully?
EDITED
For starters, you could both read from the file and write to the socket in chunks at the same time.
Since, you are transferring data over TCP, remember that data is transferred reliably as a stream and not as messages. So, don't make assumptions about how the data is recv'd except for the order.
Here is how it could be written:
open socket
open file
size_of_file = read_file_size(file);
send(socket, &size_of_file, sizeof(int), ...)
while (all is written)
read fixed chunk from file
write as much was read to the socket
cleanup // close file, socket
As for the recv part, I think it is best you send the file size over as an integer and keep reading in a while loop until you have recv'd as many bytes as you are sending from the server.
It's like this:
recv(socket, &size_of_msg, sizeof(int), ...)
while(all is read)
read fixed chunk from the socket
cleanup
Well I see atleast some issue with the way you are sending message over socket.
First from the man page of fread:
The function fread() reads nmemb elements of data, each size bytes
long, from the stream pointed to by stream, storing them at the loca-
tion given by ptr.
and what you are trying is this:
fread(str, size, 1,fp);
I assume what you meant was
fread(str, 1,size,fp);
Though it shold not casue the issue.
But the problem lies here:
n = send(nsd, str, strlen(str), 0);
printf("%d bytes sent successfully\n",n);
if(n == 0) break;
sentbytes = sentbytes + n;
size = size - sentbytes;
Though you are decreasing 'size' by decreasing by number of bytes successfully send, where are you extending str to point to new buffer location where data will be send.This will only resend initial bytes of the buffer repeatedly.
str += n; //Assuming str is char*
will solve your issue.
Using strlen doesn't seem appropriate. You've read the file, you know how long it is, so why do strlen? Either you'll just get the same result (so it's redundant) or you'll get something else (so it's a bug).
"Shall i place the recv() part in a while(1) loop so that the client keeps running until all the bytes have been received successfully?"
Something like that. Never presume that a recv() call got everything that was sent -- tcp/ip breaks messages into packets at a lower level, and recv() will return after reading whatever amount of data has actually been received at whatever point. You don't have to worry about that directly, except in so far as you do need to use some kind of protocol to indicate how long a message is so the receiver knows how much to read, then eg.:
char buffer[4096];
int msgsz = 600, // see below
sofar = 0,
cur;
while (sofar < msgsz) {
cur = recv (
socket_fd,
&buffer[sofar],
msgsz - sofar,
0
);
if (cur == -1) {
// error
break;
} else if (cur == 0) {
// disconnected
break;
}
sofar += cur;
}
WRT msgsz, you would include this somewhere in a fixed length header, which is read first. A simple version of that might be just 4 bytes containing a uint32_t, ie, an int with the length. You could also use a null terminated string with a number in it, but that means reading until '\0' is found.

Reliably splitting lines out of a string

I'm writing myself a small server daemon in C, and the basic parts like processing connects, disconnects and receives are already in, but a problem in receiving still persists.
I use "recv" to read 256 bytes at once into a char array, and because it can contain multiple lines of data as one big chunk, I need to be able to split each line separatly to process it.
That alone wouldn't be the problem, but because of the possibility that a line could be cut off because it didn't fit into the buffer anymore, I also need to be able to see if a line has been cut off. Not that bad, too, just check the last char for \r or \n, but what if the line was cut off? My code does not allow for easy "just keep reading more data" because I'm using select() to handle multiple requests.
Basically, this is my situation:
//This is the chunk of code ran after select(), when a socket
//has readable data
char buf[256] = { 0 };
int nbytes;
if ((nbytes = recv(i, buf, sizeof(buf) - 1, 0)) <= 0)
{
if (nbytes == 0)
{
struct remote_address addr;
get_peername(i, &addr);
do_log("[Socket #%d] %s:%d disconnected", i, addr.ip, addr.port);
}
else
do_log("recv(): %s", strerror(errno));
close(i);
FD_CLR(i, &clients);
}
else
{
buf[sizeof(buf) - 1] = 0;
struct remote_address addr;
get_peername(i, &addr);
do_log("[Socket #%d] %s:%d (%d bytes): %s", i, addr.ip, addr.port, nbytes, buf);
// split "buf" here, and process each line
// but how to be able to get the rest of a possibly cut off line
// in case it did not fit into the 256 byte buffer?
}
I was thinking about having a higher scoped temporary buffer variable (possibly malloc()'d) to save the current buffer in, if it was too long to fit in at once, but I always feel bad about introducing unnecessarily high scoped variables if there's a better solution :/
I appreciate any pointers (except for the XKCD ones :))!
I guess you need to add another per-stream buffer that holds the incomplete line until the line feed that comes after is received.
I'd use some kind of dynamically expanding buffer like GString to accumulate data.
The other thing that might help would be putting the socket into nonblocking mode using fcntl(). Then you can recv() in a loop until you get a -1. Check errno, it will be either EAGAIN or EWOULDBLOCK (and those aren't required to have the same value: check for both).
Final remark: I found that using libev (google it; I can't post multiple links) was more fun than using select().

Resources