使用:
(1)首先运行服务端,待服务端运行起来;
(2)最后运行客户端,输入要传输文件到哪个目标机器的IP地址;
(3)输入传输文件的路径及文件(完成的路径),其中包含文件的类型,也就是后缀需要包含(代表需要传输文件的类型)。
例如:E:/Data(D)/Cat_and_Dog/dog.jpg
参考博主:https://blog.csdn.net/sinat_23118323/article/details/71024351

1.客户端:

#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<atlimage.h>
#include<process.h>
#include<algorithm>
#define MAX 1024*10
#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];unsigned long long g_fileSize;
public://获取ClientSockSOCKET 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();//获取图片的高度int getHeight(string file);//获取图片的宽度int getWidth(string file);//清理和关闭网络库void CLEAR();//有裸数据的情况下void send_image(int width,int height,int depth,char *data, int length);//接收图片static DWORD WINAPI run(const LPVOID arg);//接收图片的线程void Thread();//一次性发送的文件bool send_once(char*Buffer,int length);//接收文件int Socket_Recv();string TYPE_file();void REVER_file(string file1, string file2);//测试传输打包文件void MAIN_SOCKET_File();//测试传输打包文件线程static DWORD WINAPI RUN(const LPVOID arg);//发送打包文件头void Send_FileHeader(char files[MAXBYTE], unsigned long long length);//发送打包文件尾void Send_FileEnd(char files[MAXBYTE], unsigned long long length);
};
//定义结构体用来设置
typedef struct my_file
{SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信sockaddr_in clientAddr; //用于保存客户端的socket地址int id; //文件块的序号
}F;
struct Potocol {unsigned long long length;
};
struct LogicHeader {int type;int logic_raw_size;int log_raw_offset;
};
struct image {int width;int height;int depth;char data[0];
};
struct imageCli {Potocol pl;LogicHeader lh;image images;
};
struct FileHeader {char name[64];//文件名称int dataType;//文件包的类别,0-表示打开文件 1-表示数 2-表示文件传输结束unsigned long long length;char raw[0];
};
struct FileHeaderSr {Potocol p;LogicHeader lh;FileHeader file;
};
#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);struct _stat64 st;_stat64(wb_file, &st);g_fileSize = st.st_size;
}
//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,因为当文件传输很大的时候,ftell返回的是longfseek(fp, 0, SEEK_END);//将读取的文件指针放到文件末尾g_fileSize = ftell(fp);fseek(fp, 0, SEEK_SET);//指针移到文件开头*/string send_file_len;send_file_len = to_string(so->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; //读取的文件长度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 / so->g_fileSize) % 5 > 0) {cout << (len_file * 100 / so->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;
}
//发送打包文件头
void Socket::Send_FileHeader(char files[MAXBYTE],unsigned long long length) {char* buf = new char[sizeof(FileHeaderSr) + length + 10];FileHeaderSr& file = *(FileHeaderSr*)buf;file.file.dataType = 0;file.file.length = length;for (int i = 0; i < strlen(files); i++) {file.file.name[i] = files[i];}file.file.name[strlen(files)] = '\0';file.lh.type = 1;file.lh.logic_raw_size = length;file.lh.log_raw_offset = sizeof(file.lh) + sizeof(file.file);file.p.length = file.lh.logic_raw_size + file.lh.log_raw_offset;//memcpy(buf + file.lh.log_raw_offset + 4, files, length);send(clientSock, buf, file.p.length + sizeof(file.p) + 5, 0);delete[]buf;
}
//发送打包文件尾
void Socket::Send_FileEnd(char files[MAXBYTE], unsigned long long length) {char* buf = new char[sizeof(FileHeaderSr) + length + 10];FileHeaderSr& file = *(FileHeaderSr*)buf;file.file.dataType = 2;file.file.length = length;for (int i = 0; i < strlen(files); i++) {file.file.name[i] = files[i];}file.file.name[strlen(files)] = '\0';file.lh.type = 1;file.lh.logic_raw_size = length;file.lh.log_raw_offset = sizeof(file.lh) + sizeof(file.file);file.p.length = file.lh.logic_raw_size + file.lh.log_raw_offset;//memcpy(buf + file.lh.log_raw_offset + 4, files, length);send(clientSock, buf, file.p.length + sizeof(file.p) + 5, 0);delete[]buf;
}
//测试传输打包文件
//LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递)
DWORD WINAPI Socket::RUN(const LPVOID arg) {m.lock();Socket* so = (Socket*)arg;cout << "测试开始,等待服务端发送消息..." << endl;char* file_name; //文件路径char File_Alias[100] = { 0 };file_name = so->wb_file;cout <<"wb_file = "<< so->wb_file << endl;unsigned long long len_file = 0;//发送文件开始so->Send_FileHeader(so->wb_file, so->g_fileSize);FILE* fp = fopen(file_name, "rb");if (fp == NULL) {cout << "文件" << file_name << "出错或不存在" << endl;}else {string send_file_len;send_file_len = to_string(so->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; //读取的文件长度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 / so->g_fileSize) % 5 > 0) {cout << (len_file * 100 / so->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;//表示发送文件结束so->Send_FileEnd(so->wb_file, so->g_fileSize);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);}m.unlock();return 0;
}
//测试传输打包文件
void Socket::MAIN_SOCKET_File() {//建立连接//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, &RUN, this, 0, NULL);}//等待子线程完成WaitForMultipleObjects(1, hThread, TRUE, INFINITE);cout << "错误代码: " << WSAGetLastError() << endl;
}
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;
}
//获取图片的高度
int Socket::getHeight(string file) {char buf[MAXBYTE] = { 0 };for (int i = 0; i < file.length(); i++) {buf[i] = file[i];}buf[file.length()] = '\0';LPCTSTR srcFilepath = _T(buf);CImage srcImage;srcImage.Load(srcFilepath);try {int height = srcImage.GetHeight();return height;}catch (exception& e) {cout << e.what() << endl;}
}
//获取图片的宽度
int Socket::getWidth(string file) {char buf[MAXBYTE] = { 0 };for (int i = 0; i < file.length(); i++) {buf[i] = file[i];}buf[file.length()] = '\0';LPCTSTR srcFilepath = _T(buf);CImage srcImage;srcImage.Load(srcFilepath);try {int width = srcImage.GetWidth();return width;}catch (exception& e) {cout << e.what() << endl;}}
//有裸数据的情况下
void Socket::send_image(int width, int height, int depth, char* data, int length) {char* buf = new char[sizeof(imageCli) + length+10];imageCli& image = *(imageCli*)buf;image.images.width = width;image.images.height = height;image.images.depth = depth;/** 测试读取的文件是否正确FILE* fp = fopen("D:\\5.jpg", "wb");if (fp == NULL) {fp = fopen("D:\\5.jpg", "w");}fwrite(data, sizeof(char), length, fp);fclose(fp);*/image.lh.type = 1;image.lh.logic_raw_size = length;image.lh.log_raw_offset = sizeof(image.lh) + sizeof(image.images);image.pl.length = image.lh.logic_raw_size + image.lh.log_raw_offset;memcpy(buf + image.lh.log_raw_offset+4, data, length);send(clientSock, buf, image.pl.length + sizeof(image.pl)+5, 0);delete []buf;}
//一次性发送的文件
bool Socket::send_once(char* Buffer, int length) {int byteReceivedOnce = 0;int bytesReceivedAll = 0;while (bytesReceivedAll < length) {if ((byteReceivedOnce = send(clientSock, Buffer + bytesReceivedAll, length - bytesReceivedAll, NULL)) < 0) {return false;}bytesReceivedAll += byteReceivedOnce;}return true;
}
//接收和传输文件目录
bool Socket::TARGE_FILE(string ip) {bool flag = INPUT_IP(ip);if (flag == true)return 1;else {return 0;}
}
DWORD WINAPI Socket::run(const LPVOID arg) {Socket* soc = (Socket*)arg;char* img=NULL;while (true) {int ret = recv(soc->clientSock, img, strlen(img), NULL);}
}
void Socket::Thread() {CreateThread(NULL, 0, &run, this, 0, NULL);
}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;
}
int Socket::Socket_Recv() {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_fileSizes = 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_fileSizes = g_fileSizes * 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_fileSizes) % 5 > 0) {cout << (len_file * 100 / g_fileSizes) << "%";cout << "\b\b\b\b";//回删三个字符,使数字在原地变化}break;}else {if (fwrite(Buffer, sizeof(char), size, fp) < size) {cout << "写入出错,部分文件缺失。" << endl;break;}len_file += size;}cout.width(3);//i的输出为3位宽if ((len_file * 100 / g_fileSizes) % 5 > 0) {cout << (len_file * 100 / g_fileSizes) << "%";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;
}
string Socket::TYPE_file() {string end_file = "";char Temporary[1024] = { 0 };char file[1024] = { 0 };int index_last = 0;int ret = recv(clientSock, file, 100, 0);for (int i = strlen(file) - 1; i >= 0; i--) {if (file[i] == '\\') {index_last = i;break;}}index_last++;end_file += "\\";if (ret > 0) {file[ret] = '\0';for (int i = index_last; i < strlen(file); i++) {end_file += file[i];}}return end_file;
}
void Socket::REVER_file(string file, string filename) {int i = 0;memset(wb_file, 0, sizeof(wb_file));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';
}
void Socket::CLEAR() {closesocket(clientSock);关闭网络库 if (WSACleanup() != 0) {err("WSACleanup");return;}cout << "客户端连接已关闭。" << endl;system("pause");}
#include"tcpSocket.h"
#define MAXBYTES 1024
int main() {Socket* soc = new Socket();while (true) {string ip;cout << "请输入目标机器的IP:";cin >> ip;bool flag = soc->TARGE_FILE(ip);if (flag == true)break;else {cout << "IP地址错误或者目标主机不存在" << endl;continue;}}string wb_file;char Tempoary[1024] = { 0 };char Buffer[MAXBYTES] = { 0 };cout << "首先请客户端输入传输文件路径: ";string file;cin >> file;soc->SEND_FILE(file);int ret = recv(soc->Getcientsock(), Tempoary, 10, 0);if (ret < 0) {soc->CLEAR();return 0;}soc->MAIN_SOCKET_File();/** 图像传输测试* string filename;char buf[MAXBYTE] = { 0 };int g_fileSize = 0;cout << "请输入文件路径: ";cin >> filename;for (int i = 0; i < filename.length(); i++) {buf[i] = filename[i];}buf[filename.length()] = '\0';struct _stat64 st;_stat64(buf, &st);g_fileSize = st.st_size;char* Buffer = new char[g_fileSize + 1];int width = soc->getWidth(buf);int height = soc->getHeight(buf);FILE* fp = fopen(buf, "rb+");if (fp == NULL) {cout << "文件打开错误" << endl;return 0;}int size = 0;if ((size=fread(Buffer, sizeof(char), g_fileSize, fp) )> 0) {soc->send_image(width, height, 2, Buffer, g_fileSize);}else {cout << "传输出现问题" << endl;}delete[]Buffer;*//*link nod = soc->initstruct();memset(Buffer, 0, sizeof(Buffer));memcpy(Buffer, &nod, sizeof(link));soc->send_once(Buffer, sizeof(link) + 1);*//*while (true) {string wb_file;char Tempoary[1024] = { 0 };char Buffer[MAXBYTES] = { 0 };cout << "首先请客户端输入传输文件路径: ";string file;cin >> file;soc->SEND_FILE(file);int ret = recv(soc->Getcientsock(), Tempoary, 10, 0);if (ret < 0)continue;soc->MAIN_SOCKET();cout << "其次请客户端输入想要写入的文件(不用输入文件名): ";cin >> wb_file;string st = "ESC";string end_file = soc->TYPE_file();send(soc->Getcientsock(), st.c_str(), st.length(), 0);soc->REVER_file(wb_file, end_file);soc->Socket_Recv();}*/soc->CLEAR();delete  soc;return 0;
}

2.服务端:

#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 MAX 10*1024
#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  wb_files[MAXBYTE];//发送文件路径char filename[MAXBYTE];unsigned long long g_fileSize;public://获取clientSockSOCKET GetclientSock();//获取servSocketSOCKET GetservSocket();//获取当前日期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();//发送图片static DWORD WINAPI run(const LPVOID arg);//发送图片的线程void Thread();//一次性的接收发送的文件等bool recv_once(char *buffer, unsigned long long length);//处理接收到的信息void solve_file();//根据分类来处理信息,鼠标,键盘,文件,图片等void do_handle(char *Buffer, unsigned long long length);//发送文件int Server_Send();//发送文件1void SEND_FILE(string file);//测试接收打包文件void MAIN_Server_File();int do_handle_file(char* Buffer, unsigned long long length);
};
//定义结构体用来设置
typedef struct my_file {SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信sockaddr_in clientAddr; //用于保存客户端的socket地址int id; //文件块的序号
}F;
struct Potocol {unsigned long long length;//代表整个数据包的长度
};
struct LogicHeader {int type;//代表业务类型int logic_raw_size;//裸数据的长度int log_raw_offset;//裸数据的偏移
};
struct image {//图像业务int width;int height;int depth;char data[0];
};
struct imageSer {LogicHeader lh;image images;
};
struct FileHeader {char name[64];//文件名称int dataType;//文件包的类别,0-表示打开文件 1-表示数 2-表示文件传输结束unsigned long long length;char raw[0];
};
struct FileHeaderCli {LogicHeader lh;FileHeader file;
};
#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++;end_file += "\\";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则使用UDPif (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;}HANDLE hThread[2];hThread[0]=CreateThread(NULL, 0, &run, this, 0, NULL);WaitForMultipleObjects(1, hThread, TRUE, INFINITE);/*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;*/
}
DWORD WINAPI Server::run(const LPVOID arg) {Server* ser = (Server*)arg;listen(ser->servSocket, 1); //监听服务器端口sockaddr_in clntAddr;int nSize = sizeof(clntAddr);cout << "等待连接..." << endl;ser->clientSock = accept(ser->servSocket, (SOCKADDR*)&clntAddr, &nSize);if (ser->clientSock == INVALID_SOCKET) {err("accept");}cout << "连接成功" << endl;return 0;
}
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;
}
SOCKET Server::GetservSocket() {return servSocket;
}
/**/
bool Server::recv_once(char *buffer,unsigned long long length) {unsigned long long byteReceivedOnce = 0;unsigned long long bytesReceivedAll = 0;while (bytesReceivedAll <length) {if ((byteReceivedOnce = recv(clientSock, buffer + bytesReceivedAll, length - bytesReceivedAll, NULL)) < 0) {return false;}bytesReceivedAll += byteReceivedOnce;}return true;
}
void Server::do_handle(char *Buffer, unsigned long long length) {LogicHeader& lh = *(LogicHeader*)Buffer;//测试传输图片是否完整/*FILE* fp = fopen("D:\\3.jpg", "wb");if (fp == NULL) {fp = fopen("D:\\3.jpg", "w");}*/switch (lh.type) {case 1:imageSer & image = *(imageSer*)Buffer;char* raw_data = Buffer + image.lh.log_raw_offset;int Logic_lengths = image.lh.logic_raw_size;raw_data[Logic_lengths] = '\0';/*fwrite(raw_data, sizeof(char), Logic_lengths, fp);fclose(fp);*/cout << "发送数据包大小 = " << length << endl;cout << "数据类型       = " << lh.type << endl;cout << "裸数据长度     = " << Logic_lengths << endl;cout << "裸数据的偏移   = " << lh.log_raw_offset << endl;cout << "图像宽度       = " << image.images.width << endl;cout << "图像高度       = " << image.images.height << endl;cout << "图片深度       = " << image.images.depth << endl;break;}
}
void Server::solve_file() {unsigned long long length = 0;unsigned long long ret = recv(clientSock, (char*)&length, sizeof(length),NULL);cout << "ret = " <<ret<< endl;cout << "length = " << length << endl;if (ret == sizeof(length)) {try {char* buf = new char[length+100];memset(buf, 0, sizeof(buf));if (recv_once(buf, length)) {do_handle(buf, length);}delete []buf;}catch (exception& e) {cout << e.what() << endl;}}}
void Server::Thread() {CreateThread(NULL, 0, &run, this, 0, NULL);
}
void Server::SEND_FILE(string file) {int i = 0;char Temporary_file[MAXBYTE] = { 0 };//保存发送文件的格式memset(wb_file, 0, sizeof(wb_file));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);struct _stat64 st;_stat64(wb_file, &st);g_fileSize = st.st_size;
}
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_fileSizes = 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_fileSizes = g_fileSizes * 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_fileSizes) % 5 > 0) {cout << (len_file * 100 / g_fileSizes) << "%";cout << "\b\b\b\b";//回删三个字符,使数字在原地变化}break;}else {if (fwrite(Buffer, sizeof(char), size, fp) < size) {cout << "写入出错,部分文件缺失。" << endl;break;}len_file += size;}cout.width(3);//i的输出为3位宽if ((len_file * 100 / g_fileSizes) % 5 > 0) {cout << (len_file * 100 / g_fileSizes) << "%";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;}
int Server::do_handle_file(char* Buffer, unsigned long long length) {//LogicHeader& lh = *(LogicHeader*)Buffer;FileHeaderCli & file = *(FileHeaderCli*)Buffer;//char* raw_data = Buffer + file.lh.log_raw_offset;int Logic_lengths_offest = file.lh.log_raw_offset;//raw_data[Logic_lengths] = '\0';/*fwrite(raw_data, sizeof(char), Logic_lengths, fp);fclose(fp);*/cout << "功能类型 = " << file.lh.type << endl;cout << "文件名 = " << file.file.name << endl;cout << "发送数据包大小 = " << length << endl;cout << "数据类型       = " << file.file.dataType << endl;cout << "裸数据偏移     = " << Logic_lengths_offest << endl;cout << "裸数据长度   = " << file.lh.logic_raw_size << endl;return file.file.dataType;
}
//测试接收打包文件
void Server::MAIN_Server_File() {char Buffer[MAXBYTES] = { 0 }; // 文件缓冲区char wb_files[MAXBYTE] = { 0 };FILE* fp = fopen(wb_file, "wb");//接收打包发送来的文件unsigned long long length = 0;unsigned long long ret = recv(clientSock, (char*)&length, sizeof(length), NULL);cout << "ret = " << ret << endl;cout << "length = " << length << endl;int flag =0;if (ret == sizeof(length)) {try {char* buf = new char[length + 100];memset(buf, 0, sizeof(buf));if (recv_once(buf, length)) {flag=do_handle_file(buf, length);}delete[]buf;}catch (exception& e) {cout << e.what() << endl;}}if (flag == 0) {//如果录入文件不存在的话就创建一个新的文件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_fileSizes = 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_fileSizes = g_fileSizes * 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_fileSizes) % 5 > 0) {cout << (len_file * 100 / g_fileSizes) << "%";cout << "\b\b\b\b";//回删三个字符,使数字在原地变化}break;}else {if (fwrite(Buffer, sizeof(char), size, fp) < size) {cout << "写入出错,部分文件缺失。" << endl;break;}len_file += size;}cout.width(3);//i的输出为3位宽if ((len_file * 100 / g_fileSizes) % 5 > 0) {cout << (len_file * 100 / g_fileSizes) << "%";cout << "\b\b\b\b";//回删三个字符,使数字在原地变化}//清空缓存区以便下一次接收memset(&Buffer, 0, MAXBYTE);}//接收打包发送来的文件unsigned long long length = 0;unsigned long long ret = recv(clientSock, (char*)&length, sizeof(length), NULL);cout << "ret = " << ret << endl;cout << "length = " << length << endl;int flags = 0;if (ret == sizeof(length)) {try {char* buf = new char[length + 100];memset(buf, 0, sizeof(buf));if (recv_once(buf, length)) {flags = do_handle_file(buf, length);}delete[]buf;}catch (exception& e) {cout << e.what() << endl;}}if (flags == 2) {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);}}}}
DWORD WINAPI Server::transmmit(const LPVOID arg) {//上锁是为了方便看输出m.lock();//F* temp = (F*)arg;Server* so = (Server*)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,因为当文件传输很大的时候,ftell返回的是longfseek(fp, 0, SEEK_END);//将读取的文件指针放到文件末尾g_fileSize = ftell(fp);fseek(fp, 0, SEEK_SET);//指针移到文件开头*/string send_file_len;send_file_len = to_string(so->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; //读取的文件长度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 / so->g_fileSize) % 5 > 0) {cout << (len_file * 100 / so->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;
}
int Server::Server_Send() {//建立连接//while (true) {cout << "已建立连接。" << endl;char Buffer[MAXBYTES] = { 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 Server::CLEAR() {//关闭socket,释放winsockif (this != nullptr) {closesocket(clientSock);closesocket(servSocket);}关闭网络库 if (WSACleanup()!=0) {err("WSACleanup");return;}cout << "服务器连接已关闭。" << endl;system("pause");
}
#include "tcpServer.h"
#define MAXBYTES 1024
int main() {Server* ser = new Server();ser->TARGE_FILE();char buffer[MAXBYTES] = { 0 };string wb_file;char Tempoary[MAXBYTES] = { 0 };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_File();/** 图像传输测试ser->solve_file();*//*while (true) {char buffer[MAXBYTES] = { 0 };string wb_file;char Tempoary[MAXBYTES] = { 0 };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();cout << "首先请服务端输入传输文件路径: ";string file;cin >> file;ser->SEND_FILE(file);int ret = recv(ser->GetclientSock(), Tempoary, 10, 0);if (ret < 0)continue;ser->Server_Send();}*/ser->CLEAR();delete ser;return 0;
}

使用C++实现Socket编程传输协议文件(包括大文件)相关推荐

  1. Socket编程、协议理解

    Socket编程.协议理解 简单说明 Socket编程 Socket 常用接口 Socket服务端业务编码 代码说明 文件服务(fileServe) 消息服务(msgServe) 消息写会(write ...

  2. 如何分发大文件、大文件传输解决方案

    随着云计算.大数据技术不断发展,4K 视频.虚拟现实(VR).视频直播等互联网应用领域不断升级更新,企业网.数据中心规模持续扩大,企业拥有的数据急剧增长,海量文件随之产生. 同时,互联网时代,众多行业 ...

  3. 镭速传输,助力华为大文件传输速率提升十倍

    成立于1987年的华为是全球领先的ICT(信息与通信)基础设施和智能终端提供商,致力于把数字世界带入每个人.每个家庭.每个组织,构建万物互联的智能世界.始终坚持在通信网络.IT.智能终端和云服务等领域 ...

  4. 企业经常需要进行传输文件,大文件传输有哪些方法?

    ​ 经常会遇到需要进行大文件传输的时候,那么如何进行大文件传输呢?有那些以方法可快速的进行大文件传输,一般来说,在工作中经常需要用到文件传输或者数据传输的时候,都会用一些常规的方式,比如邮件传输,邮寄 ...

  5. VC++中使用内存映射文件处理大文件

    引言 文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile().WriteFile().ReadFile() ...

  6. python按行读取文件效率高吗_Python按行读取文件的实现方法【小文件和大文件读取】...

    本文实例讲述了Python按行读取文件的实现方法.分享给大家供大家参考,具体如下: 小文件: #coding=utf-8 #author: walker #date: 2013-12-30 #func ...

  7. PHP获取文件夹内所有文件包括子目录文件的名称或路径

    /* * new getFile($_dir[,$_emptyDir,$_fileType]); * @parma $_dir 是目录名称 * @parma $_emptyDir 是否获取空文件夹,选 ...

  8. Python 生成大量文件及大文件

    Python 生成大量文件及大文件 很多时候测试需要大量的文件和很大的文件,这些手动操作是非常麻烦的,现在来看看使用python自动生成 一次生成大量小文件(速度很慢) import os impor ...

  9. Flink重写Iceberg数据湖小文件变大文件

    1. 重写小文件变大文件 Flink支持Batch任务,将iceberg表的小文件重写成大文件 合并前HDFS的metadata和data目录文件如下: [root@flink1 ~]# [root@ ...

最新文章

  1. .NET面向上下文、AOP架构模式(实现)
  2. 区块链课程笔记-第一课哈希算法在加密中的应用
  3. CPU亲和性的使用与机制--含Xen中VCPU和PCPU的綁定方法
  4. keepalived 构建主备mysql
  5. 关于NSKeyedArchiver的编码格式
  6. 【高等数学】高等数学基础理论归纳
  7. 在工作或学习中要留出喝茶的时间
  8. 畅玩4x 刷linux,荣耀4x如何root
  9. OpenCV-Python实战(番外篇)——利用增强现实制作美颜挂件,让你的照片与众不同
  10. 【只推荐一位】推荐一位资深Python爱好者,现任世界500强架构师
  11. Qtum手机钱包教程
  12. txt文本换行(txt文本设置、wps替换换行)
  13. 【TensorFlow】简单解释----什么是张量(tensor)
  14. 【MAC】手动下载安装docker
  15. HD AUDIO再2003安装的问题
  16. 2022国产芯片技术创新与市场应用论坛即将召开
  17. C语言/C++基础之五彩炫酷珠
  18. 第一个Python爬虫-抓取煎蛋网上图片
  19. EXTJS开发过程遇到的一些问题的小结(转自麦田守望者)
  20. lol1月8日服务器维护,LOL2018年1月16日停机维护公告_LOL国服8.1版本更新内容汇总_飞翔教程...

热门文章

  1. 文件列表出现分页按钮
  2. 你不知道的车牌识别系统
  3. 教程 | OpenCV4中的极坐标变换
  4. 基于OpenCV的实时停车地点查找
  5. 关于Spring中的context:annotation-config/配置(开启注解)
  6. 第十一周作业关于json
  7. 【Centos】利用Vultr服务器和namesilo布网
  8. 分布式服务框架-原理与实践:14---流量控制-学习笔记(理论篇)
  9. python 的文件目录拷贝转移,自动递归目录建立目录
  10. 进行博客博文管理的设计