无法发送更大的文件 我用C++做了一个客户机服务器程序。

无法发送更大的文件 我用C++做了一个客户机服务器程序。,c++,winsock,C++,Winsock,如果我试图发送大文件,我会遇到问题。例如,一个50字节的文件工作正常,而一个200字节的文件失败 服务器代码: 客户端代码 服务器上到处都是错误,但这是最相关的问题:为什么我只能发送几个字节 char *Filesize = new char[10]; int Size = 0; // note the size is set to zero int recv_size, recv_file; char Buffer[MAXCHAR]; // no idea how big MAXCHAR

如果我试图发送大文件,我会遇到问题。例如,一个50字节的文件工作正常,而一个200字节的文件失败

服务器代码: 客户端代码
服务器上到处都是错误,但这是最相关的问题:为什么我只能发送几个字节

char *Filesize = new char[10];
int Size = 0;  // note the size is set to zero
int recv_size, recv_file;
char Buffer[MAXCHAR]; 
// no idea how big MAXCHAR is, but it turns out to be irrelevant
FILE *File; 

recv_file = recv(AcceptSocket, Buffer, Size, 0); 
// Above we use that size of zero to read zero bytes from the socket
recv_size = recv(AcceptSocket, Filesize, 10, 0);
// get the size of the file. This doesn't seem too bad
while (Filesize) { // but we just used a 10 byte blob of data containing who knows what 
                   // as the exit condition from a while loop. 
                   // never use anything from an external source, especially the internet
                   // without validating and verifying first.  
    //Menerima File Size
    Size = atoi((const char*)Filesize); // atoi fails to convert silently. Use strtol instead.
    File = fopen("D:\\fileReceived.txt", "wb"); // open file
    fwrite((const char*)Buffer, 1, Size, File); 
        // write over file contents with what  we hope is filesize from a buffer into 
        // which we read zero bytes. File now full of random crap.
    fclose(File);
    printf("File received \n");
    ZeroMemory(Buffer, Size);
    //  printf("File size : %d\n",Size);
    recv_file = recv(AcceptSocket, Buffer, Size, 0);
        // read size of the **last file** (we hope) into buffer
    recv_size = recv(AcceptSocket, Filesize, 10, 0);
}
至少,在尝试读取文件之前,必须先读取文件大小

关于TCP的重要有趣事实:TCP是一个流,而不是一个数据包。不要因为你用send写了一个数字就认为这个数字是唯一等待读取的东西。为了提高效率,TCP将数据打包在一起,因此,如果先发送1234,然后发送一个1234字节长的文件,则文件大小和文件将同时到达的可能性非常大。所以10字节的recv很可能会读取1234、1234的终止null和文件的前5个字节。现在由您决定将文件长度与文件数据分开

但是,如果将长度作为32位整数发送,它将始终为4字节。简单,是吗?不,因为有些计算机和网络协议表示向后的数字。我是认真的。谷歌搜索endian

下一步:recv返回读取的字节数。您可能无法获得所请求的字节数,并且必须一直请求,直到获得while对象。如果出现问题,recv也会返回-1,因此每次recv时,请检查返回代码是否为正数,以及在对数据执行任何操作之前所需的字节数。读一个32位的文件大小,只得到24位,然后试图用这24位来做有意义的工作,这真的会毁了你的一天

还有更多!如果MAXCHARS小于文件的大小怎么办?嗯,那很容易。您可以记录MAXCHARS或文件中剩余的字节数,并将其写出,直到文件完成

因此:


<>你把你的问题标记为C++,但是代码完全是C.</P>

这里是一个C++版本的服务器代码。要完成项目,您的客户机需要首先发送一个填充的FileTransfer对象,例如

FileTransfer xfer(file.size);
auto result = send(send_socket, &xfer, sizeof(xfer), 0);

然后从文件中发送数据,理想情况下是read Call the Dutch,您的代码像一个sieveLike一样泄漏。在这里和visual studio中,我很难分辨代码中的变量和类型。服务器端的接收循环在我看来完全是假的。尝试记录每个缓冲区和变量的含义。然后,将其内容输出到相关位置,并将其与预期值进行比较。这应该给你一个线索,在哪里出了问题。也就是说,使用std::vector代替新[],不要使用globals,删除sockaddr t的所有C样式强制唯一异常,并且在输出消息的错误不够后不继续执行,而是引发异常,前提是您检查错误。首先,检查send和recv的返回值。它可能返回更少的字节。此外,服务器代码的循环似乎已中断。您能否给出一个正确循环服务器代码的示例?非常好的解释,感谢您的回答..:我真的很感激
char *Filesize = new char[10];
int Size = 0;  // note the size is set to zero
int recv_size, recv_file;
char Buffer[MAXCHAR]; 
// no idea how big MAXCHAR is, but it turns out to be irrelevant
FILE *File; 

recv_file = recv(AcceptSocket, Buffer, Size, 0); 
// Above we use that size of zero to read zero bytes from the socket
recv_size = recv(AcceptSocket, Filesize, 10, 0);
// get the size of the file. This doesn't seem too bad
while (Filesize) { // but we just used a 10 byte blob of data containing who knows what 
                   // as the exit condition from a while loop. 
                   // never use anything from an external source, especially the internet
                   // without validating and verifying first.  
    //Menerima File Size
    Size = atoi((const char*)Filesize); // atoi fails to convert silently. Use strtol instead.
    File = fopen("D:\\fileReceived.txt", "wb"); // open file
    fwrite((const char*)Buffer, 1, Size, File); 
        // write over file contents with what  we hope is filesize from a buffer into 
        // which we read zero bytes. File now full of random crap.
    fclose(File);
    printf("File received \n");
    ZeroMemory(Buffer, Size);
    //  printf("File size : %d\n",Size);
    recv_file = recv(AcceptSocket, Buffer, Size, 0);
        // read size of the **last file** (we hope) into buffer
    recv_size = recv(AcceptSocket, Filesize, 10, 0);
}
recv file size
Make sure it's really the filesize and nothing else.
open the output file
while file size is greater than zero
    recv up to MAXCHARS or file size, whichever is lower, into buffer
    if the number of bytes read is greater than zero
        write the number of bytes read from buffer into output file
        subtract the number of bytes read from file size
    else
        something bad happened to the connection. Give up.
close file
FileTransfer xfer(file.size);
auto result = send(send_socket, &xfer, sizeof(xfer), 0);
// move the code between 8x----x8x into protocol.h
// 8x---- snip ----x8
#pragma once
// protocol.h

#ifndef PROTOCOL_H
#define PROTOCOL_H 1

#include <cstdint>

struct FileTransfer
{
    enum { ProtoVersion = 1 };
    static const size_t BufferSize = 4 * 4096;
    uint32_t    m_proto;
    size_t      m_size;

    FileTransfer(size_t size_) : m_proto(ProtoVersion), m_size(size_) {}
    FileTransfer() : m_proto(0), m_size(0) {}

};

#endif // PROTOCOL_H
// 8x---- snip ----x8

// server.cpp : Defines the entry point for the console application.

#include "stdafx.h"
#define NOMINMAX
#include <WinSock2.h>
#include <Windows.h>
#include <Ws2tcpip.h>
#include <algorithm>
#include <fstream>
#include <iostream>

//#include "protocol.h"

#pragma comment(lib, "Ws2_32.lib")

void _describeConnection(sockaddr_in& service)
{
    char clientIP[128];
    inet_ntop(AF_INET, &(service.sin_addr), clientIP, sizeof(clientIP));
    auto clientPort = ntohs(service.sin_port);

    std::cout << "new connection from " << clientIP << ':' << clientPort << "\n";
}

bool _errorIndicatesInterrupted()
{
    auto err = WSAGetLastError();
    return (err == WSAEINTR || err == WSAEINPROGRESS);
}

void _receiveFile(SOCKET socket)
{
    FileTransfer xfer;
    auto recv_size = recv(socket, reinterpret_cast<char*>(&xfer), sizeof(xfer), 0);
    if (recv_size < sizeof(xfer)) {
        std::cout << "error: only " << recv_size
            << " bytes while recv()ing FileTransfer\n";
        return;
    }

    if (xfer.m_proto != FileTransfer::ProtoVersion) {
        std::cout << "error: connection protocol " << xfer.m_proto
            << " not supported\n";
        return;
    }

    if (xfer.m_size <= 0) {
        std::cout << "error: zero length transfer\n";
        return;
    }

    std::ofstream out("D:\\fileReceived.txt", std::ios::binary | std::ios::trunc);
    char recvBuffer[FileTransfer::BufferSize];
    size_t bytesLeft = xfer.m_size;
    while (bytesLeft) {
        do {
            recv_size = recv(socket, recvBuffer, std::min(bytesLeft, FileTransfer::BufferSize), 0);
        } while (recv_size < 0 && _errorIndicatesInterrupted());
        if (recv_size < 0) {
            std::cout << "error: transfer aborted\n";
            return;
        }
        out.write(recvBuffer, recv_size);
        bytesLeft -= recv_size;
    }

    std::cout << "transfered " << xfer.m_size << " bytes\n";
}

bool _server()
{
    SOCKET m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_socket == INVALID_SOCKET) {
        std::cout << "socket() failed! " << WSAGetLastError() << "\n";
        return false;
    }

    sockaddr_in service;
    service.sin_family = AF_INET;
    inet_pton(service.sin_family, "127.0.0.1", &service.sin_addr.s_addr);
    service.sin_port = htons(55555);

    int m_bind = bind(m_socket, (sockaddr*)&service, sizeof(service));
    if (m_bind == SOCKET_ERROR) {
        std::cout << "bind() failed! " << WSAGetLastError() << "\n";
        return false;
    }

    if (listen(m_socket, 1) == SOCKET_ERROR) {
        std::cout << "listen() failed! " << WSAGetLastError() << "\n";
        return false;
    }

    // This code can only accept one connection at a time.
    int addresslen = sizeof(service);
    for (;;) {
        std::cout << "waiting for client...\n";

        SOCKET acceptSocket = accept(m_socket, (sockaddr*)&service, &addresslen);
        if (acceptSocket < 0) {
            std::cout << "accept() failed: " << WSAGetLastError() << "\n";
            return false;
        }

        _describeConnection(service);

        _receiveFile(acceptSocket);

        closesocket(acceptSocket);
    }
}

int _tmain()
{
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 2);
    int wsaerr = WSAStartup(wVersionRequested, &wsaData);
    if (wsaerr != 0) {
        std::cout << "WinSock DLL not found\n";
        return 1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        std::cout << "WinSock 2.2 required\n";
        return 1;
    }

    _server();

    //  system("PAUSE");    Just use CTRL+F5.

    return 0;
}