网站上的图分辨率做多少,网站栏目建设存在的问题,常州网站建设外包公司哪家好,seo推广是什么一、基础知识
UDP#xff08;UserDatagramProtocol#xff09;是一个简单的面向消息的传输层协议#xff0c;尽管UDP提供标头和有效负载的完整性验证#xff08;通过校验和#xff09;#xff0c;但它不保证向上层协议提供消息传递#xff0c;并且UDP层在发送后不会保留…一、基础知识
UDPUserDatagramProtocol是一个简单的面向消息的传输层协议尽管UDP提供标头和有效负载的完整性验证通过校验和但它不保证向上层协议提供消息传递并且UDP层在发送后不会保留UDP 消息的状态。因此UDP有时被称为不可靠的数据报协议。如果需要传输可靠性则必须在用户应用程序中实现。
所以为了利用UDP的传输速度并保证传输的可靠性需要在UDP的基础上实现可靠的数据传输协议。为了开发的效率作者选择了开源的UDT做为基础并在其基础上构建文件和目录的传输。
二、服务端
数据结构/配置定义
pub.h 定义通用数据类型和转换方法
#pragma once#include QObjectusing namespace std;///请求类型定义
enum reqType
{finished -1, ///finished the sending.file, folder
};///请求定义
struct fileReq
{int type; //reqTypeQString name; //name for file or folderfileReq(){type reqType::file;}
};///QString转string
string _Q2S(QString qstr);
///string转QString
QString _S2Q(string str);config.h 定义基础配置信息
#pragma once#include QObjectclass config : public QObject
{Q_OBJECTpublic:config(QObject *parent nullptr);~config();///获取本地存储路径QString getStoreDir() { return m_StoreDir; };///获取本地服务端口QString getPort() { return m_Port; };///设置本地存储路径void setStoreDir(QString dir) { m_StoreDir dir; };///设置本地服务端口void setPort(QString port) { m_Port port; };
private:///本地存储路径QString m_StoreDir;///本地服务端口QString m_Port;
};///获取全局配置对象
config *getConfig();监听和接收连接
建立主线程接收客户端的连接并对每个连接开启新的子线程。
void mainThread::run()
{emit log(listening...);sockaddr_storage clientaddr;int addrlen sizeof(clientaddr);UDTSOCKET fhandle;while (m_start){if (UDT::INVALID_SOCK (fhandle UDT::accept(m_serv, (sockaddr *)clientaddr, addrlen))){emit log(UDT::getlasterror().getErrorMessage());break;}char clienthost[NI_MAXHOST];char clientservice[NI_MAXSERV];getnameinfo((sockaddr *)clientaddr, addrlen, clienthost, sizeof(clienthost), clientservice, sizeof(clientservice), NI_NUMERICHOST | NI_NUMERICSERV);QString msg QString(new connection: %1:%2).arg(clienthost).arg(clientservice);emit log(msg);startClientThread(fhandle);}emit log(Service end.);
}
处理请求
子线程执行主要为三部分1. 接收和解析请求 2. 处理请求 3. 传输结束
void fileThread::run()
{getReqs(m_fileReq);foreach(fileReq req, m_fileReq)procReq(req);sendEnd();
}
接收请求方法中将会从客户端接收请求头的大小然后接收请求的具体内容并填充至请求列表。请求支持单个文件或目录类型。
void fileThread::getReqs(QListfileReq reqlist)
{reqlist.clear();// aquiring file name information from clientchar file[1024];int len;if (UDT::ERROR UDT::recv(m_client, (char*)len, sizeof(int), 0)){QString msg getReqs size: QString::fromLocal8Bit(UDT::getlasterror().getErrorMessage());emit log(msg);return;}if (UDT::ERROR UDT::recv(m_client, file, len, 0)){QString msg getReqs: QString::fromLocal8Bit(UDT::getlasterror().getErrorMessage());emit log(msg);return;}file[len] \0;QString info QString::fromUtf8(file);QStringList reqs info.split(;);foreach(QString req, reqs){QStringList file req.split(,);if (file.size() 1){fileReq r;r.type file.at(0).toInt();r.name file.at(1);reqlist.append(r);}}
}
获取请求列表后按照文件和目录类型以此处理。
void fileThread::procReq(fileReq req)
{if (req.type reqType::file)sendFile(req);else if (req.type reqType::folder)sendDir(req);
}
针对目录类型需要进行递归遍历获取文件并发送。
每次发送文件之前需要先发送头信息以告知客户端发送类型、文件名称和文件大小。
bool fileThread::sendHeader(fileReq req, int64_t fileSize)
{QString header QString(%1,%2,%3).arg(req.type).arg(req.name).arg(fileSize);QByteArray arr header.toUtf8();int size arr.size();// send header size informationif (UDT::ERROR UDT::send(m_client, (char*)size, sizeof(int), 0)){errUDT( send header size);return false;}// send header informationif (UDT::ERROR UDT::send(m_client, arr.data(), size, 0)){errUDT(send header);return false;}return true;
}
执行文件发送
bool fileThread::sendFile(fileReq req, const QString root, bool keepDir)
{QString fullname joinFullPath(getConfig()-getStoreDir(), root, req.name);fullname fullname.replace(/, \\);string file _Q2S(fullname);//open the filefstream ifs(file, ios::in | ios::binary);ifs.seekg(0, ios::end);int64_t size ifs.tellg();ifs.seekg(0, ios::beg);// send the headerif (!keepDir){QFileInfo info(req.name);req.name info.fileName();} if(!sendHeader(req, size)) return false;UDT::TRACEINFO trace;UDT::perfmon(m_client, trace);// send the fileint64_t offset 0;if (UDT::ERROR UDT::sendfile(m_client, ifs, offset, size)){errUDT(sendfile);return false;}UDT::perfmon(m_client, trace);cout speed trace.mbpsSendRate Mbits/sec endl;QString msg QString(speed %1 Mb/s).arg(trace.mbpsSendRate);emit log(msg);ifs.close();return true;
}
三、客户端
config.h配置定义
#pragma once#include QObjectclass config : public QObject
{Q_OBJECTpublic:config(QObject *parent nullptr);~config();///获取主机名/IPQString getHost() { return m_Host; };///获取端口QString getPort() { return m_Port; };///获取本地保存目录QString getLocalDir() { return m_LocalDir; };///获取服务端待下载文件名QString getServerFile() { return m_ServerFile; };///获取服务端待下载目录QString getServerDir() { return m_ServerDir; };///设置主机名/IPvoid setHost(QString host) { m_Host host; };///设置端口void setPort(QString port) { m_Port port; };///设置本地保存目录void setLocalDir(QString dir) { m_LocalDir dir; };///设置服务端待下载文件名void setServerFile(QString file) { m_ServerFile file; };///设置服务端待下载目录void setServerDir(QString dir) { m_ServerDir dir; };
private:///主机名/IPQString m_Host;///端口QString m_Port;///本地保存目录QString m_LocalDir;///服务端待下载文件名QString m_ServerFile;///服务端待下载目录QString m_ServerDir;
};config *getConfig();客户端负责实现请求的发送和文件接收。
发送请求
bool fileThread::sendReq()
{// send name information of the requested fileQStringList reqs;if (!getConfig()-getServerFile().isEmpty())reqs.append(QString(%1,%2).arg(reqType::file).arg(getConfig()-getServerFile()));if (!getConfig()-getServerDir().isEmpty())reqs.append(QString(%1,%2).arg(reqType::folder).arg(getConfig()-getServerDir()));QByteArray reqFile reqs.join(;).toUtf8();int len reqFile.size();if (UDT::ERROR UDT::send(m_client, (char*)len, sizeof(int), 0)){cout send: UDT::getlasterror().getErrorMessage() endl;return false;}if (UDT::ERROR UDT::send(m_client, reqFile.data(), len, 0)){cout send: UDT::getlasterror().getErrorMessage() endl;return false;}return true;
}
接收文件
先接收头信息然后接收文件数据。
bool fileThread::recvFile()
{// get size informationbool result false;int len;if (UDT::ERROR UDT::recv(m_client, (char*)len, sizeof(int), 0)){errUDT(recv header size);return result;}char buffer[1024];if (UDT::ERROR UDT::recv(m_client, buffer, len, 0)){errUDT(recv header);return result;}buffer[len] \0;QString info QString::fromUtf8(buffer);QStringList ls info.split(,);if (ls.size() 3){emit log(recvFile: illegal params.);return result;}int type ls.at(0).toInt();QString filename ls.at(1);if (type reqType::finished){emit log(server side finished.);return false;}else if (type reqType::folder) ///dir{QString path getConfig()-getLocalDir() \\ filename;QDir dir;bool result dir.mkpath(path);if (!result)emit log(failed to make dir: path);return result;}int64_t size ls.at(2).toInt();if (size 0){emit log(no such file: filename);return false;}UDT::TRACEINFO trace;UDT::perfmon(m_client, trace);// receive the fileQString path getConfig()-getLocalDir() \\ filename;string localFile _Q2S(path.replace(/,\\));fstream ofs(localFile, ios::out | ios::binary | ios::trunc);int64_t recvsize;int64_t offset 0;if (UDT::ERROR (recvsize UDT::recvfile(m_client, ofs, offset, size))){errUDT(recvfile);}elseresult true;UDT::perfmon(m_client, trace);emit log(QString(speed %1 Mb/s).arg(trace.mbpsRecvRate));ofs.close();return result;
}
四、程序示例
服务端存储目录 启动服务端 启动客户端 执行下载“测试”目录后的结果如下图 接收目录 可执行程序链接
(27条消息) 【免费】【可执行程序】基于UDT的文件目录可靠传输CQt资源-CSDN文库