在套接字编程中发送文件tcp

时间:2015-06-16 20:07:32

标签: c++ sockets tcp

我正在尝试实现简单的文件传输。以下是我一直在测试的两种方法:

方法一:发送和接收而不拆分文件。 我对文件大小进行了硬编码以便于测试。

发件人:

send(sock,buffer,107,NULL); //sends a file with 107 size

接收器:

char * buffer = new char[107];                      
recv(sock_CONNECTION,buffer,107,0);

std::ofstream outfile (collector,std::ofstream::binary);
outfile.write (buffer,107); 

输出符合预期,文件未损坏,因为我发送的.txt文件包含与原始文件相同的内容。

方法二:通过拆分接收方的内容来发送和接收。每个循环5个字节。

发件人:

send(sock,buffer,107,NULL);   

接收器:

char * buffer = new char[107];                      //total file buffer
char * ptr = new char[5];                           //buffer
int var = 5;                
int sizecpy = size; //orig size

while(size > var ){                                //collect bytes

    recv(sock_CONNECTION,ptr,5,0);

    strcat(buffer,ptr);                     //concatenate
    size= size-var;     //decrease
    std::cout<<"Transferring.."<<std::endl;

    }

    std::cout<<"did it reach here?"<<std::endl;
    char*last = new char[size];

    recv(sock_CONNECTION,last,2,0);    //last two bytes
    strcat(buffer,last);    
    std::ofstream outfile (collector,std::ofstream::binary);
    outfile.write (buffer,107); 

输出:文本文件包含无效字符,尤其是在开头和结尾。

问题:如何使方法2工作?尺寸相同但产生不同的结果。原始文件和方法2上的新文件的相似度约为98%~99%,而方法1的相似度为100%。传输文件的最佳方法是什么?

3 个答案:

答案 0 :(得分:3)

  

传输文件的最佳方法是什么?

通常我没有回答像最好的方法这样的问题。但在这种情况下,显而易见:

  1. 您在开始传输时以网络字节顺序发送文件大小和校验和
  2. 可选择发送更多标题数据(例如文件名)
  3. 客户端读取文件大小和校验和,并将其解码为主机字节顺序
  4. 您以合理大小的块发送文件的数据(5个字节不是合理的大小),块应匹配tcp / ip帧最大可用有效负载大小
  5. 您在客户端通过块接收块,直到先前发送的文件大小匹配
  6. 您在客户端计算接收数据的校验和,并检查它是否与之前收到的数据匹配
  7. 注意:您不需要在客户端将内存中的所有块组合在一起,只需将它们附加到存储介质中的文件即可。校验和(CRC)通常可以通过数据块运行来计算。

答案 1 :(得分:1)

我看到的初始问题是std::strcat。您不能在未初始化的缓冲区上使用它。此外,您不是复制空终止的c字符串。您正在复制大小的缓冲区。最好使用std::strncat

char * buffer = new char[107];                      //total file buffer
char * ptr = new char[5];                           //buffer
int var = 5;                
int sizecpy = size; //orig size

// initialize buffer
*buffer = '\0'; // add null terminator

while(size > var ){                                //collect bytes

    recv(sock_CONNECTION,ptr,5,0);

    strncat(buffer, ptr, 5); // strncat only 5 chars

    size= size-var;     //decrease
    std::cout<<"Transferring.."<<std::endl;

}

除此之外,您应该将错误检查,以便套接字库可以告诉您通信是否出错。

答案 2 :(得分:1)

不同意Galik。最好不要使用strcat,strncat或除预期输出缓冲区之外的任何东西。

TCP非常有趣。你真的不知道你将获得多少数据,但你会得到它或错误。

这将一次读取最多MAX个字节。 somethingRegex | xargs -I {} sh -c 'printf "found \033[34m{}\033[39m";dummy {}' MAX到你想要的任何东西。

#define

更有趣的是,写入输出缓冲区,这样您就不必存储整个文件。在这种情况下,MAX应该是一个更大的数字。

std::unique_ptr<char[]> buffer (new char[size]);
int loc = 0; // where in buffer to write the next batch of data
int bytesread; //how much data was read? recv will return -1 on error                        

while(size > MAX)
{                                //collect bytes
    bytesread = recv(sock_CONNECTION,&buffer[loc],MAX,0);
    if (bytesread < 0)
    {
         //handle error.
    }
    loc += bytesread;
    size= size-bytesread;     //decrease
    std::cout<<"Transferring.."<<std::endl;
}
bytesread = recv(sock_CONNECTION,&buffer[loc],size,0);
if (bytesread < 0)
{
     //handle error
}

std::ofstream outfile (collector,std::ofstream::binary);
outfile.write (buffer.get(),size);