C++ 通过tcp传输的文件末尾的额外换行符

C++ 通过tcp传输的文件末尾的额外换行符,c++,python,c,sockets,winsockets,C++,Python,C,Sockets,Winsockets,我有两个程序,recvfile.py和sendfile.cpp。除了在新文件的末尾有一堆额外的换行符外,它们还能工作。我不知道额外的空间是怎么到那里的。我知道问题出在发送方,因为当我使用python的sendall()函数发送文件时,不会发生同样的情况 以下是文件: jmm_sockets.c #include <winsock.h> #include <stdio.h> #include <stdlib.h> int getServerSocket(int

我有两个程序,recvfile.py和sendfile.cpp。除了在新文件的末尾有一堆额外的换行符外,它们还能工作。我不知道额外的空间是怎么到那里的。我知道问题出在发送方,因为当我使用python的sendall()函数发送文件时,不会发生同样的情况

以下是文件:

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;
}
#包括
#包括
#包括
int getServerSocket(int端口)
{
WSADATA WSADATA;
if(WSAStartup(MAKEWORD(2,0),&wsaData)!=0){
fprintf(stderr,“WSAStartup()失败\n”);
出口(1);
}
//为传入连接创建套接字
int servSock=socket(AF_INET、SOCK流、IPPROTO_TCP);
if(servSock==无效的_套接字){
fprintf(stderr,“Oops:socket()失败%d\n”,WSAGetLastError());
出口(1);
}
//构造本地地址结构
servAddr中的结构sockaddr_;
memset(&servAddr,0,sizeof(servAddr));
servAddr.sin_family=AF_INET;
servAddr.sin\u addr.s\u addr=INADDR\u ANY;
servAddr.sinu端口=htons(端口);
//绑定到本地地址
int servAddrLen=sizeof(servAddr);
if(绑定(servSock,(SOCKADDR*)&servAddr,servAddrLen)=套接字错误){
fprintf(stderr,“Oops:bind()失败%d\n”,WSAGetLastError());
出口(1);
}
返回servSock;
}
int getClientSocket(字符*主机,int端口)
{
WSADATA WSADATA;
if(WSAStartup(MAKEWORD(2,0),&wsaData)!=0){
fprintf(stderr,“Oops:WSAStartup()失败”);
出口(1);
}
//创建tcp套接字
int sock=socket(AF_INET、sock流、IPPROTO_TCP);
if(套接字主机;
港口;
filename=argv[1];
}else if(argc==4){
主机=argv[1];
端口=atoi(argv[2]);
filename=argv[3];
}否则{

cerr之所以会有额外的换行符,是因为您在套接字上发送了额外的换行符,这是因为您试图发送的数据比您应该发送的要多

如果您检查了输入文件
fin
fail()
状态,您会发现它在对
fin.get(c)
的最后几次调用中失败,因此
c
的值保持不变——它保持为换行字符,这是输入文件中的最后一个字符

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

解决此问题的方法是以二进制模式打开文件以禁用CRLF转换:

fin.open(filename.c_str(), ios::in | ios::binary);
此外,你不应该一次发送一个字节的文件,这是非常慢的。如果你不走运,你会为每个字节发送一个完整的数据包。如果你走运,你的操作系统的网络堆栈会将这些多次发送累积成更大的数据包(不依赖于此),但即使这样,您仍然要对内核进行大量的系统调用

考虑重构代码,以减少对
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);

我以二进制模式打开了该文件,但现在仍然有额外的字符在while循环中。对于缓冲区,我必须在调用send之前将整个文件读入缓冲区吗?感谢您到目前为止的帮助。现在额外的字节数较低,但仍然与输入文件的字节数不一样。至于重构:目前,我想先获得正确的发送字节数,然后再考虑它们是如何分组的。
fin.open(filename.c_str(), ios::in | ios::binary);
// 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);