how to send a file using sockets in c [closed] - c

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
txt file from a client to a server. Right now, the file is sent to the server, but it's missing the content (empty file).
client.c
void send_file(char *filename, int sockfd)
{
FILE *fp;
int n;
char data[BUFFERLEN] = {0};
fp = fopen(filename, "r");
while (fgets(data, BUFFERLEN, fp) != NULL)
{
send(sockfd, data, sizeof(data), 0)
bzero(data, BUFFERLEN);
}
}
server.c
void receive_file(char *filename, int sockfd)
{
int n;
FILE *fp;
char buffer[BUFFERLEN];
fp = fopen(filename, "w");
while (1)
{
n = recv(sockfd, buffer, BUFFERLEN, 0);
if (n <= 0)
{
break;
return;
}
fprintf(fp, "%s", buffer);
bzero(buffer, BUFFERLEN);
}
return;
}

while (fgets(data, BUFFERLEN, fp) != NULL)
{
send(sockfd, data, sizeof(data), 0)
This is wrong. Not all lines have the size of BUFFERLEN. Don't send what you haven't read. Do not use fgets in this application. Use fread, and pass its return value back to send.
n = recv(sockfd, buffer, BUFFERLEN, 0);
fprintf(fp, "%s", buffer);
This is also wrong. buffer is not necessarily null-terminated. Do not use fprintf in this application, use fwrite, and pass the return value of recv back to it.
It also helps to open files in binary mode.

There are multiple fundamental bugs in both the client and the server.
while (fgets(data, BUFFERLEN, fp) != NULL)
A single line of text gets read into data, followed by:
send(sockfd, data, sizeof(data), 0)
Two bugs on this one line:
this sends the entire data buffer. If the text line was 80 characters, that's what fgets read, but BUFFERLEN is 8192, this is going to attempt to send 8192 characters, instead of 80, since sizeof(data) is BUFFERLEN.
The return value from send gets completely ignored. This is always wrong, with sockets. The way that sockets work, if you request to send() either 80, 8192, or any number of bytes, you have no guarantees whatsoever that the requested number of bytes was actually sent on the socket. The sending socket may, very well, be able to send only ten bytes. Or may be just one byte. The return value from send() indicates how many bytes were actually sent. You must adjust your sending logic accordingly. So, for example, if you fgetsed and send 80 characters, and send() told you that only ten went out on the socket, you will then need to try again to send the remaining 70 characters. It is your responsibility to do so. And, of course, there's no guarantee that all 70 characters will be sent on the 2nd try.
And now, for problems with the server:
n = recv(sockfd, buffer, BUFFERLEN, 0);
Here, you're checking the number of bytes received. This is correct, but then:
fprintf(fp, "%s", buffer);
%s requires a '\0' terminated string. If you guessed that you have no such guarantee from recv() you guessed correctly. You either need to make sure whatever you fprintf here is manually terminated by an extra '\0' character (and making sure that slapping on one in the buffer won't overrun it, corrupting memory), or use something else, like fwrite, perhaps.
All of the above bugs must be fixed in order for this to work correctly.

Related

How to send file and filename through socket in C?

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?

File transfer in C - Linux

I'm trying to transfer a file from server to client. I first send the name of the file I want to receive to the server, the server opens the file and writes its contents into a buffer and sends it back to the client. The client then copies the contents of that buffer into a newly created file to duplicate the contents of the server file.
When "Receive.txt" is created, only the FIRST word of the file I opened is copied into the file I created. Where am I going wrong?
server.c sending part:
if(checkCommand){
char *tmp = buf + 4;
char data[MAX_BLOCK_SIZE];
FILE *fp;
printf("File name: %s\n", tmp);
fp = fopen(tmp, "r");
if(fp == NULL){
printf("File not found\n");
exit(1);
}
do{
fscanf(fp, "%s", buf);
nw = write(sd, buf, MAX_BLOCK_SIZE);
} while(fscanf(fp, "%s", buf) != EOF);
}
client.c receiving part:
else if(getCommand){
FILE *fp;
write(sd, buf, MAX_BLOCK_SIZE);
read(sd, buf, MAX_BLOCK_SIZE);
fp = fopen("receive.txt", "w");
if(fp == NULL){
printf("File could not be opened.\n");
exit(1);
}
fprintf(fp, "%s", buf);
}
As correctly reported in comments section, the core of your issue consists in fscanf() being called both for reading data and for detecting EOF in order to quit the loop. But the latter actually consume data as well!
If I execute your program passing the following input file
Yesterday
All my troubles seemed so far away
Now it looks as though they're here to stay
and printing the output to stdout with printf("%s\n", buf); (instead of pushing data to a socket), that's what I get:
Yesterday
my
seemed
far
Now
looks
though
here
stay
So I get not only the first word, but one every two words.
Furthermore you write to the socket MAX_BLOCK_SIZE bytes whatever is the number of valid bytes you correctly read from file, so buf will contain MAX_BLOCK_SIZE - strlen(buf) bytes of garbage.
This sending loop will fix most of the issues mentioned above:
while(fscanf(fp, "%s", buf) != EOF)
{
write(sd, buf, strlen(buf));
}
Please note how you still would have something to care about:
are we sure that all words fit a MAX_BLOCK_SIZE big buffer? A word logger than the block will cause out of bound access (and undefined behavior), so a way to limit the number of acquired characters should be implemented
are we sure that all the data is sent in one shot? Probably yes, as the words are relatively short, but generally speaking the return value of the write() function should be checked, and if it is different from the amount of data to be sent the code should manage a way to send the remainder (or even exit the loop if the return value is negative!)
I'm not going to address these issues in this answer, but it was correct mentioning them. What I would like to emphatize, istead, is that sending data in this way will generate an output without whitespaces_. Something like:
YesterdayAllmytroublesseemedsofarawayNowitlooksasthoughthey'reheretostay
Even assuming that you need just a text file transfer, probably it is not what you want. For this reason I suggest implementing a binary trasfer using fread() in this way
#include<stdio.h>
#include <stdlib.h>
#define MAX_BLOCK_SIZE 1024
char buf[MAX_BLOCK_SIZE];
int main(void)
{
char tmp[] = "srcFile.txt";
FILE * fp;
int rd;
printf("File name: %s\n", tmp);
fp = fopen(tmp, "rb");
if(fp == NULL)
{
printf("File not found\n");
exit(1);
}
while(( rd = fread(buf, 1, MAX_BLOCK_SIZE, fp)) > 0)
{
write(sd, buf, rd);
//printf("%s\n", buf);
}
// You can call feof() or ferror() in order to discover if either EOF or an error occurred
return 0;
}
Uncommenting the printf what I get is exactly the input file. Please note how it would not necessary, on Linux environment, to select the binary mode with b, as
The mode string can also include the letter 'b' either as a last character or as a character between the characters in any of the two-character strings described above. This is strictly for compatibility with C89 and has no effect; the 'b' is ignored on all POSIX conforming systems, including Linux. (Other systems may treat text files and binary files differently, and adding the 'b' may be a good idea if you do I/O to a binary file and expect that your program may be ported to non-UNIX environments.)
(the emphasis is mine).
Just some notes about the receiver part:
I cannot comment the initial write(sd, buf, MAX_BLOCK_SIZE); because I'm not sure about the detais of your application, but it look strange. Make sure to send consistent data, in any case.
Always check the return values of read and write! Not only they can fail (and the error, that you can get querying errno, must be properly managed) but they can also read/write from/to socket less bytes than requested
In the general case of a binary file, writing data to the output file with fprintf(fp, "%s", buf); is not safe, as buf could contain bytes that are not printable or, even worse, it won't be NULL-terminated. Be aware that binary data could contain the '\0' character inside it, and that would result in an unexpected truncation. Use fwrite(), instead.

Incorrect fprintf results

In the code below, I am trying to read from a socket and store the results in a file.
What actually happens, is that my client sends a GET request to my server for a file.html. My server finds the file and writes the contents of it to the socket. Lastly my client reads the content from thread_fd and recreates the file.
For some reason the recreated file has less content than the original. I have located the problem to be some lines in the end, that are missing. When I use printf("%s", buffer) inside the while loop everything seems fine in STDOUT but my fprintf misses somewhat 3.000 bytes for a file of 81.000 bytes size.
#define MAXSIZE 1000
int bytes_read, thread_fd;
char buffer[MAXSIZE];
FILE* new_file;
memset(buffer, 0, MAXSIZE);
if((new_file = fopen(path, "wb+")) == NULL)
{
printf("can not open file \n");
exit(EXIT_FAILURE);
}
while ((bytes_read = read(thread_fd, buffer, MAXSIZE)) > 0)
{
fprintf(new_file, "%s", buffer);
if(bytes_read < MAXSIZE)
break;
memset(buffer, 0, MAXSIZE);
}
You read binary data from the socket that may or may not contain a \0 byte. When you then fprintf that data the fprintf will stop at the first \0 it encounters. In your case that is 3000 bytes short of the full file. If your file contains no \0 byte the fprintf will simply continue printing the ram contents until it segfaults.
Use write() to write the data back to the file and check for errors. Don't forget to close() the file and check that for errors too.
Your code should/could look like:
int readfile(int thread_fd, char *path)
{
unsigned int bytes_read;
char buffer[MAXSIZE];
int new_file;
if ((new_file = open(path, _O_CREAT|_O_BINARY,_S_IWRITE)) == -1) return -1;
while ((bytes_read = read(thread_fd, buffer, MAXSIZE)) > 0)
{
if (write(new_file, buffer, bytes_read)!= bytes_read) {
close(new_file);
return -2;
}
}
close(new_file);
return 0;
}
There are a few issues with your code that can cause this.
The most likely cause is this :
if(bytes_read < MAXSIZE)
break;
This ends the loop when read returns less than the requested amount of bytes. This is however perfectly normal behavior, and can happen eg. when not enough bytes are available at the time of the read call (it's reading from a network socket after all). Just let the loop continue as long as read returns a value > 0 (assuming the socket is a blocking socket - if not, you'll also have to check for EAGAIN and EWOULDBLOCK).
Additionally, if the file you're receiving contains binary data, then it's not a good idea to use fprintf with "%s" to write to the target file. This will stop writing as soon as it finds a '\0' byte (which is not uncommon in binary data). Use fwrite instead.
Even if you're receiving text (suggested by the html file extension), it's still not a good idea to use fprintf with "%s", since the received data won't be '\0' terminated.
This worked!
ps: I don't know if I should be doing this, since I am new here, but really there is no reason for negativity. Any question is a good question. Just answer it if you know it. Do not judge it.
#define MAXSIZE 1000
int bytes_read, thread_fd, new_file;
char buffer[MAXSIZE];
memset(buffer, 0, MAXSIZE);
if((new_file = open(path, O_RDONLY | O_WRONLY | O_CREAT)) < 0)
{
printf("can not open file \n");
exit(EXIT_FAILURE);
}
while ((bytes_read = read(thread_fd, buffer, MAXSIZE)) > 0)
write(new_file, buffer, bytes_read);
close(new_file);

Transfer file in C delievers mangled data

I'm a newbie in C programming. I am writing a function where the client copies a file from the server. However, when I open my newly created file it contains a lot of additional characters. How can I prevent it from copying useless data?
Relevant sections of the server follow
if (file = fopen(buf, "r")){
//send the file
// while(fgets(buffer, 1024, file) != NULL){
// res = write(new_fd, &buffer, sizeof(buffer));
// }
while(!feof(file)){
fscanf(file,"%s",buffer);
write(new_fd, &buffer, sizeof(buffer));
}
fclose(file);
}
Relevant sections of the client follow
fp = fopen ("testfile", "w");
while(read(sockfd, &buffer, sizeof(buffer)) != -1){
fputs(buffer, fp);
}
fclose(fp);
Two things: First don't do while (!feof(...)), it doesn't work as you expect it to. The reason is that the EOF flag is not set until after a failed read operation, so you will call fscanf once when the file has already reached the end. Instead to while (fscanf(...) == 1).
Secondly, depending on how you declare buffer, don't use &buffer or sizeof(buffer). Neither in the sender or the receiver. If buffer is a pointer then &buffer will return a pointer to that pointer, and sizeof(buffer) will return the size of the pointer and not what it points to. Besides, if buffer is an array then it might not be completely filled by the input, so why send data you don't need? Only send strlen(buffer) + 1 bytes (the +1 is for the string terminator).
Oh and a third thing, don't use fscanf to read a line, use fgets instead. Or even better, to be more effective, use fread to fill the complete buffer and send it all in less calls.
read() does not null terminate it's buffer. fputs requires a null terminated buffer.
while(read(sockfd, &buffer, sizeof(buffer)) != -1){
fputs(buffer, fp);
You can add the terminator with something like this:
int n = 0;
char buffer[SOME_CONSTANT];
while((n = read(sockfd, buffer, sizeof(buffer - 1))) != -1){
buffer[n] = 0;
fputs(buffer, fp);
}
Note also the declaration and use of buffer vs &buffer in the call to read.
Finally, see all of Joachim Pileborg's suggestions!

Segmentation fault on sending text from file from server to client

I'm currently still programming a simple client-server application in C via Ubuntu. So far my login function seems to have worked well (enter some text in client, grab text from a file in server and verify), but this particular display option is giving me trouble.
Some snippets of the server-side code (I grabbed the file copy to buffer function below from another site):
char bflag[1]; //mainmenu option recveived from client
char buffer[BUFSIZE+1]; //BUFSIZE is 1024
long lSize;
size_t result;
FILE * userf;
userf = fopen("Books.txt", "r+b");
recv(new_sockfd, bflag, BUFSIZE, 0); //receive flag from clientside
if ( (strncmp (bflag, "a", 1)) == 0) //display flag received
{
fseek (userf , 0 , SEEK_END);
lSize = ftell (userf);
rewind (userf);
// copy the file into the buffer:
result = fread (buffer,1,lSize,userf);
send(new_sockfd, buffer, BUFSIZE, 0);
}
fclose(userf);
And on the client side, utilizing a switch for the various options:
char bbuf[BUFSIZE+1]; //BUFSIZE is 1024
switch (mmenuc)
{
case 1:
{
strcpy (mmenuf, "a");
send (sockfd, mmenuf, BUFSIZE,0);//send flag 'a' to server
system("clear");
printf("Listing of books available:\n");
printf("O = Available X = Unavailable\n");
printf("\n");
recv (sockfd, bbuf, BUFSIZE,0);
printf ("%s", bbuf);
printf("\n");
getchar(); //eats the "\n"
getchar(); //to pause
break;
}
The problem that I am facing now is that the all the text in the file is retrieved and appears on the client side terminal fine, but on the server side terminal it gives a Segmentation Fault.
I assume there's a buffer overflow somewhere, but I'm not sure what's causing it.
Also, the Books.txt file is padded with spaces for an editing function later.
The server probably stores something like "a< cr >< lf >" in the buffer "Bflag". Not good. Should cause an error, but does not always cause one immediately.
You do not need to figure out the size of your file before you do the read:
Just issue: result = fread (buffer,1,BUFSIZE,userf);
Now, if your file ends up being larger than the buffer, your program won't crash but just not read all the file. You can change your working program later on to handle the case that the file is larger than one buffer. Use "result" (if it is larger than zero) for the number of bytes-to-write to the client.
If your file is (more than a few bytes) larger than BUFSIZE, it will probably cause a "segmentation fault" on exit of the function you provided in the first codeblock. I think that's where your segmentation fault comes from.

Resources