文件傳輸程序設計

一個簡單的文件傳輸程序

文件是一種數據存儲的形式,因此文件的傳輸實質上就是數據的傳輸。在這個程序中主要步驟爲
1.發送方(這裏暫定爲服務端)首先打開文件將文件數據讀入應用程序的發送緩衝區,然後調用send()函數發送給接收方(這裏暫定爲客戶端)。
2.接收方調用recv()函數接收數據,並將接收到的數據寫入文件.

程序中會用到的一些技術

1.文件位置的表示

在C/C++程序中,文件路徑通常是一個由盤符、文件夾名、子文件夾名和文件名組成的字符串,各項之間用反斜線”\”隔開。
比如 D:\C++程序\test\readme.txt 在程序代碼中的文件路徑應該表示成如下形式:
D:\C++程序\test\readme.txt
但是程序在運行時鍵盤輸入的文件路徑仍然是正常表示,即反斜線仍是其自身”\”。
2.C++中的文件操作
①:C++中的文件處理功能是由輸入文件流ifstream和輸出文件流ofstream提供的,這兩個流在頭文件fstream中定義。
②:文件操作基本步驟
Ⅰ:在程序中包含頭文件fstream
#include “fstream”
Ⅱ:定義文件流變量
ifstream inFile; //定義輸入文件流對象
ofstream outFile; //定義輸出文件流對象
Ⅲ:打開文件
inFile.open( filename, inmode);
outFile.open( filename, outmode);
inFile和outFile是第②中定義的流對象,filename是要打開的文件名,可以包含文件路徑,inmode和outmode則是打開或建立文件的方式,該參數有默認值,可缺省。
Ⅳ:讀寫文件
對二進制文件使用get()方法可以從輸入流中讀取一個字符,put()方法則用於向輸出流中寫入一個字節
char ch1,ch2=’A’; //定義兩個字符變量
inFile.get(ch1); //從輸入文件流中讀取一個字符存入變量ch1;
outFile.put(ch2); //將變量ch2種的一個字符寫入輸出文件流;
Ⅴ:關閉文件
inFile.close();
outFile.close();
3.獲取文件長度的方法
接收端判斷文件傳輸是否結束需要使用文件長度,但C/C++並不提供直接獲取文件長度的函數。有很多方法可以獲取文件長度,這裏只介紹一種常用的簡單方法。
這種方法的原理是基於這樣一個事實,就是當文件的讀寫位置指針位於文件結束時,其值就等於文件長度。具體實現方法參見如下函數getfilesize()
long getfilesize(const char * filename)
{
ifstream inFile(filename); //定義流變量並打開文件
inFile.seekg(0, ios::end); //設置文件指針到文件流的尾部
streampos ps = inFile.tellg(); //讀取文件指針的位置
inFile.close(); //關閉文件流
return ps;
}
4.文件信息結構體定義
發送文件名和文件長度所用的結構
struct fileMessage {
char fileName[256];
long int fileSize;
};
5.文件傳輸程序發送端和接收端的完整工作流程
文件傳輸流程

服務器端程序代碼:

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

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

#pragma comment(lib, "Ws2_32.lib")
#define  PORT 1024

typedef struct fileMessage
{
    char fileName[26];
    long int filesize;
}fileMessage;


int main()
{
    SOCKET sock_server, newsock;
    struct sockaddr_in server_addr, client_addr; 
    char msg[] = { "Hello clinet" };
    // 初始化 winsock2.dll[12/27/2017 MagicScaring]
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 2);        //生成版本號
    if (WSAStartup(wVersionRequested, &wsaData) != 0)
    {
        cout << "加載 winsock.dll失敗" << endl;
        return 0;
    }
    // 創建套接字 [12/27/2017 MagicScaring]
    if ((sock_server = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
    {
        cout << "創建套接字失敗! 錯誤代碼:" << WSAGetLastError() << endl;
        WSACleanup();                   //註銷WinSock動態鏈接庫
        return 0;
    }
    // 填寫需要綁定的本地地址 [12/27/2017 MagicScaring]
    int addr_len = sizeof(struct sockaddr_in);
    memset((void*)&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //server_addr.sin_addr.s_addr = inet_addr("192.168.5.38");

    if (bind(sock_server, (struct sockaddr*)&server_addr, addr_len) != 0)
    {
        cout << "綁定失敗!錯誤代碼:" << WSAGetLastError() << endl;
        closesocket(sock_server);               //關閉已連接套接字
        WSACleanup();                           //註銷WinSock動態鏈接庫
        return 0;
    }

    // 開始監聽 [12/27/2017 MagicScaring]
    if (listen(sock_server, 0) != 0)
    {
        cout << "listen調用失敗!錯誤代碼:" << WSAGetLastError() << endl;
        closesocket(sock_server);
        WSACleanup();
        return 0;
    }
    else
    {
        cout << "listening...." << endl;
    }
    // 開始發送文件 [1/2/2018 MagicScaring]
    char filename[500];
    cout << "請輸入要傳輸的文件的路徑" << endl;
    cin.getline(filename, 500);
    // 接收客戶端連接請求 [1/2/2018 MagicScaring]
    if ((newsock = accept(sock_server, (struct sockaddr *)&client_addr, &addr_len)) == INVALID_SOCKET)
    {
        cout << "accept 函數調用失敗! 錯誤代碼:" << WSAGetLastError() << endl;
        closesocket(sock_server);
        WSACleanup();
        return 0;
    }
    cout << "connect from " << inet_ntoa(client_addr.sin_addr) << endl;

    char OK[3], fileBuffer[1000];
    fileMessage fileMsg;
    int size = strlen(filename);


    while (filename[size] != '\\' && size > 0)
    {
        size--;
    }
    strcpy(fileMsg.fileName, filename + size + 1);

    ifstream inFile(filename, ios::in | ios::binary);

    if (!inFile.is_open())
    {
        cout << "Can not open " << filename << endl;
        closesocket(newsock);
        WSACleanup();
        return 0;
    }
    // 獲取文件長度 [1/2/2018 MagicScaring]
    inFile.seekg(0, ios::end);
    size = inFile.tellg();
    inFile.seekg(0, ios::beg);

    fileMsg.filesize = htonl(size);
    send(newsock, (char*)&fileMsg, sizeof(fileMsg), 0);

    // 接收客戶端發送來的OK信息 [1/2/2018 MagicScaring]
    if (recv(newsock, OK, sizeof(OK), 0) <= 0)
    {
        cout << "接收OK信息失敗,程序退出" << endl;
        closesocket(sock_server);
        closesocket(newsock);
        WSACleanup();
        return 0;
    }
    // 發送文件內容 [1/2/2018 MagicScaring]
    if (strcmp(OK, "OK") == 0)
    {
        while (!inFile.eof())
        {
            inFile.read(fileBuffer, sizeof(fileBuffer));
            size = inFile.gcount();     //獲取實際讀取的字節數
            send(newsock, fileBuffer, size, 0);
        }
        cout << "file transfer finished" << endl;
        inFile.close();
    }
    else
    {
        cout << "對方無法接收文件" << endl;
    }

    closesocket(sock_server);
    closesocket(newsock);
    WSACleanup();
    return system("pause");
}

客戶端程序代碼:

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

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

#pragma comment(lib, "Ws2_32.lib")
#define  PORT 1024

typedef struct fileMessage
{
    char fileName[26];
    long int filesize;
}fileMessage;
#define  PORT 1024


int main()
{
    SOCKET sock_client;
    struct sockaddr_in server_addr, client_addr;
    int addr_len = sizeof(struct sockaddr_in);
    int name_len;
    char msgbuffer[1000];
    memset(msgbuffer, 0, sizeof(msgbuffer));
    // 初始化 winsock2.dll[12/27/2017 MagicScaring]
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 2);        //生成版本號
    if (WSAStartup(wVersionRequested, &wsaData) != 0)
    {
        cout << "加載 winsock.dll失敗" << endl;
        return 0;
    }
    // 創建套接字 [12/27/2017 MagicScaring]
    if ((sock_client = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
    {
        cout << "創建套接字失敗! 錯誤代碼:" << WSAGetLastError() << endl;
        WSACleanup();                   //註銷WinSock動態鏈接庫
        return 0;
    }
    // 填寫服務器地址 [12/27/2017 MagicScaring]
    char IP[20] = { "192.168.5.38" };
    /*char IP[20];
    cout << "輸入服務器地址:" << endl;
    cin >> IP;*/
    memset((void*)&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr(IP);

    // 與服務器建立連接 [12/27/2017 MagicScaring]
    if (connect(sock_client, (struct sockaddr*)&server_addr, addr_len) == SOCKET_ERROR)
    {
        cout << "連接失敗! 錯誤代碼:" << WSAGetLastError() << endl;
        closesocket(sock_client);
        WSACleanup();
        return 0;
    }
    fileMessage fileMsg;
    long int filelen = 0;
    char fileName[500] = "C:\\Users\\MagicScaring\\Desktop\\";
    char ok[3] = "OK";
    char fileBuffer[1000];
    _mkdir(fileName);

    if ((filelen = recv(sock_client, (char*)&fileMsg, sizeof(fileMsg), 0)) <= 0)
    {
        cout << "未接受到文件名字及文件長度" << endl;
        closesocket(sock_client);
        WSACleanup();
        return 0;
    }
    filelen = ntohl(fileMsg.filesize);
    strcat(fileName, fileMsg.fileName);

    // 創建文件準備接收文件內容 [1/2/2018 MagicScaring]

    ofstream outFile(fileName, ios::out | ios::binary);
    if (!outFile.is_open())
    {
        cout << "Cannot open " << fileName << endl;
        closesocket(sock_client);
        WSACleanup();
        return 0;
    }
    send(sock_client, ok, sizeof(ok), 0);

    // 接收文件數據並寫入文件 [1/2/2018 MagicScaring]
    int size = 0;
    do 
    {
        size = recv(sock_client, fileBuffer, sizeof(fileBuffer), 0);
        outFile.write(fileBuffer, size);
        filelen -= size;
    } while (size != 0 && filelen > 0);

    cout << "Transfer finished" << endl;
    outFile.close();
    closesocket(sock_client);
    WSACleanup();

    return system("pause");
}

結果演示:

服務端
客戶端

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章