如何通过c中的套接字发送多个文件?
How to send multiple files through a socket in c?
我正在编写一个 c 程序来将多个文件从客户端传输到服务器。客户端一次发送 20 个字节。当它到达文件末尾时,我将向服务器发送一个标志(只是一个表示 "done" 的字符串),因此它会知道第一个文件何时结束。但问题是当客户端发送文件的最后几个剩余字节可能不是 20 字节时,另一端的服务器将尝试接收 20 字节。
所以发生的是文件的最后剩余字节(假设 15 个字节以了解问题)在一个 send() 中发送。文件结束后,另一个发送标志(大小为 5 字节)的 send() 将由一个 recv() 读取。因此,该标志将永远不会在服务器上被识别,并且当客户端开始发送第二个文件时,服务器将继续将第二个文件内容附加到第一个文件中。
如何才能传输多个文件而不混淆它们(即区分不同的文件)?
(另外我不想在发送文件之前与服务器共享文件大小)
我很感激任何建议!
客户代码:
#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]='[=10=]';
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;
}
服务器代码:
#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]='[=11=]';
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 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 : done
First File received
Second File received
客户端输出:
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
从客户端的输出中可以看出,第 6 次读取的文件只有 15 个字节,因此客户端发送了 15 个字节。之后,发送一个标志("done")(我没有计算在内)。在服务器的输出中,第 6 次读取大小应该是 15。但是它读取标志和数据。
尝试使用 SOCK_SEQPACKET
而不是 SOCK_STREAM
。来自套接字的引用(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.
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)
一种替代方法是在将文件发送到服务器之前:
在客户端:
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
其中 xxx 是实际文件大小 + 一些可以被 20 整除的差异。
在服务器端:
1] Receive the file completely which is padded.
2] Receive actual file size.
3] unpad the padded bytes to get actual file.
在不需要发送长度的 TCP 之上使用不同的协议,例如。使用转义序列:
[esc][esc] is an actual, single, [esc] char.
[esc][NUL] is the end-of-file marker.
缺点是您必须通过每个 tx 字节才能根据需要插入序列并检查收到的每个字符。这通常意味着 byte-by-byte state-machine:(
好处是您可以在数据的第一位可用后立即开始发送数据。如果数据的特点是在生成它时有一些延迟,这很容易导致对等方比在任何传输之前首先建立整个文件(以准确确定长度)更早地接收到整个数据可以开始。例如,假设您使用无法预测要发送的压缩数据的最终总长度(如 zip)但可以生成 8K 块的方案压缩数据文件 - 您可以发送第一个8K 一旦可用,并在传输的同时继续压缩其余部分。
我正在编写一个 c 程序来将多个文件从客户端传输到服务器。客户端一次发送 20 个字节。当它到达文件末尾时,我将向服务器发送一个标志(只是一个表示 "done" 的字符串),因此它会知道第一个文件何时结束。但问题是当客户端发送文件的最后几个剩余字节可能不是 20 字节时,另一端的服务器将尝试接收 20 字节。
所以发生的是文件的最后剩余字节(假设 15 个字节以了解问题)在一个 send() 中发送。文件结束后,另一个发送标志(大小为 5 字节)的 send() 将由一个 recv() 读取。因此,该标志将永远不会在服务器上被识别,并且当客户端开始发送第二个文件时,服务器将继续将第二个文件内容附加到第一个文件中。
如何才能传输多个文件而不混淆它们(即区分不同的文件)?
(另外我不想在发送文件之前与服务器共享文件大小)
我很感激任何建议!
客户代码:
#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]='[=10=]';
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;
}
服务器代码:
#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]='[=11=]';
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 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
客户端输出:
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
从客户端的输出中可以看出,第 6 次读取的文件只有 15 个字节,因此客户端发送了 15 个字节。之后,发送一个标志("done")(我没有计算在内)。在服务器的输出中,第 6 次读取大小应该是 15。但是它读取标志和数据。
尝试使用 SOCK_SEQPACKET
而不是 SOCK_STREAM
。来自套接字的引用(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.
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)
一种替代方法是在将文件发送到服务器之前: 在客户端:
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
其中 xxx 是实际文件大小 + 一些可以被 20 整除的差异。
在服务器端:
1] Receive the file completely which is padded.
2] Receive actual file size.
3] unpad the padded bytes to get actual file.
在不需要发送长度的 TCP 之上使用不同的协议,例如。使用转义序列:
[esc][esc] is an actual, single, [esc] char.
[esc][NUL] is the end-of-file marker.
缺点是您必须通过每个 tx 字节才能根据需要插入序列并检查收到的每个字符。这通常意味着 byte-by-byte state-machine:(
好处是您可以在数据的第一位可用后立即开始发送数据。如果数据的特点是在生成它时有一些延迟,这很容易导致对等方比在任何传输之前首先建立整个文件(以准确确定长度)更早地接收到整个数据可以开始。例如,假设您使用无法预测要发送的压缩数据的最终总长度(如 zip)但可以生成 8K 块的方案压缩数据文件 - 您可以发送第一个8K 一旦可用,并在传输的同时继续压缩其余部分。