2010-05-25 45 views
1

我有两个程序,recvfile.py和sendfile.cpp。他们的工作除了最终在新文件的末尾添加了一些额外的换行符外。我不知道多余的空间如何到达那里。我知道问题是发送方,因为当我使用python的sendall()函数发送文件时不会发生这种情况。通过tcp传输文件结尾的额外换行符

以下是文件:

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() 

回答

2

原因你结束了额外的换行符是因为你在套接字中发送额外的换行符,这是因为你试图发送比你应该更多的数据。

如果你检查你的输入文件finfail()状态,你会发现,它的失败在最后几个电话到fin.get(c),所以c值保持不变 - 它保持为一个换行符,这是输入文件中的最后一个字符。

这是因为CRLF translation而发生的:您使用的文件大小(size变量)是磁盘上的原始文件大小,对所有CR进行计数。但是,当以文本模式打开它并一次读取一个字节时,标准库默默将所有CRLF转换为LF,因此您不会通过套接字发送CR。因此,在此过程结束时获得的额外换行符的数量等于原始文件中换行符的数量。

解决这个问题的方法是打开该文件中二进制模式禁用CRLF翻译:

​​

此外,你不应该在一个时间来发送文件的一个字节 - 这是可怕的慢。如果你不走运,你会为每个字节发送一个完整的数据包。如果你幸运的话,你的操作系统的网络堆栈会将这些多次发送累加到更大的数据包中(不依赖于这些数据包),但即使如此,你仍然在进行大量的内核系统调用。

考虑重构你的代码,以较少的呼叫send()recv(),在这里你通过每通电话的字节数量较多,如:

// 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); 
+0

我开了二进制模式的文件,但现在我还是落得与额外的字符。我也在while循环中检查fin.fail()。 对于缓冲区,我必须在调用send之前将整个文件读入缓冲区吗? – lowerkey 2010-05-25 19:03:05

+0

感谢您的帮助。现在额外字节的数量较少,但仍与输入文件的字节数不相同。至于重构:现在我想获得正确的字节数,然后再担心它们是如何分组的。 – lowerkey 2010-05-25 19:45:56