1 服务器运行逻辑
在前面几节中我们以及完成了数据包的收取(解析数据),数据包的发送(数据包的封包发送)
现在服务器逻辑如下:根据接收到命令号做出对应的处理,在对应的处理函数里把结果直接发送给客户端
CServerSocket* pserver = CServerSocket::getInstance(); int acceptCount = 0; if (pserver->InitSocket() == false) { MessageBox(NULL, _T("网络初始化失败,请检查网络状态"), _T("网络初始化失败!!"), MB_OK | MB_ICONERROR); exit(0); } while (CServerSocket::getInstance() != NULL) { if (pserver->AcceptClient() == false) { MessageBox(NULL, _T("接入用户失败,自动重试"), _T("接入用户失败!!"), MB_OK | MB_ICONERROR); acceptCount += 1; if (acceptCount >= 3) { MessageBox(NULL, _T("多次用户失败,退出程序"), _T("多次接入用户失败!!"), MB_OK | MB_ICONERROR); exit(0); } } // 接收数据,返回命令号,接收到的数据存放在m_packet成员变量里面 int cmd = pserver->DealCommand(); switch (cmd) { case 1:// 获取磁盘分区 MakeDriverInfo(); break; case 2:// 获取指定目录下的文件信息 MakeDirectoryInfo(); break; } }
2 获取文件和目录信息功能实现步骤
2.1 功能实现
首先服务器接收到获取文件和目录命令,从里面解析出数据,解析出获取哪个目录下的文件和目录
新增接口,返回路径信息
// 从包里面取出路径信息 bool GetFilePath(std::string& strPath) { if ((m_packet.sCmd >= 2) && (m_packet.sCmd <= 4)) { strPath = m_packet.strData; return true; } return false; }
-
获取目录信息
-
切换目录,判断是否有权限进入目录,或者是否是空的目录
-
找到目录下的第一个文件
-
找到一个就发送一个
-
通过第一个文件继续找其它文件
2.2 参考代码:
在本代码中采用的是找到一个文件或文件夹就直接发送给客户端,如果等全部找到再发送,会导致客户端半天没有回馈
int MakeDirectoryInfo() { // 获取目录 std::string strFilePath; if (!CServerSocket::getInstance()->GetFilePath(strFilePath)) { OutputDebugString(_T("当前的命令不是获取文件列表,命令解析错误")); return -1; } // 切换目录,判断是否有权限进入目录,或者是否是空的目录 if (_chdir(strFilePath.c_str()) != 0) { FILEINFO finfo; finfo.IsInvalid = true; finfo.HasNext = false; finfo.isDirectory = true; memcpy(finfo.szName, strFilePath.c_str(), strFilePath.size()); CPacket packet(2, (BYTE*)&finfo, sizeof(finfo)); CServerSocket::getInstance()->Send(packet); OutputDebugString(_T("没有权限访问该目录")); return -2; } //遍历目录下的文件和文件夹 // 先找到第一个文件,要准备一个信息缓冲区 fdata // _findfirst 返回标识与 filespec 规范匹配的文件或文件组的唯一搜索句柄 // 可在后续调用 _findnext 或 _findclose 时使用 _finddata_t fdata; int hfind = 0; if (hfind = (_findfirst("*", &fdata)) == -1) { OutputDebugString(_T("该目录下没有任何文件")); return -3; } // 查找与以前 _findfirst 调用中的 filespec 参数相匹配的下一个名称(如有),然后相应更改 fileinfo 结构内容。 // 获取到文件信息后需要发送给对方,这个时候需要将文件信息封装一下 -- 定义结构体保存文件信息 do { FILEINFO finfo; finfo.isDirectory = true; memcpy(finfo.szName, fdata.name, strlen(fdata.name)); CPacket packet(2, (BYTE*)&finfo, sizeof(finfo)); CServerSocket::getInstance()->Send(packet); } while (!_findnext(hfind, &fdata)); // 文件目录发送完毕给客户端返回一个空包,客户端好判断是否接收完毕 FILEINFO finfo; finfo.HasNext = false; CPacket packet(2, (BYTE*)&finfo, sizeof(finfo)); CServerSocket::getInstance()->Send(packet); return 0; }
2.2 文件信息的封装
由于对方想要的是文件信息,那么应该包含以下信息
-
文件名
-
是文件还是目录
-
是否有效
-
是否还有后续(用于接收方方便判断数据何时接收完毕)
所以我们需要把这些信息整合在一起,从而用到结构体,由于在c++里面类和结构体几乎一致,所以可以使用构造函数方便我们创建这个结构式简化赋值操作
代码如下:
typedef struct file_info { // 使用构造函数方便初始值赋值,不然创建一个对象每一个都得手动赋值 file_info() { IsInvalid = false; isDirectory = -1; HasNext = true; memset(szName, 0, sizeof(szName)); } // 是否无效 true无效,false有效,默认有效 bool IsInvalid; // 是否是目录,true目录,false文件,默认不是目录 bool isDirectory; // 文件名 char szName[256]; // 是否还有后续,false没有,true有,默认还有 bool HasNext; }FILEINFO, *PFILEINFO;