I had an assignment in networking course, where I implemented a simple client server application that downloads a file (text) from the server to the client. This I did by continuously reading the file on the server side and sending the data by send and receiving the data by recvcalls. Text files work great.
But I want to implement (not part of the assignment) a video sending ability to this app :D (no need of pen drives among friends !). I used the same code as sending file hoping that it will work. If I ran the code on my own machine ( server and client running on my machine) the video was successfully transferred. But when the server and client run on different machines (server on my friend's laptop and client on mine), I get the video file, but a corrupted one ! :(
Server snippet for sending file :
void sendFile(FILE * fp, int * arg )
{
int socket=(int)*arg;
while(1)
{
/* First read file in chunks of 256 bytes */
char buff[1024]={0};
if(fp==NULL)
{
printf("File open error");
return;
}
int nread = fread(buff,1,1024,fp);
/* If read was success, send data. */
if(nread > 0)
{
if(nread != 1024)
buff[nread] = 0;
send(socket, buff, nread,0);
}
if (nread < 1024)
{
if (ferror(fp))
printf("Error reading\n");
break;
}
}
}
Client snippet for receiving file :
FILE * fp = fopen(fileName,"wb");
if(fp == NULL)
{
printf("Error opening file");
return 1;
}
int bytesReceived = 0;
char recvBuff[1024];
while((bytesReceived = recv(socket_client, recvBuff, 1024,0)) > 0)
{
fwrite(recvBuff, 1,bytesReceived,fp);
if(bytesReceived < 1024)
break;
}
fclose(fp);
printf("File successfully downloaded!\n");
The video file after download gives the error: This file is incomplete and cannot be played.. I really want to make this network application, that I can use in my daily life! Any idea, where the snippets are getting wrong? Any Help Appreciated. Thanks in advance! :)
Another way - wait for all data of message:
while((bytesReceived = recv(socket_client, recvBuff, 1024, MSG_WAITALL)) > 0)
Related
I write program and it works fine, but i want to rewrite it using sendfile() and now i got stuck in a loop.
Server side:
send name = ok
send md5 checksum = ok
send size = ok
send file = ko
Client side:
recv name = ok
recv md5 cecksum = ok
recv size = ok
create dir and create file = ok
write data to created file = ko
P.S In previous version of program i stuck some time to, but it depend how much i use printf why? for e.x i add one line with printf program stuck, delete it, works fine.
UPDT: rewrite code client/server
client
/* Received file name */
int rc_byte = 0;
rc_byte = recv(fd, rx_tx_file->out_name, sizeof(rx_tx_file->out_name),0);
if (rc_byte < 0){
perror("Failed to receive file name: ");
exit(-1);
} else
printf("Recv out name %s\n", rx_tx_file->out_name);
//printf("file name rc %s\n", rx_tx_file->out_name);
trimm_path_name(rx_tx_file);
/* Received md5sum */
rc_byte = recv(fd, rx_tx_file->md5sum, sizeof(rx_tx_file->md5sum), 0);
if (rc_byte < 0) {
perror("Failed to receive check sum: ");
exit(-1);
} else
printf("recv md5s %s\n", rx_tx_file->md5sum);
/* Received file size */
rc_byte = recv(fd, &size, sizeof(size), 0);
if(rc_byte < 0) {
perror("Recevid size of file: ");
exit(-1);
}
printf("%d recv size\n", size);
to_read = size;
if (stat(dir, &st) == -1){
mkdir(dir, 0777);
}
send_data: (add func to server)
void send_data(int client_fd, m_file *rx_tx_file, int option, int size) {
int send_byte = 0;
int total_send = 0;
if (option == SEND_NAME) {
while (total_send < strlen(rx_tx_file->in_name)) {
send_byte = send(client_fd, rx_tx_file->in_name, sizeof(rx_tx_file->in_name),0);
if(send_byte == -1) {
perror("Failed to send file name to client: ");
exit(SEND_TO_CLIENT_ERROR);
}
total_send += send_byte;
}
}
else if (option == SEND_MD5) {
total_send = 0;
send_byte = 0;
while (total_send < strlen(rx_tx_file->md5sum)) {
send_byte = send(client_fd, rx_tx_file->md5sum, sizeof(rx_tx_file->md5sum),0);
if(send_byte == -1){
perror("Failed to send file md5sum to client: ");
exit(-1);
}
total_send += send_byte;
}
}
else if (option == SEND_SIZE) {
send_byte = send(client_fd, &size, sizeof(size),0);
if (send_byte == -1) {
perror("Failed to send size: ");
}
}
}
server:
client_fd = accept(server_fd, (struct sockaddr*) &client_addr, &length)
/*send name of file*/
send_data(client_fd, rx_tx_file, SEND_NAME, 0);
/*send md5 sum*/
take_check_sum(rx_tx_file,rx_tx_file->file_in, 0);
send_data(client_fd, rx_tx_file, SEND_MD5, 0);
/*send size of file*/
size = stats.st_size;
send_data(client_fd, rx_tx_file, SEND_SIZE, size);
remain_data = stats.st_size;
printf("File [%s] ready to send\ncheck sum [%s]\n", rx_tx_file->in_name,rx_tx_file->md5sum);
while (((send_byte = sendfile(client_fd, file_fd, &offset, size)) > 0) && (remain_data > 0))
{
remain_data -= send_byte;
printf("remain %d", remain_data);
}
printf("Succesfully");
Since i work with one client and pass file which should send on server side through command line args, i dont need to wait in while (client_fd = accpet) i just work with one connection and close server. Now its work good. But one question is open, how i should rewrite client side to recv data in a loop. I don't know which size i should recv and because of that i cant write right condition to my while loop. THX all for helping.
TCP is a stream. It has no message boundaries. Your code won't work because of that.
First, you send the name of the file:
send(client_fd, rx_tx_file->in_name, strlen(rx_tx_file->in_name)+1,0)
then you immediately send the md5 sum and then the file size:
send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0)
send(client_fd, &size, sizeof(int),0)
Since the first two strings don't have a fixed number of bytes, it's quite likely that when you try to read the file size or md5 sum from the server you also read the size of the file and maybe even some of the file data.
First, stop trying to put as much of your send and read code as you can into the conditional clause of your if and while statements.
What exactly does
if (send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0) == -1) {
perror("Failed to send file md5sum to client: ");
exit(-1);
}
gain you over
ssize_t bytes_sent = send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0);
if ( bytes_sent < 0 )
{
perror("Failed to send file md5sum to client: ");
exit(-1);
}
Putting all that code into the if clause gains you nothing on the send. And what if strlen(rx_tx_file->md5sum)+1 is 87 and the send() call returns 15? That's a possible return value that your code can't handle because it stuffs everything into the if clause.
ssize_t bytes_sent = send(client_fd, rx_tx_file->md5sum, strlen(rx_tx_file->md5sum)+1, 0);
if ( bytes_sent < 0 )
{
perror("Failed to send file md5sum to client: ");
exit(-1);
}
else if ( bytes_sent < strlen(rx_tx_file->md5sum)+1 )
{
// partial send...
}
That's actually better coded as a loop.
You didn't post your receive code, but if it's in the same style you not only don't gain anything, by putting everything into the if clause you again can't do any decent error detection or correction.
If your file name recv code is similar to
char filename[1024];
if (recv(fd, &filename, sizeof(filename), 0) < 0) {
perror("Failed to read file name: ");
exit(-1);
}
you can't tell what you just received. How many bytes did you just receive? You may have received the file name. You may have received only part of the file name. You may have received the file name, the md5 sum, and some of the file contents itself.
You don't know what you received, and with your code you can't tell. If you zero out the file name and md5 receive buffers and only recv up to one byte less than the size of the buffer, you at least avoid undefined behavior. But if you don't zero out the buffer, or if you read up the the last byte of the buffer, you can also wind up without a nul-terminated string for your filename or md5 sum. And when you then try to treat it as a nul-terminated string you get undefined behavior.
And if you did get extra bytes in the recv calls you make before trying to read the file data, that explains why your code gets stuck - it already read some of the file contents before getting to the loop, so the loop will never see all the content - some of it is gone.
You should avoid using strlen here in your server:
if(send(client_fd, rx_tx_file->in_name, strlen(rx_tx_file->in_name)+1,0) == -1)
Rather just send fixed length string of size sizeof(rx_tx_file->out_name) as you expect in your client
If the filename is smaller just pad it with spaces to make it of length sizeof(rx_tx_file->out_name)
You should also put each receive call in while loop, and add checks that it actually received expected number of bytes, at times recv will just return partial data, you need to post another recv to receive rest of the expected data
I have client server architecture wherein I transfer a binary file from server to client.
Server Code:
FILE *outFile = fopen("/tmp/localpkg.deb","wb");
if( outFile == NULL )
{
RPTLOG(1, "Error opening file for writing.\n");
return -1;
}
FILE *readFilefp = fopen(file_name, "rb");
if ( readFilefp == NULL)
{
RPTLOG(1, "Error opening file.\n");
return -1;
}
char fileBuffer[1024];
bzero(fileBuffer, 1024);
while(1)
{
size_t readSize = fread(fileBuffer, sizeof(char), sizeof(fileBuffer) -1, readFilefp);
if(readSize == 0)
{
RPTLOG(1, "No more contents are left in file.\n");
n = write(sockfd, "\r\n", 2);
break;
}
n = write(sockfd, fileBuffer,readSize);
fwrite(&fileBuffer, sizeof(char), readSize, outFile);
RPTLOG(1, "Data[%d] written to sock=%s.\n", n, fileBuffer);
bzero(fileBuffer, 1024);
}
fclose(readFilefp);
fclose(outFile);
char *endfile_var =(char*) malloc(2048);
bzero(endfile_var,100);
strcpy(endfile_var,"ENDFILE\r\n");
n = write(sockfd, endfile_var, 9 );
RPTLOG(1, "ENDFILE text sent to client[%s] NumBytes sent=%d\n", endfile_var, n );
Client code:
FILE *fp = fopen(localfile, "wb");
if( fp == NULL )
{
RPTLOG(1, "Not enough permissions to write on disk, exiting....\n");
break;
}
memset(buf, 0, 2048);
//Started receiving installation package from server
while ((ret = read(sock, buf, 2047)) > 0) //Stcuk point: read blocks over here when it is about to receive last few lines of
{ //binary file from server
buf[ret] = '\0';
if ( ret == 1 )
{
RPTLOG(1, "Caught character where ret = %d\n", ret);
if ( buf[0] == '\n' )
continue;
}
if (strstr(buf, "ENDFILE") != 0 )
{
RPTLOG(1, "Endfile detected\n");
break;
}
else
{
fwrite(buf, 1, ret, fp);
}
memset(buf, 0, 2048);
}
if( ret == 0)
{
RPTLOG(4, "Connection closed from server = %d \n", ret );
}
else if( ret < 0)
{
RPTLOG(4, "Read error on client socket= %d \n", ret );
}
fclose(fp);
My Problem:
When server is about to send last few lines of binary file, client stuck in read call [stuck point in client code posted above]. Here client program is designed in such a way that when it receives "ENDFILE" line from server it will assume that file contents have been ended from server side and come out of while loop.
So kindly suggest the solution to receive binary file from server successfully.
[Special Note:] Client code has been wrapped up in a build and installed at customer end. So I have to make changes in server side code only. Also as indicated in server code above, for debugging purpose I have written content sent on socket to '/tmp/localpkg.deb' file as well. And that file contains all the contents which were written on socket at server side.
[Special Note 2:] When I try to send plain text file, I can send it successfully. Problem comes in only while sending binary file.
Thanks in advance.
The reason why your code fails is simple enough. You are reading a chunk of binary data into your buf, and than you call strstr on this buf. strstr goes until first \0 byte when looking for given token. Obviously, binary data will have a lot of those, so search stops after first one encountered and returns 'nothing was found'.
As a result, your code never exits the read loop, and keeps waiting for more data to arrive from the server - which will never send it.
The proper solution is, of course, implement a proper protocol for sending binary data. However, if this is impossible due to the listed constraints, a semi-workable solution would be to to replace strstr function with a custom one, which will go over provided buffer ignoring the the nul-terminators (using size of the buffer instead) and looks for the token provided. It will still break if your binary data has the token in it, but there is nothing you can do about it.
I created an application that send a text file from client to server
So far i'm send it as string like this:
fp = fopen(filename, "r");
if (fp != NULL) {
newLen = fread(source, sizeof(char), 5000, fp);
if (newLen == 0) {
fputs("Error reading file", stderr);
} else {
source[++newLen] = '\0'; /* Just to be safe. */
}
}else{
printf("The file %s does not exist :(");
return 1;
}
fclose(fp);
send(s , source , strlen(source) , 0); //send file
However my professor told me I must send the file in Binary and be ready to accept a file of any size
I'm trying to figure out how to send the file in binary and break it into chunks
You can copy it one byte at a time.
Reading/writing more than a byte at a time theoretically would make it read and write more efficiently to disk. But since the binary is likely short, and disk I/O is already internally buffered it probably doesn't make a noticeable difference.
perror() is a convenient function that displays the text associated with an error code returned from the most recent UNIX system call. The text in the quotes is the title it displays before showing you the system message associated with the code.
exit(EXIT_FAILURE) exits with a -1 value which is what scripts can test to see if your program succeeded or failed, as the exit status can be retrieved for a UNIX program.
size_t is an integer type, but it's named size_t to give a hint as to what you're using it for.
If you wanted to transfer more data at a time you could. But 1-byte xfers is simple and safe and it works.
FILE *exein, *exeout;
exein = fopen("filein.exe", "rb");
if (exein == NULL) {
/* handle error */
perror("file open for reading");
exit(EXIT_FAILURE);
}
exeout = fopen("fileout.exe", "wb");
if (exeout == NULL) {
/* handle error */
perror("file open for writing");
exit(EXIT_FAILURE);
}
size_t n, m;
unsigned char buff[8192];
do {
n = fread(buff, 1, sizeof buff, exein);
if (n)
m = fwrite(buff, 1, n, exeout);
else
m = 0;
} while ((n > 0) && (n == m));
if (m)
perror("copy");
I have created a Client/Server application in C by using the SSL library. the issue i am facing is each time i send a file there are some bytes missing in the start of file.
let suppose the text file which i am sending contains
123456789
and when the client receive the file it would contains
56789
Server-Code
void sendFile(SSL* ssl)
{
char response[2048] = {0};
int read = 0;
FILE* fd;
fd = fopen("snt.txt","rb");
if (fd == NULL)
{
printf("file loading failed\n");
return;
}
while ((read=fread(response,sizeof(char),1024,fd)) > 0)
{
SSL_write(ssl,response,read);
printf("read :%d\n",read);
//puts(response);
//printf("***Data Sent***\n");
memset(response,0,1024);
}
printf("***Data Sent***\n");
fclose(fd);
}
Client Code
FILE *ft;
char filebuf[2048];
int read = 0;
int error_check=0;
ft = fopen("rcv.txt","ab");
if (ft == NULL)
{
printf("Can not open file to write\n");
return -1;
}
memset(filebuf,0,2048);
int cnk=1;
while ((error_check=BIO_read(bio,&read,sizeof(int)))>0)
{
//printf("%d read\n",read);
if (error_check==0)
break;
if (read==0)
break;
BIO_read(bio,filebuf,read);
printf("%d Chunk Recieved\n",cnk++);
//puts(filebuf);
fwrite(filebuf,sizeof(char),strlen(filebuf),ft);
memset(filebuf,0,2048);
}
printf("***File Recieved***");
fclose(ft);
the other issue is client side is not terminated, control doesn't get away from the while-loop, kindly guide me how can i tackle these issues
Assuming size(int) is 4, I'd say the 1st 4 bytes are read by this line:
while ((error_check=BIO_read(bio,&read,sizeof(int)))>0)
That leaves the rest of the data sent to this line:
BIO_read(bio,filebuf,read);
The latter reads it into filebuf which then is written to the file rcv.txt.
I'm writing a simple server for my networks class and I am having trouble getting data transferred to the client (provided by the prof) correctly.
Once I get through all the setup and the connection is established I start reading chunks of the file and writing them to the socket checking to see that the return from the read and write functions match. I'm also keeping a running total of the bytes that are read/written and comparing that to the total file size (via stat.st_size) and they all match up.
No matter how many times I request the same file the log on the server side always has the correct metrics. The client sporadically loses the end of the file. The difference between the actual and expected size is almost never the same from one invocation to the next and it appears to always be the end of the file that's missing, no pieces from the middle. The size of the arrived file is also a multiple of 512 (the chunk size).
So, it seems that some number of whole chunks are making it and then the rest are getting lost somehow.:w
#define CHUNK_SIZE 512
/* other definitions */
int main()
{
/* basic server setup: socket(), bind(), listen() ...
variable declarations and other setup */
while(1)
{
int cliSock = accept(srvSock, NULL, NULL);
if(cliSock < 0)
; /* handle error */
read(cliSock, filename, FILE_NAME_SIZE - 1);
int reqFile = open(filename, O_RDONLY);
if( reqFile == -1)
; /* handle error */
struct stat fileStat;
fstat(reqFile, &fileStat);
int fileSize = fileStat.st_size;
int bytesRead, totalBytesRead = 0;
char chunk[CHUNK_SIZE];
while((bytesRead = read(reqFile, chunk, CHUNK_SIZE)) > 0)
{
totalBytesRead += byteasRead;
if(write(cliSock, chunk, bytesRead) != bytesRead)
{
/* perror(...) */
/* print an error to the log file */
bytesRead = -1;
break;
}
}
if (bytesRead == -1)
{
/* perror(...) */
/* print an error to the log file */
close(cliSock);
continue;
}
/* more code to write transfer metrics etc to the log file */
}
}
All of the removed error handling code is some flavor of printing an error message to the log file and getting back to the top of the loop.
Edit flipped a < that should have been >
Presumably you are unceremoniously closing the socket with close() when you have written all the data you wanted to the socket (or perhaps just exiting the process, which does the same thing).
This is not right - if the other side has sent some data that you haven't read1, the connection will be reset. The reset can cause unread data to be lost.
Instead, you should use shutdown() to gracefully shutdown the writing side of the socket, then wait for the client to close. Something like:
ssize_t bytesRead;
char chunk[CHUNK_SIZE];
shutdown(cliSock, SHUT_WR);
while((bytesRead = read(cliSock, chunk, CHUNK_SIZE)) != 0)
{
if (bytesRead < 0)
{
if (errno != EINTR)
{
/* perror() */
/* print error in log file */
break;
}
}
else
{
/* maybe log data from client */
}
}
close(cliSock);
1. This can include EOF, if the other side has closed its writing channel.