дополнительные символы новой строки в конце файла переносятся по протоколу TCP - PullRequest
1 голос
/ 25 мая 2010

У меня есть две программы, recvfile.py и sendfile.cpp. Они работают за исключением того, что в конце нового файла я получаю кучу дополнительных символов новой строки. Я не знаю, как туда добраться. Я знаю, что проблема связана с отправителем, потому что этого не происходит, когда я использую функцию sendall () python для отправки файла.

Вот файлы:

jmm_sockets.c

#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>

int getServerSocket(int port)
{
  WSADATA wsaData;
  if(WSAStartup(MAKEWORD(2,0), &wsaData) != 0){
    fprintf(stderr, "WSAStartup() failed\n");
    exit(1);
  }

  // create socket for incoming connections
  int servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(servSock == INVALID_SOCKET){
    fprintf(stderr, "Oops: socket() failed %d\n", WSAGetLastError());
    exit(1);
  }

  // construct local address structure
  struct sockaddr_in servAddr;
  memset(&servAddr, 0, sizeof(servAddr));
  servAddr.sin_family = AF_INET;
  servAddr.sin_addr.s_addr = INADDR_ANY;
  servAddr.sin_port = htons(port);

  // bind to the local address
  int servAddrLen = sizeof(servAddr);
  if(bind(servSock, (SOCKADDR*)&servAddr, servAddrLen) == SOCKET_ERROR){
    fprintf(stderr, "Oops: bind() failed %d\n", WSAGetLastError());
    exit(1);
  }

  return servSock;
}

int getClientSocket(char* host, int port)
{
  WSADATA wsaData;
  if(WSAStartup(MAKEWORD(2,0), &wsaData) != 0){
    fprintf(stderr, "Oops: WSAStartup() failed");
    exit(1);
  }

  // create tcp socket
  int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(socket<0){
    fprintf(stderr, "Oops: socket() failed %d\n", WSAGetLastError());
    exit(1);
  }

  // set up serverAddr structure
  struct sockaddr_in servAddr;
  memset(&servAddr, 0, sizeof(servAddr));
  servAddr.sin_family = AF_INET;
  servAddr.sin_addr.s_addr = inet_addr(host);
  servAddr.sin_port = htons(port);

  // connecet to server address
  if(connect(sock, (SOCKADDR*)&servAddr, sizeof(servAddr)) < 0){
    fprintf(stderr, "Oops: connect() failed. %d\n", WSAGetLastError());
    exit(1);
  }

  return sock;
}

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());
  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.get(c);
      if(send(sock, &c, 1, 0)!=1){
    cerr << "Error: send() failed " << WSAGetLastError() << endl;
    exit(1);
      }
      readSoFar++;
    }

  }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')

    buffer = None
    data = str()
    recvdBytes = 0
    while recvdBytes < filelen:
        buffer = conn.recv(1)
        recvdBytes = recvdBytes + 1
        data = data + buffer

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

1 Ответ

2 голосов
/ 25 мая 2010

Причина, по которой вы получаете дополнительные новые строки, заключается в том, что вы отправляете дополнительные новые строки через сокет, потому что вы пытаетесь отправить больше данных, чем нужно.

Если вы проверили состояние fail() вашего входного файла fin, вы обнаружите, что он не работает при последних нескольких вызовах fin.get(c), поэтому значение c остается неизменным - оно остается как символ новой строки, который является последним символом во входном файле.

Это происходит из-за перевода CRLF : размер файла, который вы используете (переменная size) - это необработанный размер файла на диске, считая все CR. Но когда вы открываете его в текстовом режиме и читаете его по одному байту за раз, стандартная библиотека автоматически переводит все CRLF в LF, поэтому вы не отправляете CR через сокет. Следовательно, число дополнительных новых строк, которые вы получаете в конце этого процесса, равно числу новых строк, которые были в исходном файле.

Способ исправить это - открыть файл в режиме binary , чтобы отключить перевод CRLF:

fin.open(filename.c_str(), ios::in | ios::binary);

Кроме того, вы не должны отправлять файл по одному байту за раз - это ужасно медленно. Если вам не повезло, вы будете отправлять целый пакет для каждого байта. Если вам повезет, сетевой стек вашей ОС будет накапливать эти множественные посылки в большие пакеты (не зависимо от этого), но даже тогда вы все равно делаете огромное количество системных вызовов в ядре.

Рассмотрите возможность рефакторинга вашего кода, чтобы сделать меньше вызовов на send() и recv(), когда вы передаете большее количество байтов на вызов, например ::1010 *

// Just use one call to send instead of looping over bytes and sending one
// byte at a time.  Simpler and faster!
send(sock, buffer, strlen(buffer), 0);
...