使用:
(1)首先运行服务端,待服务端运行起来;
(2)最后运行客户端,输入要传输文件到哪个目标机器的IP地址;
(3)输入传输文件的路径及文件(完成的路径),其中包含文件的类型,也就是后缀需要包含(代表需要传输文件的类型)。
例如:E:/Data(D)/Cat_and_Dog/dog.jpg
参考博主:https://blog.csdn.net/sinat_23118323/article/details/71024351
客户端:
#pragma once
#ifndef _TCPSOCKET_H_
#define _TCPSOCKET_H_
#include <WinSock2.h> //windows socket的头文件
#include <Windows.h>
#include <iostream>
#include <thread>
#include <process.h>
#include<mutex>
#include<string>
#include<process.h>
#define err(errMsg) printf("[error] %s failed,code %d\
line:%d\n",errMsg, WSAGetLastError(),__LINE__)
#pragma comment(lib, "ws2_32.lib") //连接winsock2.h的静态库文件
using namespace std;
class Socket {
private:
SOCKET clientSock;
int id;
string ip;
char wb_file[MAXBYTE];
public:
//获取ClientSock
SOCKET Getcientsock();
//获取当前日期
void OBTION_TIME();
//获取开始时间
double START_TIME();
//获取结束时间
double END_TIME();
//获取文件大小
void getByteSize(unsigned long long size);
//返回以MB为单位的文件大小
unsigned long long RETURN_MB(unsigned long long size);
//判断输入IP是否存在
bool TARGE_FILE(string ip);
//输入文件
void SEND_FILE(string file);
//LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递)
static DWORD WINAPI transmmit(const LPVOID arg);
bool INPUT_IP(string ipt);
//系统的实现以上函数
int MAIN_SOCKET();
//清理和关闭网络库
void CLEAR();
};
//定义结构体用来设置
typedef struct my_file
{
SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信
sockaddr_in clientAddr; //用于保存客户端的socket地址
int id; //文件块的序号
}F;
#endif // !_TCPSOCKET_H_
#include "tcpSocket.h"
#define MAXBYTES 300*1024
//表示一秒钟会有多少个时钟计时单元
#define CLOCKS_PER_SEC ((clock_t)1000)
mutex m;
//获取当前日期
void Socket::OBTION_TIME() {
SYSTEMTIME start; //windows.h中
GetLocalTime(&start);//time.h的tm结构体一样的效果
cout << start.wYear << "/" << start.wMonth << "/" << start.wDay << " " << start.wHour << ":" << start.wMinute << ":" << start.wSecond << endl;
}
// 获取开始时间
double Socket::START_TIME() {
DWORD start_time;
start_time = GetTickCount64();
return (double)start_time;
}
//获取结束时间
double Socket::END_TIME() {
DWORD end_time;
end_time = GetTickCount64();
return double(end_time);
}
SOCKET Socket::Getcientsock() {
return clientSock;
}
//获取文件大小
void Socket::getByteSize(unsigned long long size) {
unsigned long long rest = 0;
if (size < 1024) {
cout << size << "B" << endl;
return;
}
else {
size /= 1024;
}
if (size < 1024) {
cout << size << "KB" << endl;
return;
}
else {
rest = size % 1024;
size /= 1024;
}
if (size < 1024) {
size *= 100;
cout << (size / 100) << "." << (rest * 100 / 1024 % 100) << "MB" << endl;
return;
}
else {
size = size * 100 / 1024;
cout << (size / 100) << "." << (size % 100) << "GB" << endl;
return;
}
}
void Socket::SEND_FILE(string file) {
int i = 0;
char Temporary_file[MAXBYTE] = { 0 };//保存发送文件的格式
for (i = 0; i < file.length(); i++) {
wb_file[i] = file[i];
Temporary_file[i] = file[i];
}
wb_file[i] = '\0';
Temporary_file[i] = '\0';
send(clientSock, Temporary_file, strlen(Temporary_file), 0);
}
//LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递)
DWORD WINAPI Socket::transmmit(const LPVOID arg){
//上锁是为了方便看输出
m.lock();
//F* temp = (F*)arg;
Socket* so = (Socket*)arg;
/*
获取文件的序号int file_id = temp->id;
获取客户机的端口号ntohs(temp -> clientAddr.sin_port);
*/
cout << "测试开始,等待服务端发送消息..." << endl;
//从客户端处接受数据
/*
char Buffer[MAXBYTE] = { 0 }; //缓冲区
recv(temp->clientSocket, Buffer, MAXBYTE, 0); //recv方法 从客户端通过clientScocket接收
cout << "线程" << temp->id << "从客户端的" << ntohs(temp->clientAddr.sin_port) << "号端口收到:" << Buffer << endl;
*/
char* file_name; //文件路径
char File_Alias[100] = { 0 };
file_name = so->wb_file;
unsigned long long len_file = 0;
FILE* fp = fopen(file_name, "rb");
if (fp == NULL) {
cout << "文件" << file_name << "出错或不存在" << endl;
}
else {
//获取文件大小
unsigned long long g_fileSize ;//注意这个地方不能使用unsigned long long,因为当文件传输很大的时候,ftell返回的是long
//fseek(fp, 0, SEEK_END);//将读取的文件指针放到文件末尾
//g_fileSize = ftell(fp);
//fseek(fp, 0, SEEK_SET);//指针移到文件开头
struct _stat64 st;
_stat64(file_name, &st);
g_fileSize = st.st_size;
string send_file_len;
send_file_len = to_string(g_fileSize);
send(so->clientSock, send_file_len.c_str(), send_file_len.length(), 0);
cout << "发送文件时间: ";
so->OBTION_TIME();
double start_time = so->START_TIME();
char Buffer[MAXBYTES] = { 0 }; //文件缓冲区
unsigned long long size = 0; //读取的文件长度
unsigned long long Actual_file_len = 0;//为了确保长度更精确
while ((size = fread(Buffer, sizeof(char), MAXBYTES, fp)) > 0) {
//返回非0值表示send错误
if (send(so->clientSock, Buffer, (unsigned long long)size, NULL) < 0)
{
cout << "传输出错,请检查网络配置。" << endl;
break;
}
len_file += size;
cout.width(3);//i的输出为3位宽
if ((len_file*100/ g_fileSize)%5>0) {
cout << (len_file * 100 / g_fileSize) << "%";
cout << "\b\b\b\b";//回删三个字符,使数字在原地变化
}
size = 0;
//每次读取完之后清空缓存区,以便下一块文件读入
memset(&Buffer, 0, MAXBYTES);
}
const char* t = "end";
send(so->clientSock, t, strlen(t), NULL);
cout << so->id << "线程已成功发送" << file_name << endl;
cout << "发送文件大小: ";
so->getByteSize(len_file);
cout << "文件发送结束时间: ";
so->OBTION_TIME();
double end_time = so->END_TIME();
double currentTime = 0;
currentTime = (double)(end_time-start_time)/ CLOCKS_PER_SEC;
cout << "发送文件耗时: " << currentTime <<"s"<< endl;
fclose(fp);
}
/*
发送简单的字符串到客户端
const char* s = "Server file";
send(temp->clientSocket, s, strlen(s)*sizeof(char)+1, NULL);
cout << "线程" << temp->id << "通过客户端的" << ntohs(temp->clientAddr.sin_port) << "号端口发送:" << s << endl;
*/
m.unlock();
return 0;
}
bool Socket::INPUT_IP(string ipt) {
//客户端socket
//加载winsock库
WSADATA wsadata;
//WSA-windows socket ansyc windows的异步套接字 2.2版本的
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) {
err("WSAStartup");
return 0;
}
clientSock = socket(AF_INET, SOCK_STREAM, 0);
if (clientSock == INVALID_SOCKET) {
err("SOCKET");
return 0;
}
//初始化socket信息
sockaddr_in clientAddr;
memset(&clientAddr, 0, sizeof(SOCKADDR));
//clientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
const char* ips = ipt.c_str();
clientAddr.sin_family = AF_INET;
clientAddr.sin_port = htons(3725);//将本地字节序转换为网络字节序,大端和小端存储
clientAddr.sin_addr.s_addr = inet_addr(ips);
if (connect(clientSock, (SOCKADDR*)&clientAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
err("connect");
return false;
}
return true;
}
//接收和传输文件目录
bool Socket::TARGE_FILE(string ip) {
bool flag = INPUT_IP(ip);
if (flag == true)return 1;
else {
return 0;
}
}
int Socket::MAIN_SOCKET(){
//建立连接
//while (true) {
cout << "已建立连接。" << endl;
char Buffer[MAXBYTE] = { 0 }; // 文件缓冲区
char wb_file[100] = { 0 }; //写入的文件
//“句柄” 类似指针, 但通过指针可读写对象, 通过句柄只是使用对象;
HANDLE hThread[2];
for (int i = 0; i < 1; i++) {
sockaddr_in clntAddr;
memset(&clntAddr, 0, sizeof(SOCKADDR));
//使用 API 的 CreateThread, 它执行完入口函数后会自动退出, 无需 ExitThread;
hThread[i] = CreateThread(NULL, 0,&transmmit, this, 0, NULL);
}
//等待子线程完成
WaitForMultipleObjects(1, hThread, TRUE, INFINITE);
cout << "错误代码: " << WSAGetLastError() << endl;
//}
return 0;
}
void Socket::CLEAR() {
closesocket(clientSock);
关闭网络库
if (WSACleanup() != 0) {
err("WSACleanup");
return ;
}
cout << "客户端连接已关闭。" << endl;
system("pause");
}
#include"tcpSocket.h"
int main() {
Socket* soc = new Socket();
char Tempoary[1024] = { 0 };
while (true) {
string ip;
cout << "请输入目标机器的IP:";
cin >> ip;
bool flag=soc->TARGE_FILE(ip);
if (flag == true)break;
else {
cout << "IP地址错误或者目标主机不存在" << endl;
continue;
}
}
while (true) {
cout << "其次请客户端输入传输文件路径: ";
string file;
cin >> file;
soc->SEND_FILE(file);
int ret = recv(soc->Getcientsock(), Tempoary, 10, 0);
if (ret < 0)continue;
soc->MAIN_SOCKET();
}
soc->CLEAR();
return 0;
}
服务端:
#pragma once
#ifndef _TCPSERVER_H_
#define _TCPSERVER_H_
#include <WinSock2.h> //windows socket的头文件
#include <Windows.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <process.h>
#include <fstream>
#include <string>
#include<time.h>
#define err(errMsg) printf("[error] %s failed,code %d\
line:%d\n",errMsg, WSAGetLastError(),__LINE__)
#pragma comment(lib, "ws2_32.lib") //连接winsock2.h的静态库文件
using namespace std;
class Server {
private:
SOCKET clientSock;
SOCKET servSocket;
int id;
char wb_file[MAXBYTE];//写入文件路径
char filename[MAXBYTE];
public:
//获取clientSock
SOCKET GetclientSock();
//获取当前日期
void OBTION_TIME();
//获取开始时间
double START_TIME();
//获取结束时间
double END_TIME();
//获取文件大小
void getByteSize(unsigned long long size);
//返回以MB为单位的文件大小
unsigned long long RETURN_MB(unsigned long long size);
//绑定和监听
void TARGE_FILE();
//返回文件的类型
string TYPE_file();
//LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递)
static DWORD WINAPI transmmit(const LPVOID arg);
//系统的实现以上函数
int MAIN_Server();
//接收文件
void REVER_file(string file,string fileanme);
//清理网络库和关闭
void CLEAR();
};
//定义结构体用来设置
typedef struct my_file {
SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信
sockaddr_in clientAddr; //用于保存客户端的socket地址
int id; //文件块的序号
}F;
#endif // !_TCPSERVER_H_
#include "tcpServer.h"
#define MAXBYTES 300*1024
mutex m;
//获取当前日期
void Server::OBTION_TIME() {
SYSTEMTIME start; //windows.h中
GetLocalTime(&start);//time.h的tm结构体一样的效果
cout << start.wYear << "/" << start.wMonth << "/" << start.wDay << " " << start.wHour << ":" << start.wMinute << ":" << start.wSecond << endl;
}
//获取开始时间
double Server::START_TIME() {
DWORD start_time;
start_time = GetTickCount64();
return (double)start_time;
}
//获取结束时间
double Server::END_TIME() {
DWORD end_time;
end_time = GetTickCount64();
return double(end_time);
}
//获取文件大小
void Server::getByteSize(unsigned long long size) {
unsigned long long rest = 0;
if (size < 1024) {
cout << size << "B" << endl;
return;
}
else {
size /= 1024;
}
if (size < 1024) {
cout << size << "KB" << endl;
return;
}
else {
rest = size % 1024;
size /= 1024;
}
if (size < 1024) {
size *= 100;
cout << (size / 100) << "." << (rest * 100 / 1024 % 100) << "MB" << endl;
return;
}
else {
size = size * 100 / 1024;
cout << (size / 100) << "." << (size % 100) << "GB" << endl;
return;
}
}
string Server::TYPE_file() {
string end_file = "";
char Temporary[1024] = { 0 };
char file[1024] = { 0 };
int index_last = 0;
int ret = recv(GetclientSock(), file, 100, 0);
for (int i = strlen(file) - 1; i >= 0; i--) {
if (file[i] == '\\') {
index_last = i;
break;
}
}
index_last++;
if (ret > 0) {
file[ret] = '\0';
for (int i = index_last; i < strlen(file); i++) {
end_file += file[i];
}
}
return end_file;
}
void Server::TARGE_FILE() {
//加载网络库
WSADATA wsaData;
//第一个参数是winsocket load的版本号(2.2)
if (WSAStartup(MAKEWORD(2, 3), &wsaData) != 0) {
err("WSAStartup");
return ;
}
//创建服务器端的socket(协议族, sokcet类型)
servSocket = socket(AF_INET, SOCK_STREAM, 0);//如果改成SOCK_DGRAM则使用UDP
if (servSocket == INVALID_SOCKET) {
err("SOCKET");
return ;
}
sockaddr_in servAddr; //服务器的socket地址,包含sin_addr表示IP地址,sin_port保持端口号和sin_zero填充字节
memset(&servAddr, 0, sizeof(SOCKADDR)); //初始化socket地址
servAddr.sin_family = AF_INET; //设置使用的协议族
servAddr.sin_port = htons(3725); //设置使用的端口
servAddr.sin_addr.s_addr = INADDR_ANY; //define s_addr = S_un.S_addr
//将之前创建的servSocket和端口,IP地址绑定
if (bind(servSocket, (SOCKADDR*)&servAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
err("bind");
return ;
}
listen(servSocket, 1); //监听服务器端口
sockaddr_in clntAddr;
int nSize = sizeof(clntAddr);
cout << "等待连接..." << endl;
clientSock = accept(servSocket, (SOCKADDR*)&clntAddr, &nSize);
if (clientSock == INVALID_SOCKET) {
err("accept");
}
cout << "连接成功" << endl;
}
void Server::REVER_file(string file,string filename) {
int i = 0;
int len_file = file.length();
int len_filename = filename.length();
for (i = 0; i < len_file; i++) {
wb_file[i] = file[i];
}
int j = 0;
for (i = len_file; i < (len_file + len_filename)&&j<len_filename;j++, i++) {
wb_file[i] = filename[j];
}
wb_file[i] = '\0';
}
SOCKET Server::GetclientSock() {
return clientSock;
}
int Server::MAIN_Server() {
char Buffer[MAXBYTES] = { 0 }; // 文件缓冲区
char wb_files[MAXBYTE] = { 0 };
FILE* fp = fopen(wb_file, "wb");
//如果录入文件不存在的话就创建一个新的文件
if (fp == NULL) {
fp = fopen(wb_file, "w");
}
unsigned long long len_file = 0;
if (fp == NULL) {
cout << "操作文件时出错" << endl;
system("pause");
}
else {
cout << "接收文件时间: ";
OBTION_TIME();
unsigned long long g_fileSize = 0;
char rev_buffer[MAXBYTES] = { 0 };//接收文件的长度
int rev_len = recv(clientSock, rev_buffer, MAXBYTE, 0);
if (rev_len > 0) {
rev_buffer[rev_len] = '\0';
for (int i = 0; i < strlen(rev_buffer); i++) {
g_fileSize = g_fileSize * 10 + ((unsigned long long)rev_buffer[i] - 48);
}
}
double start_time = START_TIME();
memset(&Buffer, 0, MAXBYTES);
unsigned long long size = 0;
//当成功接收文件(size > 0)时,判断写入的时候文件长度是否等于接收的长度
while ((size = recv(clientSock, Buffer, MAXBYTES, 0)) > 0) {
if (Buffer[size - 3] == 'e' && Buffer[size - 2] == 'n' && Buffer[size - 1] == 'd')
{
char buffer[MAXBYTES] = { 0 };
for (int i = 0; i < strlen(Buffer) - 3; i++) {
buffer[i] = Buffer[i];
}
len_file += size - 3;
size -= 3;
if (fwrite(buffer, sizeof(char), size, fp) < size) {
cout << "写入出错,部分文件缺失。" << endl;
break;
}
cout.width(3);//i的输出为3位宽
if ((len_file * 100 / g_fileSize) % 5 > 0) {
cout << (len_file * 100 / g_fileSize) << "%";
cout << "\b\b\b\b";//回删三个字符,使数字在原地变化
}
break;
}
else {
if (fwrite(Buffer, sizeof(char), size, fp) < size) {
cout << "写入出错,部分文件缺失。" << endl;
}
len_file += size;
}
cout.width(3);//i的输出为3位宽
if ((len_file * 100 / g_fileSize) % 5 > 0) {
cout << (len_file * 100 / g_fileSize) << "%";
cout << "\b\b\b\b";//回删三个字符,使数字在原地变化
}
//清空缓存区以便下一次接收
memset(&Buffer, 0, MAXBYTE);
}
cout << "接收完成" << endl;
cout << "接受文件大小: ";
len_file = (unsigned long long)len_file;
getByteSize(len_file);
cout << "文件结束接受时间: ";
OBTION_TIME();
double end_time = END_TIME();
double currentTime = 0;
currentTime = (double)(end_time - start_time) / CLOCKS_PER_SEC;
cout << "接收文件耗时: " << currentTime <<"s"<< endl;
fclose(fp);
}
return 0;
}
void Server::CLEAR() {
//关闭socket,释放winsock
closesocket(servSocket);
////关闭网络库
if (WSACleanup()) {
err("WSACleanup");
return ;
}
cout << "服务器连接已关闭。" << endl;
system("pause");
}
#include "tcpServer.h"
int main() {
Server* ser = new Server();
ser->TARGE_FILE();
string wb_file;
while (true) {
cout << "首先请服务端输入想要写入的文件(不用输入文件名): ";
cin >> wb_file;
string st = "ESC";
string end_file = ser->TYPE_file();
send(ser->GetclientSock(), st.c_str(),st.length(), 0);
ser->REVER_file(wb_file,end_file);
ser->MAIN_Server();
}
ser->CLEAR();
return 0;
}