Network Programming - File Transfer

Network Programming - File Transfer

TOC

Claim

  • Client initiates a socket connection to the server to establish a data transmission channel
  • The client sends the file name to be transferred to the server, ending with '#' character, character accepted by the server, knowing that received the '#'
  • The client sends to the server file length, 4 bytes
  • The client sends file contents to the server
  • After he completed file server, sends "OK"
  • The client receives the "OK", the socket is closed

Thinking

  • and socket connection substantially as they were before the client and server, different main process data received transmission needs to be processed
  • Client transfer file name, to add a '#', which requires access to parameters for processing the file name on the command-line arguments read into the array, at the end of the array plus a '#', the transmission is an array.
//对文件名传输进行处理
        char filename[MAX_PATH];
        int i = 0;
        for (i = 0; i < len3; i++)
        {
            filename[i] = argv[3][i];
        }
        filename[i++] = '#';

        //传输文件名
        iResult = send(ConnectSocket, filename, strlen(filename), 0);
        if (iResult == SOCKET_ERROR)
        {
            cout << "Send Filename : " << Sendbuffer << " with error " << WSAGetLastError() << "\n" << endl;
            break;
        }
  • File type is not limited to text files, so when the process should be treated as a binary file of
    the C ++ file stream provides read () and write () function may be more convenient for binary data of a certain size read and writes.
        //以二进制文件形式读取文件
        ifstream sourcefile(argv[3], ios::in | ios::binary);
sourcefile.read(filebuf, MAX_LEN);
rec_file.write(Recvb, MAX_LEN);
  • File transfer requires transferring content file length is 4 bytes, 4 bytes sufficient majority of
  • Determine the file
        //确定文件的大小
        int len;        
        FILE* fp;
        if(fp = fopen(argv[3], "r"))
        {
            fseek(fp, 0, SEEK_END);
            printf("%ld\n", ftell(fp));
            len = ftell(fp);
            fclose(fp);
        }
        else
        {
            cout << "Error" << endl;
        }
  • The buffer size is limited, but also in the transfer of large files, is very likely the buffer size is less than the length of the file, so at the time of the processing loop processing, the transmission cycle to transmit, receive cycle should be accepted, the writing.
    File stream in C ++ provides a read () and write () function, continue to call these two functions to transmit data beyond the buffer size.
sourcefile.read(filebuf, MAX_LEN);
rec_file.write(Recvb, MAX_LEN);
  • If the file length of time required multiple transmissions, each will accept print information
    while (left > 0)
    {
        if (left > MAX_LEN)
        {
            iResult = recvn(s, Recvb, MAX_LEN);
            if (iResult == -1)
            {
                cout << "Receive failed with error " << WSAGetLastError() << endl;
                return -1;
            }
            else if (iResult == 0)
            {
                cout << "Receive data stopped. There "<<left<<" bytes unreceived" << endl;
                break;
            }
            else 
            {
                cout << "Receive partial data : " << iResult << " bytes" << endl;
                rec_file.write(Recvb, MAX_LEN);
                left -= MAX_LEN;
            }
        }
        else
        {
            iResult = recvn(s, Recvb, left);
            if (iResult == -1)
            {
                cout << "Receive failed with error " << WSAGetLastError() << endl;
                return -1;
            }
            else if (iResult ==0 )
            {
                cout << "Receive data stopped. There " << left << " bytes unreceived" << endl;
                break;
            }
            else
            {
                cout << "Receive data : " << iResult << " bytes" << endl;
                rec_file.write(Recvb, left);
                left = 0;
            }

        }
    }
  • When the content of the transfer file, the main file according to the length of the front after the received fixed length data call acceptance data reception function.
  • For the length of the transmission file to be noted that the reference can not be transmitted by the array
    length of an integer length of four bytes and four bytes of an array may represent may not represent the same, it is far greater than the range of integer array .
  • The length of the file transfer to be noted that when setting parameters
  • After the transfer is complete, the end of the print server file transfer tips and information back to the client.
    //接收文件长度数据
    iResult = recv(s, (char*)&len_tran, 4, 0);
    if (iResult != 4)
    {
        if (iResult == -1)
        {
            cout << "Receive failed with error " << WSAGetLastError() << endl;
            return -1;
        }
        else
        {
            cout << "Length data is not complete. Connection closed\n" << endl;
            return -1;
        }
    }

    recvlen =ntohl( len_tran);
    cout << "The length of file to receive is " << recvlen << "\n" << endl;
  • The client information after receiving OK to exit.
  • The server at the time of problems, closesocket and return
    attention, because they have received the entire file is a while loop, so other problems, such as when to accept defeat, continue to the first error processing (at this time because the client operation failed has closed the connection), the unified conduct closesocket and return.
iResult = recv(ConnectSocket, RecvBuffer, sizeof(RecvBuffer), 0);

        if (iResult == SOCKET_ERROR || iResult == 0)
        {
            cout << "Client(" << inet_ntoa(information.addrinfo.sin_addr) << " : " << information.addrinfo.sin_port << ") Exited" << endl;
            //每次如果出现任何错误,continue会回到这里,因为这个时候客户端已经关闭,就可以统一的closesocket关闭连接同时返回
            closesocket(ConnectSocket);
            return -1;

        }

result

  • The client parameters

    passed parameters: destination IP address, port number, and the name of the file to transfer.
  • File information
    transfer is a PDF file, size
  • The client and server interaction
    result:
    The client

    can see from the image above:
    client can name, transfer files and print out the length of the transmission file after file successfully sent confirmation message returned from the server. Exit after confirmed.

Server

As can be seen from the figure:
the server can get the address and port information of the client, you can print out are about to receive the file name, length of the file. While prompt when receiving the completed documents, print out the contents of the file. Finally, send a confirmation message to the client. Client exits after confirmation.

When the transfer file is too large, the server receives the data cycle and accept each time to print out the information.

And
in the file directory of the client, you can see pass over the data, and can open normal

result, the results met the requirements

Code

Client

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
#include <stdlib.h>
#include <stdint.h>
#include <fstream>
#include<io.h>
#include <stdint.h>
using namespace std;

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

using namespace std;

//缓存区长度
#define  MAX_LEN 100000

int __cdecl main(int argc, char* argv[])
{
    WSADATA ws;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct sockaddr_in Server_addr;
    int iResult = 0;
    int Addrlen = 0;
    HANDLE hThread = NULL;
    char Sendbuffer[MAX_LEN];
    char Recvbuffer[MAX_LEN];


    //检查参数个数
    if (argc != 4)
    {
        cout << "Need enter target IP, port and the file name to transmit !" << endl;
        return -1;
    }
    int PORT = atoi(argv[2]);


    //初始化 socket
    iResult = WSAStartup(MAKEWORD(2, 2), &ws);
    if (iResult != 0)
    {
        cout << "Initiate failed with error: " << GetLastError() << endl;
        WSACleanup();
        return -1;
    }

    //创建 socket
    ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ConnectSocket == INVALID_SOCKET)
    {
        cout << "Create Client socket failed with error: " << WSAGetLastError() << endl;
        WSACleanup();
        return -1;
    }


    Server_addr.sin_family = AF_INET;
    Server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    Server_addr.sin_port = htons(PORT);
    memset(Server_addr.sin_zero, 0x00, 8);

    //连接 socket
    iResult = connect(ConnectSocket, (struct sockaddr*)&Server_addr, sizeof(Server_addr));
    if (iResult != 0)
    {
        cout << "Connect with error: " << WSAGetLastError() << endl;
        closesocket(ConnectSocket);
        WSACleanup();
        return -1;
    }
    else
    {
        cout << "Connect successfully!" << endl;
    }


    char filebuf[MAX_LEN + 1];        //文件内容存储数组

    //传输文件,先传输文件名,再传输文件长度,最后传输文件内容
    while (true)
    {
        cout << "Ready to send file" << endl;
        int len3 = strlen(argv[3]);

        //对文件名传输进行处理
        char filename[MAX_PATH];
        int i = 0;
        for (i = 0; i < len3; i++)
        {
            filename[i] = argv[3][i];
        }
        filename[i++] = '#';

        //传输文件名
        iResult = send(ConnectSocket, filename, strlen(filename), 0);
        if (iResult == SOCKET_ERROR)
        {
            cout << "Send Filename : " << Sendbuffer << " with error " << WSAGetLastError() << "\n" << endl;
            break;
        }

        //确定文件的大小
        int len;        
        FILE* fp;
        if(fp = fopen(argv[3], "r"))
        {
            fseek(fp, 0, SEEK_END);
            printf("%ld\n", ftell(fp));
            len = ftell(fp);
            fclose(fp);
        }
        else
        {
            cout << "Error" << endl;
        }


        //以二进制文件形式读取文件
        ifstream sourcefile(argv[3], ios::in | ios::binary);

        int filelen = len;
        int filelen_net = htonl(filelen);
        //传输内容长度
        cout << "Sending the length of file: " << filelen << "\n" << endl;


        iResult = send(ConnectSocket, (char*)&filelen_net, 4, 0);
        if (iResult == SOCKET_ERROR)
        {
            cout << "Send file length failed with error " << WSAGetLastError() << endl;
            break;
        }


        //传输文件内容
        cout << "Ready to send file!" << endl;
        int left = filelen;

        while (left>0)
        {
            if (left > MAX_LEN)
            {
                sourcefile.read(filebuf, MAX_LEN);
                iResult = send(ConnectSocket, filebuf, MAX_LEN, 0);
                if (iResult == SOCKET_ERROR)
                {
                    cout << "Send file content failed with error " << WSAGetLastError() << "\n" << endl;
                    break;
                }
                left -= MAX_LEN;
            }
            else
            {
                sourcefile.read(filebuf, left);
                iResult = send(ConnectSocket, filebuf, left, 0);
                left = 0;
                if (iResult == SOCKET_ERROR)
                {
                    cout << "Send file content failed with error " << WSAGetLastError() << "\n" << endl;
                    break;
                }
            }
        }

        sourcefile.close();
        if (left != 0)
        {
            cout << "Send file content failed with error " << WSAGetLastError() << "\n" << endl;
            cout << "Client Exit..." << endl;
            break;
        }

        //判断来自客户端的返回信息
        iResult = recv(ConnectSocket, Recvbuffer, sizeof(Recvbuffer), 0);
        Recvbuffer[iResult] = '\0';
        if (iResult == SOCKET_ERROR)
        {
            cout << "Failed to receive confirm from server!\n" << endl;
            break;
        }
        else if (strcmp(Recvbuffer, "OK\0") == 0)
        {
            cout << "Received confirm from server: " << Recvbuffer << "\nSending file succeed!\n" << endl;
            break;
        }

    }

    closesocket(ConnectSocket);
    WSACleanup();
    system("pause");
    return 0;

}

Server

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
#include <stdlib.h>
#include <stdint.h>
#include <fstream>

using namespace std;

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

#define PORT 4000
#define IP_ADDR "0.0.0.0"
#define  MAX_LEN 100000

//定长数据传输
int recvn(SOCKET s, char *recvbuf, unsigned int fixedlen)
{
    int iResult;
    int cnt;        //记录当前待接受的数量
    cnt = fixedlen;
    while (cnt > 0)
    {
        iResult = recv(s, recvbuf, cnt, 0);
        if (iResult < 0)
        {
            cout << "Receive failed with error :" << WSAGetLastError() << endl;
            return -1;
        }

        if (iResult == 0)
        {
            cout << "Data is not as long as fixed. Connection closed" << endl;
            return fixedlen - cnt;
        }
        recvbuf += iResult;
        cnt -= iResult;
    }
    return fixedlen;
}

//变长内容传输
int recvvl(SOCKET s, char*filename)
{
    int iResult;
    long recvlen;
    int len_tran;
    char Recvb[MAX_LEN];

    //接收文件长度数据
    iResult = recv(s, (char*)&len_tran, 4, 0);
    if (iResult != 4)
    {
        if (iResult == -1)
        {
            cout << "Receive failed with error " << WSAGetLastError() << endl;
            return -1;
        }
        else
        {
            cout << "Length data is not complete. Connection closed\n" << endl;
            return -1;
        }
    }

    recvlen =ntohl( len_tran);
    cout << "The length of file to receive is " << recvlen << "\n" << endl;


    ofstream rec_file;
    rec_file.open(filename, ios::binary);
    int left = recvlen;

    while (left > 0)
    {
        if (left > MAX_LEN)
        {
            iResult = recvn(s, Recvb, MAX_LEN);
            if (iResult == -1)
            {
                cout << "Receive failed with error " << WSAGetLastError() << endl;
                return -1;
            }
            else if (iResult == 0)
            {
                cout << "Receive data stopped. There "<<left<<" bytes unreceived" << endl;
                break;
            }
            else 
            {
                cout << "Receive partial data : " << iResult << " bytes" << endl;
                rec_file.write(Recvb, MAX_LEN);
                left -= MAX_LEN;
            }
        }
        else
        {
            iResult = recvn(s, Recvb, left);
            if (iResult == -1)
            {
                cout << "Receive failed with error " << WSAGetLastError() << endl;
                return -1;
            }
            else if (iResult ==0 )
            {
                cout << "Receive data stopped. There " << left << " bytes unreceived" << endl;
                break;
            }
            else
            {
                cout << "Receive data : " << iResult << " bytes" << endl;
                rec_file.write(Recvb, left);
                left = 0;
            }

        }
    }
    if (left == 0)
    {
        cout << "Receive all the data\n" << endl;
    }


    return recvlen;
}


//自己定义的结构体,用于多线程传参
struct my_para
{
    SOCKET connect_socket;
    struct sockaddr_in addrinfo;
};


//多线程函数
DWORD WINAPI ClientThread(LPVOID lpparameter)
{
    struct my_para information = *(struct my_para *)lpparameter;
    int iResult = 0;
    char RecvBuffer[MAX_LEN];
    SOCKET ConnectSocket = information.connect_socket;

    int filelength = 0;
    char filename[MAX_PATH] = { 0 };


    while (TRUE)
    {
        //接收名字
        iResult = recv(ConnectSocket, RecvBuffer, sizeof(RecvBuffer), 0);

        if (iResult == SOCKET_ERROR || iResult == 0)
        {
            cout << "Client(" << inet_ntoa(information.addrinfo.sin_addr) << " : " << information.addrinfo.sin_port << ") Exited" << endl;
            //每次如果出现任何错误,continue会回到这里,因为这个时候客户端已经关闭,就可以统一的closesocket关闭连接同时返回
            closesocket(ConnectSocket);
            return -1;

        }


        //对接收的文件名进行处理,存储下来,在后面本地保存时使用
        int i = 0;
        for (i; RecvBuffer[i] != '#'; i++)
        {
            filename[i] = RecvBuffer[i];
        }
        filename[i] = '\0';
        cout << "The file " << filename << " is to receive!\n" << endl;


        //接收文件
        cout << "Ready to receive file!\n" << endl;

        iResult = recvvl(ConnectSocket, filename);
        if (iResult == 0 || iResult == -1)
        {
            cout << "Send file failed !\n\n\n" << endl;
            continue;
        }

        //文件传输成功时,返回OK信息
        char returnmessage[] = "OK\0";

        iResult = send(ConnectSocket, returnmessage, strlen(returnmessage), 0);
        if (iResult == SOCKET_ERROR)
        {
            cout << "Return confirm message with error " << WSAGetLastError() << endl;
            continue;
        }
        else
        {
            cout << "Return success!\n" << endl;
        }

    }
    closesocket(ConnectSocket);
    return 0;
}

int main(int argc, char * argv[])
{
    WSADATA ws;
    SOCKET ServerSocket = INVALID_SOCKET, ClientSocket = INVALID_SOCKET;
    struct sockaddr_in LocalAddr, ClientAddr;
    int iResult = 0;
    int Addrlen = 0;
    HANDLE hThread = NULL;


    //Initiate 
    iResult = WSAStartup(MAKEWORD(2, 2), &ws);
    if (iResult != 0)
    {
        cout << "Initiate failed with error: " << WSAGetLastError() << endl;
        return -1;
    }

    //create socket
    ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ServerSocket == INVALID_SOCKET)
    {
        cout << "create socket failed with error: " << WSAGetLastError() << endl;
        WSACleanup();
        return -1;
    }

    LocalAddr.sin_family = AF_INET;
    LocalAddr.sin_addr.s_addr = inet_addr(IP_ADDR);
    LocalAddr.sin_port = htons(PORT);
    memset(LocalAddr.sin_zero, 0x00, 8);

    //bind socket
    iResult = bind(ServerSocket, (struct sockaddr *)&LocalAddr, sizeof(LocalAddr));
    if (iResult != 0)
    {
        cout << "Bind socket failed: " << WSAGetLastError() << endl;
        closesocket(ServerSocket);
        WSACleanup();
        return -1;
    }

    //listen
    iResult = listen(ServerSocket, 10);
    if (iResult != 0)
    {
        cout << "Listen socket failed with error: " << WSAGetLastError() << endl;
        closesocket(ServerSocket);
        WSACleanup();
        return -1;
    }

    cout << "Server is already setup!\n" << endl;


    SOCKET acc_sock;
    while (TRUE)
    {
        Addrlen = sizeof(ClientAddr);
        acc_sock = accept(ServerSocket, (struct sockaddr*)&ClientAddr, &Addrlen);
        if (acc_sock < 0)
        {
            cout << "Accept failed with error: " << WSAGetLastError() << endl;
            break;
        }
        char *addr_temp = inet_ntoa(ClientAddr.sin_addr);

        cout << "Client has already connected! (" << addr_temp << " : " << ClientAddr.sin_port << ")\n" << endl;

        struct my_para temp;
        temp.addrinfo = ClientAddr;
        temp.connect_socket = acc_sock;

        hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)&temp, 0, NULL);
        if (hThread == NULL)
        {
            cout << "Create Thread failed!" << endl;
            break;
        }

        CloseHandle(hThread);

    }

    closesocket(ServerSocket);
    closesocket(ClientSocket);
    WSACleanup();
    system("pause");

    return 0;
}

Guess you like

Origin www.cnblogs.com/volva/p/11815075.html