一个简单的文件传输程序
文件是一种数据存储的形式,因此文件的传输实质上就是数据的传输。在这个程序中主要步骤为
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");
}
结果演示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CPe80uY9-1592977402618)(https://img-blog.csdn.net/20180103111243226?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTWFnaWNTY2FyaW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]