简体   繁体   English

读取整个文件并通过套接字发送

[英]Read the whole file and send it via sockets

I made a server and client that should transfer files. 我做了一个服务器和客户端,应该传输文件。 I tried to make it read the whole file and send it. 我试图使其读取整个文件并发送。 But now as I see it, I am having a problem. 但是现在,如我所见,我遇到了问题。 Server should automatically send the file when the client is connected. 连接客户端后,服务器应自动发送文件。 But the file is empty, and I don't know where the problem is You can see that I'm trying to send .txt file. 但是文件为空,我不知道问题出在哪里您可以看到我正在尝试发送.txt文件。 But I would like in the future send a big file, but not bigger than 1MB.) 但我希望将来发送一个大文件,但不能大于1MB。)

Edit: 编辑:

Picture here: http://img819.imageshack.us/img819/8259/aadi.jpg 图片在这里: http : //img819.imageshack.us/img819/8259/aadi.jpg

  • Left side: The file that I tried to send. 左侧:我尝试发送的文件。
  • Right side: The file I received 右侧:我收到的文件

在此处输入图片说明

The Problem: The file that I received has been damaged, and I can't use it. 问题:我收到的文件已损坏,无法使用。

Server: 服务器:

#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;

#pragma comment(lib, "Ws2_32.lib")
#define Port 6000

SOCKET Socket, Sub;
WSADATA Winsock;
sockaddr_in Addr;
sockaddr_in IncomingAddress;
int AddressLen = sizeof(IncomingAddress);

int main()
{
    WSAStartup(MAKEWORD(2, 2), &Winsock);    // Start Winsock

    if(LOBYTE(Winsock.wVersion) != 2 || HIBYTE(Winsock.wVersion) != 2)    // Check version
    {
        WSACleanup();
        return 0;
    }

    Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    ZeroMemory(&Addr, sizeof(Addr));
    Addr.sin_family = AF_INET;
    Addr.sin_port = htons(Port);  
    bind(Socket, (sockaddr*)&Addr, sizeof(Addr));

    if(listen(Socket, 1) == SOCKET_ERROR)
    {
        printf("listening error\n");
    }
    else
    {
        printf("listening ok\n");
    }

    if(Sub = accept(Socket, (sockaddr*)&IncomingAddress, &AddressLen))
    {
        char *ClientIP = inet_ntoa(IncomingAddress.sin_addr);
        int ClientPort = ntohs(IncomingAddress.sin_port);
        printf("Client conncted!\n");
        printf("IP: %s:%d\n", ClientIP, ClientPort);

        printf("Sending file .. \n");

        FILE *File;
        char *Buffer;
        unsigned long Size;

        File = fopen("C:\\Prog.rar", "rb");
        if(!File)
        {
            printf("Error while readaing the file\n");
            getchar();
            return 0;
        }

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

        Buffer = new char[Size];

        fread(Buffer, Size, 1, File);
        char cSize[MAX_PATH];
        sprintf(cSize, "%i", Size);

        fclose(File);
        send(Sub, cSize, MAX_PATH, 0); // File size

        //int len = Size;
        //char *data = Buffer;

        int Offset = 0;
        while(Size > Offset)
        {
            int Amount = send(Sub, Buffer + Offset, Size - Offset, 0);

            if(Amount <= 0)
            {
                cout << "Error: " << WSAGetLastError() << endl;
                break;
            }
            else
            {
                Offset += Amount;
                printf("2\n");
            }
        }


        free(Buffer);
        closesocket(Sub);
        closesocket(Socket);
        WSACleanup();
    }

    getchar();
    return 0;
}

Client: 客户:

#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;

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

SOCKET Socket;
WSADATA Winsock;
sockaddr_in Addr;
int Addrlen = sizeof(Addr);

int main()
{
    WSAStartup(MAKEWORD(2, 2), &Winsock);    // Start Winsock

    if(LOBYTE(Winsock.wVersion) != 2 || HIBYTE(Winsock.wVersion) != 2)    // Check version
    {
        WSACleanup();
        return 0;
    }

     Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    ZeroMemory(&Addr, sizeof(Addr));    // clear the struct
    Addr.sin_family = AF_INET;    // set the address family
    Addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    Addr.sin_port = htons(6000);    // set the port

    if(connect(Socket, (sockaddr*)&Addr, sizeof(Addr)) < 0)
    {
        printf("Connection failed !\n");
        getchar();
        return 0;
    }

    printf("Connection successful !\n");

    printf("Receiving file .. \n");



    int Size;
    char *Filesize = new char[1024];

    if(recv(Socket, Filesize, 1024, 0)) // File size
    {
        Size = atoi((const char*)Filesize);
        printf("File size: %d\n", Size);
    }

    char *Buffer = new char[Size];

    //int len = Size;
    //char *data = Buffer;

    int Offset = 0;
    while(Size > Offset)
    {
        int Amount = recv(Socket, Buffer + Offset, Size - Offset, 0);

        if(Amount <= 0)
        {
            cout << "Error: " << WSAGetLastError() << endl;
            break;
        }
        else
        {
            Offset += Amount;
            printf("2\n");
        }
    }

    FILE *File;
    File = fopen("Prog.rar", "wb");
    fwrite(Buffer, 1, Size, File);
    fclose(File);

    getchar();
    closesocket(Socket);
    WSACleanup();
    return 0;
}

The send API may not send all the data you requested to send. send API可能不会发送您请求发送的所有数据。 So, you have to pay attention to the return value, and retry the send from where the last send ended. 因此,您必须注意返回值,并从最后一次发送结束的地方重试发送。 As an example: 举个例子:

offset = 0;
while (offset < bufsize) {
    r = send(socket, buf+offset, bufsize-offset);
    if (r <= 0) break;
    offset += r;
}

While you are doing something similar for your file transfer, you do not make sure this is the case for your file size. 当您为文件传输执行类似操作时,您不确定文件大小是否如此。

When you send the file size, you only need to send the string that represents the size, not the entire MAX_PATH . 发送文件大小时,只需要发送表示大小的字符串,而不是整个MAX_PATH The receiver then needs to parse the first string to determine the size, but any data read in after the end of the first string needs to be considered part of the file. 然后,接收器需要解析第一个字符串以确定大小,但是在第一个字符串结尾之后读取的任何数据都应被视为文件的一部分。 However, since you are trying the send MAX_PATH , the receiver should receive the same amount. 但是,由于您正在尝试发送MAX_PATH ,因此接收方应该收到相同的金额。 Your client code receives 1024 bytes, but there is no indication this is the same size as MAX_PATH . 您的客户代码接收到1024个字节,但没有迹象表明它的大小与MAX_PATH相同。

The recv API may also return fewer bytes than requested. recv API可能还返回少于请求的字节数。 You use a loop to handle reading the file, but you may need a loop to read the entire message that contains the file size. 您可以使用循环来处理读取文件,但是可能需要循环来读取包含文件大小的整个消息。

In your client receive loop, you are incrementing the data pointer. 在客户端接收循环中,您正在递增data指针。 This makes it unusable to write out the file later. 这使得以后无法写出该文件。 You already have Buffer though, so use that to write out your file. 但是,您已经有了Buffer ,因此可以使用它来写出您的文件。

fwrite(Buffer, 1, len, File);

If you encounter an error doing socket I/O, you can retrieve the error with WSAGetLastError() , or you can issue getsockopt() on the socket with the SO_ERROR option. 如果在执行套接字I / O时遇到错误,则可以使用WSAGetLastError()检索错误,也可以使用SO_ERROR选项在套接字上发出getsockopt() These may return different values, but the error reason should be correlated. 这些可能返回不同的值,但是错误原因应该相关。

Myself faced the same problem and after googling found that send() api can send a maximum data based on low level TCP buffers which are os dependent.So inorder to send a huge file we need to perform file chunking , ie send the file in the form of chunks. 我自己也遇到了同样的问题,在谷歌搜索后发现send()api可以基于依赖于操作系统的低级TCP缓冲区发送最大的数据。因此,要发送巨大的文件,我们需要执行文件分块,即在块的形式。

`const int FILE_CHUNK_SIZE = 2000; 

 //get file size
 ifstream file("myFile.file", ios::binary);
 file.seekg(0, ios::end);
 unsigned int fileSize = file.tellg();
 file.close();

 //get the file
 char* fileBuffer = new char[fileSize];
 file.open("myFile.file", ios::binary);
 file.seekg (0, ios::beg);
 file.read (fileBuffer, fileSize);
 file.close();

 //send file in chunks
 unsigned int bytesSent = 0;
 int bytesToSend = 0;

 while(bytesSent < fileSize)
 {
     if(fileSize - bytesSent >= FILE_CHUNK_SIZE)
        bytesToSend = FILE_CHUNK_SIZE;
    else
        bytesToSend = fileSize - bytesSent;
    send(ConnectSocket, fileBuffer + bytesSent, bytesToSend, 0 );
    bytesSent += bytesToSend;
}
delete [] fileBuffer;`

At the receiving end we need to have a recv() api called till the whole file content is read. 在接收端,我们需要调用recv()API,直到读取整个文件内容。

credits to:shacktar cplusplus.com 学分到:shacktar cplusplus.com

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM