c socket file transfer - c

Let me start by saying this is a homework assignment and I am not a c programmer. I have been working on this for days and I am stuck. I have read the beej guide from cover to cover and have been searching google for a week, it's time to ask for help. I have a client-server TCP socket application that sends and receives messages as expected, now I need to implement simple file upload/download functionality.
The code below almost works but it adds 4 bytes to the beginning of the copied file, two non-printable characters followed by \00\00 and the client no longer responds.
The client is connected by a non-blocking socket using the select command.
I know there is lots of room for improvement but can someone help me get started?
// Server
void put (int sockfd, char *localfile) {
// Get file Size
FILE *file;
int size;
file = fopen(localfile, "rb");
fseek(file, 0, SEEK_END);
size = ftell(file);
fseek(file, 0, SEEK_SET);
//Send file Size
write(sockfd, &size, sizeof(int));
//Send file as Byte Array
char send_buffer[size];
memset(send_buffer, 0, sizeof(send_buffer));
//while(!feof(file)) {
// fread(send_buffer, 1, sizeof(send_buffer), file);
// write(sockfd, send_buffer, sizeof(send_buffer));
// memset(send_buffer, 0, sizeof(send_buffer));
//}
int sent;
while((sent = fread(send_buffer, sizeof(char), sizeof(send_buffer), file)) > 0)
{
if(send(sockfd, send_buffer, sent, 0) < 0)
{
fprintf(stderr, "[Server] ERROR: Failed to send file %s. (errno = %d)\n", localfile, errno);
break;
}
memset(send_buffer, 0, sizeof(send_buffer));
}
fclose(file);
}
//Client
void get(int sockfd, char *remotefile) {
FILE *file;
int size;
//Read file Size
read(sockfd, &size, sizeof(int));
//Read file Byte Array
char p_array[size];
memset(&p_array, 0, sizeof(p_array));
read(sockfd, p_array, size);
//Convert it Back into file
file = fopen(remotefile, "wb");
fwrite(p_array, 1, sizeof(p_array), file);
fclose(file);
}

You are making the usual error of ignoring the read count when reading the socket, and assuming that each read fills the buffer. You can't assume that.
Your send loop doesn't make that mistake. So, use that as a model, but redo it for receiving so as to use read() instead of fread(). You should then see that there's no need to allocate a buffer the size of the file, and there is therefore no need to send the filesize ahead of the file, unless you're planning on keeping the connection open for another purpose.
There's also no reason for any of the memset() calls.

Related

TCP send a file data from client to server problem: different checksum on file

I try to transfer a data size of around 100MB over a TCP ipv4 connection socket.
I calculate the CheckSum in the client before sending it to see what the checksum is.
After sending the data file to the server and the server writes it to a new file I calculate again the checksum and I can see a differents.
I think is probably with my send and receive functions.
The Sender function used in CLIENT :
void send_file(FILE *fp, int sockfd) {
int n;
char data[SIZE] = {0};
while (fgets(data, SIZE, fp) != NULL) {
if (send(sockfd, data, sizeof(data), 0) == -1) {
perror("[-]Error in sending file.");
exit(1);
}
bzero(data, SIZE);
}
}
The Writer function the use in the SERVER:
void write_file(int sockfd, char *filename) {
int n;
FILE *fp;
//char *filename = "new_data.txt";
char buffer[SIZE];
fp = fopen(filename, "w");
while (1) {
n = recv(sockfd, buffer, SIZE, 0);
if (n <= 0) {
break;
return;
}
fprintf(fp, "%s", buffer);
bzero(buffer, SIZE);
}
}
fgets() and fprintf() are used for reading and writing zero-terminated strings, not arbitrary binary data. fread() and fwrite() are your friends here. Something like:
Client:
#define CHUNK_SIZE 1024
char buffer[CHUNK_SIZE];
while ((num_bytes = fread(buffer, 1, CHUNK_SIZE, fp)) > 0)
{
send(sockfd, buffer, num_bytes, 0);
}
Server:
// Same chunk size and buffer as above
while ((num_bytes = recv(sock, buffer, CHUNK_SIZE, 0)) > 0)
{
fwrite(buffer, 1, num_bytes, fp);
}
Technically fwrite() and send() can write less bytes than you ask them to, and you really should loop until all bytes are written.
You should also technically open your files with modes "rb" and "wb" for binary files.

What is wrong with my C socket code of transferring file in successive segment

I am using Linux. C2 (client) should read and send contents of file F1.txt to C1 (server) in successive messages of size 256 bytes (or remaining size of the file when you get near the end of the file)
First, I get the F1.txt size in bytes and sent it to the server c1.
fseek(fp, 0L, SEEK_END); //move fp to the end of the file
int fileLen = ftell(fp); // get the file size because ftell returns the current value of the position indicator
fseek(fp, 0L, SEEK_SET); //seek back to the begin of the file
write(sock, &fileLen, sizeof(int)); //send file size to server c1
Next, I send the file in successive 256 bytes by a for loop. The client c2 code is
char buffer[BUF_SIZE] = {0}; //BUF_SIZE=256
for (int i = 0; i <((fileLen/256) +1); i++)
{
memset(buffer, 0, sizeof(buffer)); //clear buffer
fread(buffer, 1, i <(fileLen/256)?(sizeof(buffer)):(fileLen%256), fp); // read BUF_SIZE elements, each one with a size of 1 bytes,
printf("Message client_c2 sent: %s\n", buffer);
write(sock, buffer, i <(fileLen/256)?sizeof(buffer):(fileLen%256));
usleep(1000);
}
fclose(fp);
The server c1 read the filesize, and read the socket within the for loop:
int receiveFileLen;
read(clnt_sock, &receiveFileLen, sizeof(int));
printf("clinet_c2 file size is %d\n",receiveFileLen);
for (int i = 0; i < ((receiveFileLen/256) +1); i++)
{
memset(buffer, 0, sizeof(buffer)); //clear buffer
read(clnt_sock, buffer, i < (receiveFileLen/256) ? sizeof(buffer) :(receiveFileLen%256) );
printf("buffer that writen into file is %s\n",buffer);
fwrite(buffer, strlen(buffer), 1,fp);
}
(1) The problem is in client c2 code's fread(fp), when i printf("Message client_c2 sent: %s\n", buffer); I found every time at the end of buffer, there is a wrong char (square shape in which shows 0002.)
(2) In server c1 code. I read the socket and fwrite it to a local txt. fwrite(buffer, strlen(buffer), 1,fp); I tried strlen(buffer)+1, but it gives me wrong chars ^B in the txt, so I use strlen(buffer).
(3) In server c1 code, I cannot read the full content of the remaining size of the file when I get near the end of the file. In the for loop, I get the previous iterations correctly written into txt, but when comes to the last iteration, i = (receiveFileLen/256) in other words, only part of socket content is read and fwrite into the txt, there is still chars that should be read remaining in the socket.
Thank you for your help!

File Transfer / Write Binary Data to TCP Socket Connection in C / C++ on Unix

I‘m working on a File Transfer Program: Client writes the Binary Data to the Server over TCP Sockets.
When trying to write all the binary bytes to the socket, I don‘t know how to stop when all bytes of the Transfer File are read. The program simply does NOT stop and keeps on writing the binary bytes to the socket.
What’s the error ?
Code Example:
printf("[+] Starting File Transfer\n");
char buffer[BLOCK_BUFSIZE];
size_t contentLength = 0, rc;
FILE *absFile = fopen(absolutePath, "rb");
while ((rc = fread(&buffer, sizeof(buffer), 1, absFile))) {
if (rc == -1) {
perror("Receive {-1}");
break;
}
write(socket_fd, &buffer, rc); // Write to Socket
memset(&buffer, 0, sizeof(buffer));
contentLength += rc;
printf("[+] Writing Buffer to Socket {%zu}\n", contentLength);
}
fclose(absFile);
printf("[+] Finished Writing Buffer\n");
Any ideas, suggestions, websites, inspirations ... ?
Thx ∆

TCP server doesn't receive the right bytes number from client

I'm doing a little project of TCP connection by C language, and the problem with my code is mentioned in the title. And the following is the uploading part of my code
Client Side:
FILE * fp = fopen(f2d, "rb+");
if(fp == NULL) perror("Fail to upload(client)");
else
{
fseek(fp, 0, SEEK_END);
int filesize = ftell(fp);
memset(buf, '\0', MAX_SIZE);
sprintf(buf, "%d", filesize);
send(serverSocket, buf, strlen(buf), 0); //send the filesize to the server
rewind(fp);
int byteNum = 0, z = 0;
printf("Uploading......\n");
while((z += byteNum) < filesize)
{
memset(buf, '\0', MAX_SIZE);
byteNum = fread(&buf, sizeof(char), sizeof(buf), fp);
printf("Bytes read to buf : %d\n", byteNum);
send(serverSocket, buf, sizeof(buf), 0);
printf("Totally sent bytes: %d\n", z);
}
printf("Upload completed.\n");
}
fclose(fp);
Server Side:
printf("Upload Requested.\n");
f2df = fopen(buf + 4, "wb+");
if(f2df == NULL) perror("Fail to upload(server)");
else
{
memset(buf, 0, MAX_SIZE);
recv(clientSocket, buf, sizeof(buf), 0); //receive the filesize
int filesize = atoi(buf);
int recvNum = 0, recv_Num = 0;
while((recvNum += recv_Num) < filesize)
{
memset(buf, 0, MAX_SIZE);
recv_Num = recv(clientSocket, buf, sizeof(buf), 0);
fwrite(&buf, sizeof(char), sizeof(buf), f2df);
printf("Bytes received from recv: %d\n", recv_Num);
printf("Totally received bytes: %d\n", recvNum);
}
printf("Upload completed.\n");
}
fclose(f2df);
The idea of my code is to send and receive the bytes until it reaches the filesize. But with a very high frequency(well, sometimes it functions normally, all bytes are transferred to the server) that the server seems to miss some bytes sent from the client(and the return value of "recv" function will then be zero after the client has sent all the bytes to the server, which makes the terminating condition of the while loop never be reached), which then causes an infinitive loop on the server side.
P.S. all the other functions are without problems(e.g. sending and receiving filesize, the actual number of the filesize... etc), I had tested them all. And there's also no problem on the client side.
And the following is the screenshot of the problem
So can anyone help me out with that, I've been trying to deal with this for weeks, thanks a lot in advance!
Go through you entire client and server code and fix:
1) All those times where you do not handle correctly the results returned from system calls. This especially applies to the result returned by recv() which can be negative, (error), zero, (peer closed the connection), or some positive number less than, or equal to, the number of bytes requested - the ONLY WAY YOU CAN TELL FOR SURE HOW MANY BYTES HAVE BEEN READ IN TO THE BUFFER. Forget the number of bytes requested in the call and any memset/bzero before, and/or strlen/whatever after, it returns, they are all inadequate/misleading.
2) All those times where you assume that, just because you request the recv() of X bytes and recv() returns a positive number, that number will be X.
3) All those times that you call any str* C-style library calls on any buffers that are not 100% guaranteed to be NULL-terminated.
The main problem is the (not) handling of the send() return value, because even in the absence of any error condition, send() may return a smaller value than requested (e. g. if a buffer is full). After each loop cycle in the client, you increment z by the number of bytes read to buf, but disregard that a smaller number of bytes may have been sent. This leads to the outcome that the client loop completes without having sent all data, despite of incorrectly saying so. Change that e. g. to:
printf("Uploading......\n");
while (z < filesize)
{
byteNum = fread(buf, sizeof(char), sizeof buf, fp);
printf("Bytes read to buf : %d\n", byteNum);
int sent = 0;
while (byteNum > 0)
{
int ret = send(serverSocket, buf+sent, byteNum, 0);
if (ret < 0) { perror("send"); goto fail; }
sent += ret, byteNum -= ret;
printf("Totally sent bytes: %d\n", z += ret);
}
}
printf("Upload completed.\n");

Using read and write to do a server and a client (FTP protocol)

My code is too long to post all here so i'm going to sum up what's wrong.
In a server part i'm sending on a socket 3 things :
A message
The content of a file
Another message
In a client part i'm receiving these things but :
This first is to print on terminal
The second to write in a new file
The last to print on the terminal too
But my client is stuck on a read and i really don't know why. I'm on the problem for hour so if someone can help me, it will be very great !
edit : Basically, i think my problem is that i don't know what to write on the server to stop the read on the client.. Is it \n, \0.. ?
Here's the 2 part of code :
server
void send_content(t_server *s, FILE *fd, int rfd)
{
int len;
char *buff;
write(s->socket, "150 File status okay;" \
"about to open data connection.\n\0", strlen("150 File status okay;about to open data connection.\n\0"));
fseek(fd, 0, SEEK_END);
len = ftell(fd);
buff = malloc(len * sizeof(char));
read(rfd, buff, len);
write(s->socket, buff, len);
write(s->socket, "\n\0", strlen("\n\0"));
write(s->socket, "226 Closing data connection.\n\0", strlen("226 Closing data connection.\n\0"));
free(buff);
}
client
void getfile(t_client *c, char **tab)
{
int ret;
int fd;
int z;
char buff[4096];
z = 0;
read(c->fd, buff, 4096);
write(1, buff, strlen(buff));
if (strlen(buff) < 25)
return ;
fd = creat(tab[1], S_IRUSR | S_IWUSR);
while (z == 0 && (ret = read(c->fd, buff, 4096)) > 0)
{
if (ret < 4096)
z = -1;
write(fd, buff, strlen(buff));
memset(buff, '\0', 4096);
}
read(c->fd, buff, 4096); // Stuck here
write(1, buff, strlen(buff));
close(fd);
}
Like noted you need a read function like this to make sure you receive
specified number of bytes(this function will loop till it receives number of bytes it was told to). Just use this receivall method instead of read everywhere.
With files you typically first send the file length, and then receive the file.
I did something similar while ago, hope it will help you a bit. This is the client side, which tries to receive first file length from the server, then the file:
/* create file */
FILE * ptrMyFile = fopen(&filenames[i][0],"wb");
if(NULL == ptrMyFile)
{
printf("Unable to open file \n");
return 1;
}
int size = 0;
int t = 4;
/* first receive file size from server */
/* NOTE: error checking is omitted from code, nevertheless users should stil do some error checking when using this code */
readall(sockfd, (unsigned char*) &size, &t);
/* how many 256 byte chunks are there? */
int div = size / 256;
/* loop to receive each chunk. */
for(int k = 0; k < div; k++)
{
int chunk_size = 256;
/* make sure we receive 256 bytes */
readall(sockfd, buffer, &chunk_size);
/* write to file */
fwrite(buffer, chunk_size, 1, ptrMyFile);
}
/* read the final chunk. */
int whatsleft = size - 256 * div;
readall(sockfd, buffer, &whatsleft);
/* write */
fwrite(buffer, whatsleft, 1, ptrMyFile);
/* close file */
fclose(ptrMyFile);
I leave the server part to you.
char buff[4096];
z = 0;
read(c->fd, buff, 4096);
write(1, buff, strlen(buff));
You should be saving the return value of the call to read(), in order to find out how many bytes you just received. You may have to make several calls to read() in order to get the entire message. It's wrong to use strlen() to find out how many bytes were received, because the buffer contents are uninitialized, and the first chunk of the message could be cut off anywhere, so you can't count on it being null-terminated.

Resources