So I'm writing this simple HTTP Client in C, and, well, I don't know C that well. Here's the gist of it. I got the HTTP client working, I can send the GET request and a get response back. I even can parse the HTTP headers to get the information I want (though my solution to that is admittedly inelegant). The problem is when it's binary information, like a picture file. I'm sure it's something simple, but ... well here's a slimmed down version of what I have.
FILE *fp = fopen("output", "wb"); // Open the file for writing
while (recv(s, buf, MAXDATASIZE - 1, 0) > 0) { //This just iterates over our HTTP response, it works don't worry
if (is_text == 1) { // Assume I already parsed the headers and know the Content-type
fprintf(fp, "%s", buf); // This works, it just writes the text to our output file
}
else { // Anything not text, this is where the problem is
fwrite(buf, sizeof buf, 1, fp); // <-- **This is the problem line**
}
memset(buf, 0, MAXADATASIZE -1, 0); // Reset the buffer, for the next iteration
}
flose(fp); // Close the file
So, the line I have marked as the problem line does write some binary stuff to my output file, it just never matches the original file. Not only do they differ, they differ in size by like orders of a hundred. It's just not the same file. Yes, I know need to figure out a way to strip the headers or the files will always be different, but clearly that's not even the problem here, yet.
--
Extra information:
Ubuntu 18.04.5
gcc version 7.5.0
"Hardware" is a virtualbox VM, 64-bit
recvreturns the number of bytes read.
ssize_t rb;
FILE *fp = fopen("output", "wb"); // Open the file for writing
while ((rb = recv(s, buf, sizeof(buf), 0)) > 0) { //This just iterates over our HTTP response, it works don't worry
fwrite(buf, 1, rb, fp); // <-- **This is not the problem line anymore**
}
flose(fp); // Close the file
Related
I am trying to send a file and its name through a socket in C.
The relevant server code is:
char file[18];
memset(file, 0, 18);
file[17] = '\0';
int recvd = recv(newsock, file, 16, 0);
char local_file_path[200];
memset(local_file_path, 0, 200);
if(recvd == -1 || recv == 0) {
fprintf(stderr, "File name not received");
continue;
}
strcat(local_file_path, "/home/ubuntu/results/");
strcat(local_file_path, file);
FILE* fp = fopen(local_file_path, "wb");
char buffer[4096];
while(1)
{
recvd = recv(newsock, buffer, 4096, 0);
fwrite(buffer, sizeof(char), recvd, fp);
if(recvd == -1 || recvd == 0) {
fclose(fp);
break;
}
}
close(newsock);
}
close(servSock);
The relevant client code is:
char* my_16_long_fname = "filename1234.txt"
int ret = send(sock, my_16_long_file_fname, strlen(my_16_long_fname), 0)
This code, however, has been creating lots of undefined behaviour such as:
1.Receiving garbage filenames filled with garbage
2.Receiving empty files (so a name with nothing inside - could be some other bug but possibly due to this)
I have thought about a few solutions:
1.Diferentiate file types by signature/header and generate a file name on the server side. Besides this being a cheap solution which doesn't teach me how to actually solve the problem, it doesn't work with the logic i'm using, where sometimes I send error codes instead of file names after opening the socket.
2.Iterate over the recv'd buffer on the first call to recv until I encounter a '\0' character. Then write the remainder of the buffer as binary data and keep on receiving data as usual.
Is this the most efficient/simplest and solid solution to this issue, which will prevent any undefined behaviour?
There is no way your current code could possibly work. If the filename is always one character, your code can read too many characters. If your filename is always the same number of characters but more than one character, your code can read too few characters. If the filename is a variable number of characters, your code could read a smaller number than was sent.
So there is no sending protocol for which this could be valid receiving code.
Until you are an expert on writing networking code, always follow these two steps:
Document the protocol.
How many bytes does the filename occupy? Is it a fixed number or a variable number? Is it always followed by a zero byte?
Implement the protocol.
For example, your code reads up to 16 bytes for the filename. But it never checks if it received the whole file name. What if it only received a single byte?
I'm developing very simple ftp client. I have created a data connection sockets, but I can't transfer file successfully:
FILE *f = fopen("got.png", "w");
int total = 0;
while (1){
memset(temp, 0, BUFFSIZE);
int got = recv(data, temp, sizeof(temp), 0);
fwrite(temp, 1, BUFFSIZE, f);
total += got;
if (total == 1568){
break;
}
}
fclose(f);
BUFFSIZE = 1568
I know that my file is 1568 bytes size, so I try to download it just for a test. Everything is file when I try to download .xml or .html files, but nothing good happens when I try to download png or avi files. Simply original file size is 1568 but got.png file size is 1573. I can't figure out what might cause that.
EDIT:
I have modified my code, so now it looks like (it can accept any file size):
FILE *f = fopen("got.png", "w");
while (1){
char* temp = (char*)malloc(BUFFSIZE);
int got = recv(data, temp, BUFFSIZE, 0);
fwrite(temp, 1, got, f);
if (got == 0){
break;
}
}
fclose(f);
Still received file is 2 bytes too long.
You are opening the file in text mode, so bare CR/LF characters are going to get translated to CRLF pairs when written to the file. You need to open the file in binary mode instead:
FILE *f = fopen("got.png", "wb");
You are always writing a whole buffer even if you have received only a partial one. This is the same problem with ~50% of all TCP questions.
The memset is not necessary. And I hope that temp is an array so that sizeof(temp) does not evaluate to the native pointer size. Better use BUFFSIZE there as well.
Seeing your edit, after fixing the first problem there is another one: Open the file in binary mode.
I see a plenty of examples but none addresses what I want to accomplish. I need to read the bytes from a socket and write them in to a file. In this Code Project blog I see where in the client script a while loop iterates through a read call:
while((n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0)
So I modified the code do that fputs(recvBuff, f1) where f1 is a pointer to a pdf file. A pdf file is also a file I'm fetching from the server so I need to reassemble it, however the fputs operated with a string and corrupts the file, so I need a byte "writer" so fwrite would have been the choice but I can't get fwrite to work. I ended up modifying my code to resemble some of the examples to test it out but to no avail.
If in fwrite the first parameters is the 'data' how would I pass it? I've tried the read() call as in the while loop above but that seem to return an integer rather then a byte stream. Any ideas?
I'm new to programming but am new to C and would appreciate a little push in a right direction. Thanks.
You want something more like this. fwrite doesn't return a stream it returns the number of items (i.e. the 3rd parameter) successfully written. In this case the "item" is a single char and you are attempting to write "bytesRead" number of them. Good form dictates that you should check that the result fread returns is the same as you requested be written but this rarely fails on a disk file so many people skip it in non-critical situations. You may want to add that on yourself.
FILE *f1;
int sockfd;
char recvBuff[4096];
size_t bytesWritten;
ssize_t bytesRead;
while((bytesRead = read(sockfd, recvBuff, sizeof(recvBuff))) > 0)
bytesWritten = fwrite(recvBuff, 1, bytesRead, f1);
sorry if this is a dumb question, but I'm kind of inexperienced with C. I'm trying to create a simple TCP client/server connection. This is how it works: the server stores several files, including .txt and .jpg. When the client wants one of them, he sends the name of the file to the server, reading and writing it to a certain location.
Everything works fine, except for the fact that the server's response is "RES status size data" . I'm only interested in the data to create the new file, but I'm not being able to deny the writing of RES status size into the output file. I tried to use strtok but am getting a segfault because of it, dunno why. This is the section of code where I receive the response from the server and start writing it to a new file fp.
while(success == 0)
{
while(f_block_sz = recvfrom(fd2, buffer, sizeof(buffer), 0, (struct sockaddr*)&serveraddr2, &addrlen))
{
if(f_block_sz < 0)
{
printf("Receive file error.\n");
break;
}
int write_sz = fwrite(buffer, sizeof(char), f_block_sz, fp);
if(write_sz < f_block_sz)
{
printf("File write failed.\n");
break;
}
bzero(buffer, LENGTH);
}
printf("ok!\n");
success = 1;
fclose(fp);
}
I don't even know what's best. Should I remove those "useless" words before writing to the newfile, or should I edit the file after it's finished?
Thanks in advance.
I not sure but i just thought if your "RES status size" is of fixed size, say 'x' bytes then while writing to the output file just skip x bytes of received data...
int write_sz = fwrite(buffer + x, sizeof(char), f_block_sz, fp);
I am not sure this is good practice or even correct, i just gave in my thought.
I have written a program in c to send a file line by line to a server from a client in c. After the file is transmitted completely, I have provided a line endoffile in the txt file to string compare in the server to identify that the file is over and the next file is being transmitted next. The next file has to be written to another file in the server. But the problem is that the strcmp is never detecting the endoffile in the code and endoffile recieved from the file in client as equal strings amd continues to write the next file from the client to the same file in server.
char enof[]="endoffile";
...
do
{
rewind(appcrt);
bytes_recieved = recv(pass_arg.connected, recv_data_c, strlen(recv_data_c), 0);
recv_data_c[bytes_recieved-1] = '\0';
fputs(recv_data_c, appcrt);
if(strcmp(enof,recv_data_c) == 0){break;}
}while(check<count);
The text file:
Necessary data
that is
being transmitted
to be written to the file
endoffile
The code used for reading from the txt file and sending to the server:
while (fgets(line, sizeof(line), crt) != NULL)
{
send(sock, line, sizeof(line), 0);
}
What change I have to make in the condition so that the problem is resolved and the code exits from the do....while(); loop. Thanks in advance.
Operating platform: Linux
Edit1: Edited the do....while() as follows:
do
{
rewind(appcrt);
bytes_recieved = recv(pass_arg.connected, recv_data_c, 100, 0);
recv_data_c[bytes_recieved] = '\0';
fputs(recv_data_c, appcrt);
printf("%s-%s",enof,recv_data_c);
//if(strcmp(enof,recv_data_c) == 0){break;}
}while(check<count);
Got the following output in terminal:
endoffile-file1line1
endoffile-file1line2
endoffile-file1line3
endoffile-file1line4
endoffile-file1line5
endoffile-file1line6
endoffile-endoffile
endoffile-file2line1
endoffile-file2line2
endoffile-file2line3
endoffile-file2line4
endoffile-file2line5
endoffile-file2line6
.
.
.
Still no hope.
Even though the client is writing a line at a time to the socket this is not how the server will consume it as data transferred over a socket is just a stream of bytes. The server must read up to the next new line character and then compare. A simple algorithm would read a byte at a time and check if it is the newline character and if is not then append it to a string until a newline character is read:
/* Read next line. */
memset(line, 0, sizeof(line));
size_t line_len = 0;
while (line_len < sizeof(line) - 1 &&
1 == recv(pass_arg.connected, &line[line_len], 1, 0))
{
if ('\n' == line[line_len]) break;
line_len++;
}
Apart from that, there are several problems with the code:
you need to send what was read from the file as it may be less than the sizeof(line). Change:
send(sock, line, sizeof(line), 0);
to:
send(sock, line, strlen(line), 0);
and also check the return value of send() to determine if it was successful.
the following is incorrect as it would only read at a maximum what was previously read (or if was initialised to an empty string nothing would be read):
bytes_recieved = recv(pass_arg.connected,
recv_data_c, strlen(recv_data_c), 0);
and, again, check the return value especially as the return value is being used to index an array. If recv() fails it returns -1, which will result in out of bounds access on the array causing undefined behaviour.
while (fgets(line, sizeof(line), crt) != NULL)
{
send(sock, line, sizeof(line), 0);
}
Don't forget that fgets() may only read one byte, if the line is blank. Thus your send() call is sending a lot of uninitialized data every call -- either contents of previous lines or random memory free()d by your application earlier.
Thus, your receiving program would need to compare against:
endoffiletten to the file
to finally see the final string. (Assuming that the line buffer started out full of ASCII NUL characters.)