无法发送更大的文件

时间:2015-06-24 04:28:03

标签: c++ winsock

我使用C ++创建了一个客户端服务器程序。

如果我尝试发送大文件,则会出现问题。例如,50字节文件正常工作,而200字节文件失败。

服务器代码:

// server.cpp : Defines the entry point for the console application.   
#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>

int _tmain(int argc, _TCHAR* argv[])
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int wsaerr;
    wVersionRequested = MAKEWORD(2, 2);

    wsaerr = WSAStartup(wVersionRequested, &wsaData);

    if (wsaerr != 0) {
        printf("The Winsock DLL not found \n "); 
    } else {
        printf("The Winsock DLL found\n ");
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        printf("not support Winsock version 2.2 ");
    } else {
        printf("support winsock version 2.2 \n ");
    }

    SOCKET m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_socket == INVALID_SOCKET) {
        printf("Error di socket(): %ld\n", WSAGetLastError());
        WSACleanup();
    } else{
        printf("Socket() Berhasil ! \n");
    }

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

    int namelen = sizeof(service);

    int m_bind = bind(m_socket, (sockaddr*)&service, namelen);
    if (m_bind == SOCKET_ERROR){
        printf("bind() failed ! %ld\n ", WSAGetLastError());
    } else {
        printf("bind() ok ! \n");
    }

    if (listen(m_socket, 1) == SOCKET_ERROR) {
        printf("Listen() failed ! %d\n ", WSAGetLastError());
    } else {
        printf("Listen() ok ! \n"); 
    }

    SOCKET AcceptSocket;
    printf("waiting for Client...\n \n");
    int addresslen = sizeof(service);

    while (AcceptSocket = accept(m_socket, (sockaddr*)&service, &addresslen)) {
        printf("Server dan Client connected --> ");
        char *ClientIP = inet_ntoa(service.sin_addr);
        int ClientPort = ntohs(service.sin_port);

        printf("IP:  %s:%d\n ", ClientIP, ClientPort);

        char *Filesize = new char[10];
        int Size = 0;
        int recv_size, recv_file;
        char Buffer[MAXCHAR];
        FILE *File; 

        recv_file = recv(AcceptSocket, Buffer, Size, 0);
        recv_size = recv(AcceptSocket, Filesize, 10, 0);

        while (Filesize) {
            //Menerima File Size
            Size = atoi((const char*)Filesize);
            File = fopen("D:\\fileReceived.txt", "wb");
            fwrite((const char*)Buffer, 1, Size, File);
            fclose(File);
            printf("File received \n");
            ZeroMemory(Buffer, Size);
            //  printf("File size : %d\n",Size);
            recv_file = recv(AcceptSocket, Buffer, Size, 0);
            recv_size = recv(AcceptSocket, Filesize, 10, 0);
        }
    }
}

客户代码

// client.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <winsock2.h>
#include <Windows.h>
#include <stdio.h>

using namespace std;

int Size = 0;
char *Buffer;

int _tmain(int argc, _TCHAR* argv[])
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int wsaerr;
    wVersionRequested = MAKEWORD(2, 2);

    wsaerr = WSAStartup(wVersionRequested, &wsaData);

    if (wsaerr != 0) {
        printf("The Winsock DLL not found \n ");
    } else {
        printf("The Winsock DLL found \n ");
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        printf("not support Winsock version 2.2 ");
    } else {
        printf("support winsock version 2.2 \n ");
    }

    SOCKET Client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (Client_socket == INVALID_SOCKET) {
        printf("Error di socket(): %ld\n", WSAGetLastError());
        WSACleanup();
    } else{
        printf("Socket() ok ! \n");
    }

    SOCKADDR_IN  clientService;
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientService.sin_port = htons(55555);

    if (connect(Client_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) {
        printf("connect() fail ! \n");
    } else {
        printf(" connect() ok .... \n ");

        while (1){
            FILE *File;
            File = fopen("D:\\logging21.txt", "rb");
            if (!File){
                printf("", WSAGetLastError());
            }

            printf("File open ok ! \n");

            fseek(File, 0, SEEK_END);
            Size = ftell(File);
            fseek(File, 0, SEEK_SET);

            char cisi[10];
            sprintf(cisi, "%i", Size);
            //  fclose(File);
            send(Client_socket, cisi, 10, 0);   //file size sent
            //  Sleep(6000);  

            Buffer = (char*)malloc(Size + 1); 
            fread(Buffer, Size, 1, File);
            fclose(File);
            send(Client_socket, Buffer, Size, 0); // File Binary sent
            free(Buffer);
            printf("sending finished....\n");
            Sleep(6000);
        }
    }
}

2 个答案:

答案 0 :(得分:0)

服务器充满了错误,但这与问题最相关:为什么我只能发送几个字节?

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将数据打包在一起,因此如果您发送&#34; 1234&#34;然后一个1234字节长的文件,赔率非常好,文件大小和文件将同时到达。因此,10字节的recv很可能会读取1234,&#34; 1234&#34;终止空,以及文件的前五个字节。您现在可以将文件长度与文件数据分开。

但是如果将长度发送为32位整数,则始终为4个字节。容易,是吗?不可以。因为某些计算机和网络协议代表数字向后。我在这里很认真。 Google up endian

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

还有更多!如果MAXCHARS小于文件大小怎么办?嗯,那个很容易。你将recCH MAXCHARS或文件中剩余的字节数写出来,直到文件完成为止。

所以:

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

答案 1 :(得分:0)

您将问题标记为C ++,但代码几乎完全是C。

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

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

然后从文件中发送数据,理想情况下读取&lt; = FileTransfer::BufferSize字节,然后将它们推送到套接字上,直到达到您承诺发送的所有字节为止。

// 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;
}

如果这不是家庭作业项目并且您正在努力设置文件传输项目,请考虑使用此处提到的库之一:Best C/C++ Network Library