如何通过c中的套接字发送多个文件?

时间:2018-02-16 11:50:36

标签: c sockets network-programming

我正在编写一个c程序,用于将多个文件从客户端传输到服务器。客户端一次发送20个字节。当它到达文件的末尾时,我正在向服务器发送一个标志(只是一个字符串说“已完成”),因此它将知道第一个文件何时结束。但问题是当客户端发送文件的最后几个剩余字节(可能不是20个字节)时,另一端的服务器将尝试接收20个字节。
那么会发生的是,文件的最后剩余字节(假设15个字节用于理解问题)在一个send()中发送。文件结束后,另一个send()发送标志(大小为5bytes)将由一个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]='\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;
}

服务器代码:

#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;
}

服务器输出:

  

服务器等待
1读取大小20
2读取大小20
3读取大小20
  4 th读取尺寸20
5读取尺寸20
6读取尺寸20
7读取   尺寸20
8读取尺寸20
9 th读取尺寸20
10读取尺寸20
11   读取尺寸20
12读取尺寸8
12读取...读取尺寸为:8,数据   读:e端口。   
  第13次读取大小5
13读取...读取大小为:5,数据读取:完成
标记   收到:完成

     

收到第一份文件

     

收到第二份文件

客户输出:

  

1 th sent_size 20
2 sent_size 20
3th sent_size 20
4 th   sent_size 20
5 sent_size 20
6th sent_size 15
第6次阅读...   read_size不是20,它是15
第一个文件发送第1个sent_size 20
2   th sent_size 20
3th sent_size 20
4th sent_size 20
5 sent_size   20第6次sent_size 8第6次读取... read_size不是20,而是8   发送第二个文件

如客户端输出中所示,文件中的第6次读取仅为15个字节,因此客户端发送15个字节。之后,发送一个标志(“完成”)(我不算数)。在服务器的输出端,第6个读取大小应该是15.但是它会随着数据一起读取标志。

3 个答案:

答案 0 :(得分:1)

在TCP之上使用不需要发送长度的不同协议,例如。使用转义序列:

  •   

    [esc] [esc]是一个实际的单一[esc]字符。

  •   

    [esc] [NUL]是文件结束标记。

您必须通过evey tx字节根据需要插入序列并检查收到的每个字符的缺点。这通常意味着逐字节状态机:(

一个好处是,只要第一位可用,您就可以开始发送数据。如果数据的特征在于生成它的某些延迟,这可能很容易导致对等体先接收整个数据,而不是必须先建立整个文件(以准确地确定长度),然后才进行任何传输。可以开始。例如,假设您使用的方案压缩数据文件无法预测要发送的压缩数据的最终总长度(如zip),但可以生成8K块 - 您可以发送第一个8K一旦可用,并继续与变速器并联压缩其余部分。

答案 1 :(得分:0)

尝试使用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.

答案 2 :(得分:0)

  

可以做什么,以便我可以在不混合的情况下传输多个文件   它们(即区分不同的文件)? (我也不想要   在发送文件之前与服务器共享文件大小)

在将文件发送到服务器之前有一个替代方案: 在客户端:

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&gt;&gt; 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.