extra newlines at end of file transported over tcp

62 阅读2分钟

在使用 sendfile.cpp 发送文件时,在接收端 recvfile.py 发现有一些额外的换行符出现在文件的末尾。这个问题出现在 sendfile.cpp 的发送代码中。

解决方案

  1. 在 sendfile.cpp 中修改文件读取方式,使用二进制方式打开文件以禁用 CRLF 转换:
fin.open(filename.c_str(), ios::in | ios::binary);
  1. 调整发送文件的方式,避免逐字节发送,以提高效率:
// 发送整个文件
int readSoFar = 0;
while (readSoFar < size) {
  fin.read(buffer, 1024); // 读取 1024 字节数据到 buffer
  send(sock, buffer, 1024, 0); // 发送缓冲区中的数据
  readSoFar += 1024;
}
  1. 在 recvfile.py 中接收文件时,使用 read() 方法一次性读取所有数据,避免逐字节接收:
data = conn.recv(filelen)  # 读取所有数据到 data
  1. 在 recvfile.py 中将接受到的数据写入文件时,使用 write() 方法一次性写入所有数据,避免逐字节写入:
f.write(data)  # 将数据写入文件

代码示例:

// sendfile.cpp
#include "jmm_sockets.h"
#include <windows.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sys/stat.h>
using namespace std;

int main(int argc, char** argv)
{
  int port;
  string host;
  string filename;

  if(argc==2){
    cout << "Host: ";
    cin >> host;

    cout << "Port: ";
    cin >> port;

    filename = argv[1];
  }else if (argc == 4){
    host = argv[1];
    port = atoi(argv[2]);
    filename = argv[3];
  }else{
    cerr << "Usage: " << argv[0] << " [<host> <port>] <filename>" << endl;
    exit(1);
  }

  // open file for reading
  ifstream fin;
  fin.open(filename.c_str(), ios::in | ios::binary);
  if(fin.fail()){
    cerr << "Error: opening " << filename << " failed. " << endl;
    exit(1);
  }

  // get file size
  fin.seekg(0, ios::end);
  int size = fin.tellg();
  fin.seekg(0, ios::beg);

  // open socket for sending
  int sock = getClientSocket((char*)host.c_str(), port);

  // send file size
  char buffer[16];
  itoa(size, buffer, 10);
  int i;
  for(i=0; i<strlen(buffer); i++){
    if(send(sock, &buffer[i], 1, 0)!=1){
      cerr << "Error: send() failed " << WSAGetLastError() << endl;
      exit(1);
    }
  }
  char c = '\n';
  if(send(sock, &c, 1, 0)!=1){
    fprintf(stderr, "Error: send() failed %d\n", WSAGetLastError());
    exit(1);
  }

  // recv y or n
  int recvMsgSize = recv(sock, &c, 1, 0);
  if(recvMsgSize!=1){
    fprintf(stderr, "Error: recv() failed %d\n", WSAGetLastError());
    exit(1);
  }

  if(c=='y'){
    // send entire file
    int readSoFar = 0;
    while (readSoFar < size) {
      fin.read(buffer, 1024);  // 读取 1024 字节数据到 buffer
      send(sock, buffer, 1024, 0);  // 发送缓冲区中的数据
      readSoFar += 1024;
    }
  }else if (c=='n'){
    // leave
    cout << "Remote host declined file." << endl;
  }

  fin.close();
  closesocket(sock);
  WSACleanup();

  // 
  return 0;
}
# recvfile.py
import sys
from jmm_sockets import *
import yesno

if len(sys.argv) != 2:
    print("Usage: ", argv[0], "<port>")

s = getServerSocket(None, int(sys.argv[1]))
conn, addr = s.accept()

buffer = None
filelen = str()

# receive filesize
while 1:
    buffer = conn.recv(1)
    if buffer == '\n':
        # give it a rest
        break
    else:
        filelen = filelen + buffer

# prompt user to accept file
filelen = int(filelen)
print("file size = ", filelen, )
userChoice = yesno.yesno("Accept?")
conn.send(userChoice)

# conditionally accecpt file
if bool(userChoice):
    filename = raw_input("What do you want to call the file? ")
    f = open(filename, 'w')

    # Receive the entire file in one call
    data = conn.recv(filelen)

    print("File: ", )
    f.write(data)
    print("written")

conn.close()