Reputation: 23
I am writing a c program to transfer multiple files from a client to a server. The client is sending 20 bytes at a time. When it reaches the end of the file, I am sending a flag (just a string saying "done") to the server, so it will know when the first file ends. But the problem is when the client is sending the last few remaining bytes of the file which might not be 20 bytes, the server on the other side would be trying to receive 20 bytes.
So what happens is that the last remaining bytes (assume 15 bytes for understanding the problem) of the file is sent in one send(). And after the file has ended, another send() to send the flag (which is of size 5bytes) will be read by one recv(). Because of this, the flag will never be recognized at the server and as the client starts to send the second file, the server will continue on to append the second file contents to the first one.
What can be done so that I can transfer multiple files without mixing them up (i.e distinguish between different files )?
(Also I don't want to share the file size with the server before sending the file)
I appreciate any suggestions!
Client code:
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <signal.h>
#define PORT 8080
#define BUFSIZE 20
int main(int argc, char const *argv[])
{
struct sockaddr_in address;
int sock = 0, valread;
struct sockaddr_in serv_addr;
char status[15]={0};
char buffer[BUFSIZE]={0};
int read_size=0,i=1,sent_size=0;
FILE *f1,*f2;
f1=fopen("M1.txt","r");
f2=fopen("M2.txt","r");
if(f1==NULL)
{
printf("Unable open file\n");
exit(0);
}
if(f2==NULL)
{
printf("Unable open file\n");
exit(0);
}
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
while((read_size=fread(buffer,1,BUFSIZE,f1))>0)
{
sent_size=send(sock,buffer,read_size , 0 );
fprintf(stderr,"%d th sent_size %d\n",i,sent_size); //Just printing how many bytes have been sent in every iteration.
if(read_size!=BUFSIZE)
{
fprintf(stderr,"%dth read... read_size is not 20 and it is %d\n",i,read_size ); //printing the last few remaining bytes when the last read from the file might not have exact 20 bytes
}
i++;
}
strcpy(status,"done"); //Flag to be sent to the server to indicate that the file transfer has been completed
send(sock,status,strlen(status)+1, 0 );
printf("First file sent\n");
for(i=0;i<BUFSIZE;i++)
buffer[i]='\0';
i=1;
while((read_size=fread(buffer,1,BUFSIZE,f2))>0)
{
sent_size=send(sock,buffer,read_size , 0 );
fprintf(stderr,"%d th sent_size %d\n",i,sent_size); //Just printing how many bytes been sent in every iteration.
if(read_size!=20)
{
fprintf(stderr,"%d th read...read_size is not 20 and it is %d\n",i,read_size );//printing the last few remaining bytes when the last read from the file might not have exact 20bytes
}
i++;
}
send(sock,status,strlen(status)+1, 0 );
printf("Second file sent\n");
fclose(f1);
fclose(f2);
close(sock);
return 0;
}
Server code :
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 8080
#define BUFSIZE 20
int main(int argc, char const *argv[])
{
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char status[15]={0},buffer[BUFSIZE]={0};
int read_size=0,i=1,j;
FILE *f1,*f2;
f1=fopen("R1.txt","w+");
f2=fopen("R2.txt","w+");
if(f1==NULL)
{
printf("Unable open file\n");
exit(0);
}
if(f2==NULL)
{
printf("Unable open file\n");
exit(0);
}
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
if (bind(server_fd, (struct sockaddr *)&address,sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server Waiting\n");
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
while((read_size=recv(new_socket,buffer,BUFSIZE,0))>0)
{
printf("%d th Read size %d \n",i,read_size );
if(read_size!=BUFSIZE)
{
printf("%d th read... read size is:%d, Data read : ",i,read_size );
for(j=0;j<read_size;j++) //Printing the contents of the buffer when read size is less than 20 ()
printf("%c",buffer[j]);
printf("\n");
if(strcmp(buffer,"done")==0)
{
printf("Flag received : done\n");
break;
}
}
fwrite( buffer,sizeof(char),read_size,f1);
i++;
}
printf("\nFirst File received\n");
for(i=0;i<BUFSIZE;i++)
buffer[i]='\0';
i=1;
while((read_size=recv(new_socket,buffer,BUFSIZE,0))>0)
{
printf("%d th Read size %d \n",i,read_size );
if(read_size!=BUFSIZE)
{
printf("%d th read... read size is:%d, Data read : ",i,read_size );
for(j=0;j<read_size;j++) //Printing the contents of the buffer when read size is less than 20
printf("%c",buffer[j]);
printf("\n");
if(strcmp(buffer,"done")==0)
{
printf("Flag received : done");
break;
}
}
fwrite( buffer,sizeof(char),read_size,f2);
i++;
}
printf("\nSecond File received\n");
fclose(f1);
fclose(f2);
close(new_socket);
return 0;
}
Server Output:
Server Waiting
1 th Read size 20
2 th Read size 20
3 th Read size 20
4 th Read size 20
5 th Read size 20
6 th Read size 20
7 th Read size 20
8 th Read size 20
9 th Read size 20
10 th Read size 20
11 th Read size 20
12 th Read size 8
12 th read... read size is:8, Data read : e port.
13 th Read size 5
13 th read... read size is:5, Data read : done
Flag received : doneFirst File received
Second File received
Client Output:
1 th sent_size 20
2 th sent_size 20
3 th sent_size 20
4 th sent_size 20
5 th sent_size 20
6 th sent_size 15
6th read... read_size is not 20 and it is 15
First file sent
1 th sent_size 20
2 th sent_size 20
3 th sent_size 20
4 th sent_size 20
5 th sent_size 20
6 th sent_size 8
6 th read...read_size is not 20 and it is 8
Second file sent
As seen in the Client's output the 6th read from the file is only 15 bytes,so the client is sending 15 bytes. After that, a flag ("done") is sent (which i am not counting). At the Server's output the 6th read size should have been 15. But its reading the flag along with the data.
Upvotes: 1
Views: 3201
Reputation: 24857
Use a different protocol on top of TCP that does not need sending the length, eg. using escape sequences:
[esc][esc] is an actual, single, [esc] char.
[esc][NUL] is the end-of-file marker.
The downside that you have to go through evey tx byte to insert the sequences as needed and to examine every char that is received. This usually means a byte-by-byte state-machine:(
An upside is that you can start sending the data as soon as first bit of it is available. If the data is characterized by some latency in generating it, this could easily result in the peer receiving the whole data earlier than if it was necessary to build up an entire file first, (to accurately determine the length), before any transmission at all can start. Suppose, for example, you were compressing the data file with a scheme that cannot predict the final total length of the compressed data to be sent, (like a zip), but can generate 8K blocks as it goes along - you can send the first 8K as soon as it becomes available and continue compressing the rest in parallel with the transmissions.
Upvotes: 1
Reputation: 1438
What can be done so that I can transfer multiple files without mixing them up (i.e distinguish between different files )? (Also I don't want to share the file size with the server before sending the file)
one alternative would be before sending the file to the server: On Client side:
1] get the actual file size
2] pad the file you want to desired size
3] Later send the actual file size.
dd if=/dev/zero bs=1 count=xxxx >> file_transfer.txt
where xxx is a actual file size + some diff which is divisible by 20 which you want.
On Server side:
1] Receive the file completely which is padded.
2] Receive actual file size.
3] unpad the padded bytes to get actual file.
Upvotes: 0
Reputation: 8534
Try to use SOCK_SEQPACKET
instead of SOCK_STREAM
. Quote from the socket(2):
SOCK_SEQPACKET Provides a sequenced, reliable, two-way connection-based data
transmission path for datagrams of fixed maximum length; a con‐
sumer is required to read an entire packet with each input system
call.
Upvotes: 0