[SOLVED] TCP File Transfer in C with socket (server/client) on Linux. Help with code
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
TCP File Transfer in C with socket (server/client) on Linux. Help with code
Hello! Thank you in advance for reading this and possibly helping out.
I am trying to make a file transfer between server and client, but is working very badly. Basically what needs to happen is:
1) The client send a txt file to the server (i called it "quotidiani.txt")
2) The server saves it in another txt file ("receive.txt")
3) The server runs a script on it that modifies it and saves it with another name ("output.txt")
4) The server send the file back to the client that saves it (on the same socket) with the name ("final.txt")
The problem is that the first file (quotidiani.txt) is read just for a little part (I actually think i read just the first 512 bytes, not sure tho), and then there are some errors. I am not very proficient in C, what i wrote i found around on stack overflow and some other sites, and i read some tutorials and guides (like beej). I'd like someone to help me understand and correct my errors. I spent hours and hours on this but i can't find a solution...
To be totally clear, this IS a university project, and i'm not asking you to make it for me, but to point me to my errors in ways i can understand them (and correct them). As i said i'm not very good in C, but i want to get better.
The code for client is:
Code:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT 20000
#define LENGTH 512
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
/* Variable Definition */
int sockfd;
int nsockfd;
char revbuf[LENGTH];
struct sockaddr_in remote_addr;
/* Get the Socket file descriptor */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
error("ERROR: Failed to obtain Socket Descriptor!\n");
}
/* Fill the socket address struct */
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(PORT);
inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr);
bzero(&(remote_addr.sin_zero), 8);
/* Try to connect the remote */
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
error("ERROR: Failed to connect to the host!\n");
}
else
printf("[Client] Connected to server at port %d...ok!\n", PORT);
/* Send File to Server */
//if(!fork())
//{
char* fs_name = "/home/aryan/Desktop/quotidiani.txt";
char sdbuf[LENGTH];
printf("[Client] Sending %s to the Server...", fs_name);
FILE *fs = fopen(fs_name, "r");
if(fs == NULL)
{
printf("ERROR: File %s not found.\n", fs_name);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
//int success = 0;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs))>0)
{
if(send(sockfd, sdbuf, fs_block_sz, 0) < 0)
{
printf("ERROR: Failed to send file %s.\n", fs_name);
break;
}
bzero(sdbuf, LENGTH);
}
printf("Ok File %s from Client was Sent!\n", fs_name);
//success = 1;
//}
/* Receive File from Server */
printf("[Client] Receiveing file from Server and saving it as final.txt...");
char* fr_name = "/home/aryan/Desktop/progetto/final.txt";
FILE *fr = fopen(fr_name, "a");
if(fr == NULL)
printf("File %s Cannot be opened.\n", fr_name);
else
{
bzero(revbuf, LENGTH);
int fr_block_sz = 0;
int success = 0;
//while(success == 0)
//{
while(fr_block_sz = recv(sockfd, revbuf, LENGTH, 0))
{
if(fr_block_sz < 0)
{
error("Receive file error.\n");
}
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if(write_sz < fr_block_sz)
{
error("File write failed.\n");
}
else if(fr_block_sz)
{
break;
}
bzero(revbuf, LENGTH);
}
printf("Ok received from server!\n");
success = 1;
fclose(fr);
//}
}
close (sockfd);
printf("[Client] Connection lost.\n");
return (0);
}
The code for server is:
Code:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT 20000
#define BACKLOG 5
#define LENGTH 512
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main ()
{
/* Defining Variables */
int sockfd;
int nsockfd;
int num;
int sin_size;
struct sockaddr_in addr_local; /* client addr */
struct sockaddr_in addr_remote; /* server addr */
char revbuf[LENGTH]; // Receiver buffer
/* Get the Socket file descriptor */
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
{
error("ERROR: Failed to obtain Socket Descriptor.\n");
}
else
printf("[Server] Obtaining socket descriptor successfully.\n");
/* Fill the client socket address struct */
addr_local.sin_family = AF_INET; // Protocol Family
addr_local.sin_port = htons(PORT); // Port number
addr_local.sin_addr.s_addr = INADDR_ANY; // AutoFill local address
bzero(&(addr_local.sin_zero), 8); // Flush the rest of struct
/* Bind a special Port */
if( bind(sockfd, (struct sockaddr*)&addr_local, sizeof(struct sockaddr)) == -1 )
{
error("ERROR: Failed to bind Port.\n");
}
else
printf("[Server] Binded tcp port %d in addr 127.0.0.1 sucessfully.\n",PORT);
/* Listen remote connect/calling */
if(listen(sockfd,BACKLOG) == -1)
{
error("ERROR: Failed to listen Port.\n");
}
else
printf ("[Server] Listening the port %d successfully.\n", PORT);
int success = 0;
while(success == 0)
{
sin_size = sizeof(struct sockaddr_in);
/* Wait a connection, and obtain a new socket file despriptor for single connection */
if ((nsockfd = accept(sockfd, (struct sockaddr *)&addr_remote, &sin_size)) == -1)
{
error("ERROR: Obtaining new Socket Despcritor.\n");
}
else
printf("[Server] Server has got connected from %s.\n", inet_ntoa(addr_remote.sin_addr));
/*Receive File from Client */
char* fr_name = "/home/aryan/Desktop/receive.txt";
FILE *fr = fopen(fr_name, "a");
if(fr == NULL)
printf("File %s Cannot be opened file on server.\n", fr_name);
else
{
bzero(revbuf, LENGTH);
int fr_block_sz = 0;
//int success = 0;
//while(success == 0)
//{
while(fr_block_sz = recv(nsockfd, revbuf, LENGTH, 0)) //could it be sockfd?
{
if(fr_block_sz < 0)
{
error("Error receiving file from client to server.\n");
}
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if(write_sz < fr_block_sz)
{
error("File write failed on server.\n");
}
else if(fr_block_sz)
{
break;
}
bzero(revbuf, LENGTH);
}
printf("Ok received from client!\n");
fclose(fr);
//}
}
/* Call the Script */
system("cd ; chmod +x script.sh ; ./script.sh");
/* Send File to Client */
//if(!fork())
//{
char* fs_name = "/home/aryan/Desktop/output.txt";
char sdbuf[LENGTH]; // Send buffer
printf("[Server] Sending %s to the Client...", fs_name);
FILE *fs = fopen(fs_name, "r");
if(fs == NULL)
{
printf("ERROR: File %s not found on server.\n", fs_name);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs))>0)
{
if(send(nsockfd, sdbuf, fs_block_sz, 0) < 0)
{
printf("ERROR: Failed to send file %s.\n", fs_name);
exit(1);
}
bzero(sdbuf, LENGTH);
}
printf("Ok sent to client!\n");
success = 1;
close(nsockfd);
printf("[Server] Connection with Client closed. Server will wait now...\n");
while(waitpid(-1, NULL, WNOHANG) > 0);
//}
}
}
I really think errors are mostly on the client.c. Any advice is very welcome too! Thanks a lot and have a nice day!
In both your client and server code, you have the following statements:
Code:
else if(fr_block_sz)
{
break;
}
You probably do not want this. Instead, you want to loop again so that you can perform another recv() call. Perhaps this code was left over from a previous version of your code where the outer while-loop was not commented out?
Mmmmm I'm not 100% sure, but that should be there because the recv() can return 0 (since i handle the > and < 0). Why isn't that useful? I'm asking because i tried to do a lot of tests, and that's why the outer while is commented, and i'm not finding any difference with it commented or not (the outer while).
For what i understand (probably is wrong, but still) what should happen is this:
If fr_block_sz is 0, then the condition below evaluates to false, not true as you are thinking. If fr_block_sz is either -1 or greater than 0, then the statement evaluates to true:
Code:
else if(fr_block_sz)
recv() can return some number N that is either -1, 0, or some number greater than 0. A -1 implies an error, but not necessarily one that requires you to give up on receiving. Check the value of errno; if it is equal to EWOULDBLOCK (or EAGAIN) or EINTR then you may want to try calling recv() again.
When a server is receiving data from a client, and a result of 0 is returned, this is a good clue that the client has disconnected. Obviously when N is greater than 0, then data has been successfully received.
My earlier comment was based on the initial code you presented; when N is not equal to zero, the condition you had programmed will evaluate to true, and thus you will exit the while-loop. The while-loop that is commented out has no bearing on the program you were executing. Thus your server performed one successful recv(), then wrote some data to a file, and then exited the while-loop. I do not think this is what you wanted.
Last edited by dwhitney67; 06-29-2012 at 10:08 AM.
Hey, thanks a lot that really helped me! I'm stuck with something else now tho.
I changed both server and client code to this:
server:
Code:
/*Receive File from Client */
char* fr_name = "/home/aryan/Desktop/receive.txt";
FILE *fr = fopen(fr_name, "a");
if(fr == NULL)
printf("File %s Cannot be opened file on server.\n", fr_name);
else
{
bzero(revbuf, LENGTH);
int fr_block_sz = 0;
while((fr_block_sz = recv(nsockfd, revbuf, LENGTH, 0)) >= 0)
{
if (fr_block_sz == 0)
{
break;
}
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if(write_sz < fr_block_sz)
{
error("File write failed on server.\n");
}
bzero(revbuf, LENGTH);
}
if(fr_block_sz < 0)
{
error("Error receiving file from client to server.\n");
}
printf("Ok received from client!\n");
fclose(fr);
}
Client:
Code:
/* Receive File from Server */
printf("[Client] Receiveing file from Server and saving it as final.txt...");
char* fr_name = "/home/aryan/Desktop/progetto/final.txt";
FILE *fr = fopen(fr_name, "a");
if(fr == NULL)
printf("File %s Cannot be opened.\n", fr_name);
else
{
bzero(revbuf, LENGTH);
int fr_block_sz = 0;
while((fr_block_sz = recv(sockfd, revbuf, LENGTH, 0)) >= 0)
{
if (fr_block_sz == 0)
{
break;
}
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if(write_sz < fr_block_sz)
{
error("File write failed.\n");
}
bzero(revbuf, LENGTH);
}
if(fr_block_sz < 0)
{
error("Receive file error.\n");
}
printf("Ok received from server!\n");
fclose(fr);
}
And now the first file get sent completely and the script works on it. The problem is that to send this first file i have to ^C (terminate) the client process... i don't know why but if i don't ^C it just keep being stuck. As soon as i do that it starts transferring the file and running the script.
Thanks again i've been stuck for days on that thing!
The recv() call in your client blocks until there is data to be read. One way to mitigate the blocking is to setup the socket to be, well, non-blocking. However this presents issues such as I described earlier, where recv() may return -1 but errno indicates EWOULDBLOCK (which is the same as EAGAIN). This is not an error, but merely an indicator that there is nothing to read.
One way to avoid blocking on recv() is to not call that function unless you know that there is indeed something to be read on the socket. You can check the socket using select(). If you provide it a timeout period, the client can avoid being locked up.
A third alternative is to set a "receive timeout" on the socket; you can do this like this:
If after 5 seconds, there is no data to receive, the recv() will unblock and return a -1 (it's been a while since I've worked with this, so the -1 is a guess; it may return 0).
First of all, thank you very very much. You have been immensely(?) helpful.
I'm not completely getting it... Basically the recv() blocks the client until there is data to be read, so since it has to send first it can't send? But isn't the send called first in the client? (client->server->script->server->client). Does this work for the server too? (do i have to make the server non-blocking too?).
As for the select() i'm reading it now, and it seems awfully complex to set up (or i just suck at this), not that it can't be done, just seem complicated.
I'm looking at the setsockopt too, and this seems like something i can set up, but i am not getting where should i put it (probably the answer lies in my first question).
Anyway this is the return value for setsockopt (incase u care)
Upon successful completion, setsockopt() shall return 0. Otherwise, -1 shall be returned and errno set to indicate the error.
I'm gonna read it now so i can try to understand better. Thanks a lot! (again).
I'm looking at the setsockopt too, and this seems like something i can set up, but i am not getting where should i put it (probably the answer lies in my first question).
You call setsockopt() after you have created the socket. For example:
Code:
...
int sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd > 0)
{
struct timeval timeout = {5, 0};
if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (const void*) &timeout, sizeof(timeout)) < 0)
{
fprintf(stderr, "Error configuring socket for rcv-timeout (errno = %d)\n", errno);
return -1;
}
/*... connect to server ... */
/* receive data */
char buf[1024];
int ret = 0;
do
{
ret = recv(sd, buf, sizeof(buf), 0);
if (ret < 0)
{
if (errno == EAGAIN)
{
printf("recv() timed out.\n");
}
else
{
fprintf(stderr, "recv() failed due to errno = %d\n", errno);
}
}
} while (ret >= 0);
}
...
Hey, I implemented (ok, i copied) the setsockopt the same way it is in the example (and again, thanks).
That's what happens:
case 1: with fork()
I start the client and server. After 5 sec i get this:
Code:
[Client] Connected to server at port 20000...ok!
[Client] Sending /home/aryan/Desktop/quotidiani.txt to the Server...Ok File /home/aryan/Desktop/quotidiani.txt from Client was Sent!
[Client] Receiveing file from Server and saving it as final.txt...recv() timed out.
[Client] Receiveing file from Server and saving it as final.txt...recv() timed out. (Since client closes i don't receive this obviusly)
Ok received from server!
Ok received from server!
[Client] Connection lost.
[Client] Connection lost. (And it closes)
and then the server receive the file from client, apply the script and hangs waiting.
case 2: without fork()
Since there were 2 [Client] messages i tought of eliminating the fork, the result is basically the same:
Code:
[Client] Connected to server at port 20000...ok!
[Client] Sending /home/aryan/Desktop/quotidiani.txt to the Server...Ok File /home/aryan/Desktop/quotidiani.txt from Client was Sent!
[Client] Receiveing file from Server and saving it as final.txt...recv() timed out.
Ok received from server!
[Client] Connection lost.
Also, i tried commenting the close(sockfd); hoping that the client would not exit, but it does, so i guess there is an error of some kind.
I'm happy this is now kinda working (well is getting better atleast) but the client still closes, whether i do it manually or with the sockopt...And I don't get where is the error!
This thing is driving me crazy :S
the script that you mentioned as script.sh. i need this program for interfacing an ARM9 cpu for data acquisition. can you please tell me of what should i do if i need to send a data file stored in the CPU when asked by the client. I really need this help. I am not from CS background and have a big problem in understanding c programs.
@tanmoy_sil - This thread has been dead for 3+ years, so better to open your own thread with your own question describing what you actually need. Asking for code without any description of what you actually mean is very vague to say the least, and only clutters the forum and search results.
Ah! We cross posted, I see you provided some detail. Still, if you could start your own thread with your own specific requirements you would get better results.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.