如何通过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 一旦可用,并在传输的同时继续压缩其余部分。