一個簡單的文件傳輸程序
文件是一種數據存儲的形式,因此文件的傳輸實質上就是數據的傳輸。在這個程序中主要步驟爲
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");
}
結果演示: