3.客户端的实现
由于客户端采用面向对象的Qt来编码,因此客户端比较容易实现。客户端的主要功能是:以列表形式显示指定目录下的文件以及相关属性;当用户点击文件列表中的某个目录时进入该子目录,并显示子目录下的文件列表;可以返回到当前目录的父目录。
根据上述需求,客户端的界面设计可以参考下图,即通过一个QTableWidget来显示某个目录下的所有文件,该部件的使用方法可以参考Qt Assitant。
接下来重点说明通信部分的功能实现。在服务器程序中,服务器向客户端发送的数据有两种,一种即为文件数,另一种则为所有文件信息形成的字节流。那么在客户端中则相应的有接受函数recvDirInfo()。
void configShowUI::recvDirInfo() { //1.先接受指定目录下文件的数目 int fileNum = 0; if (cli.recvData(&fileNum, sizeof(int)) == 0) { ui->statusLabel->setText("ERROR:receive fileNum error!"); } //2.接收指定目录下的文件名序列 char *dirbuf = NULL; if ((dirbuf = (char *)malloc(DATA_MAX * 10)) == NULL) { ui->statusLabel->setText("ERROR:malloc error!"); } memset(dirbuf, 0, DATA_MAX * 10); if (cli.recvData(dirbuf, DATA_MAX * 10) == 0) { ui->statusLabel->setText("ERROR:receive dir info error!"); } else { ui->statusLabel->setText("receive dir info success!"); this->printCurDir(QString(dirbuf), fileNum); free(dirbuf); } }
该函数有两种使用场景,其一为客户端与服务器连接成功时,此时服务器要向客户端发送指定目录下的文件列表(服务器主动发送),因此应该调用该函数。其二为当用户在客户端的文件列表中点击某个子目录时,客户端此时主动要求服务器发送指定子目录下的文件列表(服务器被动发送),此时应该调用该函数。
第二种情景可以采用Qt中的信号–槽机制(可以参考Qt Assitant了解相关概念),当用户点击了文件列表中的某个子目录时,该列表部件(fileWidget)就发送cdNextDir(QString)信号(该信号在),包含列表部件的当前部件(this)接收到这个信号后就做出相应操作,即执行槽函数cdNextDirOperation(QString)。这对信号和槽之间还传递了pathname这个变量,该变量存储了子目录的完整路径。下面的连接函数应该在当前部件的构造函数中完成。
connect(this->fileWidget, SIGNAL(cdNextDir(QString)), this, SLOT(cdNextDirOperation(QString)));
槽函数cdNextDirOperation(QString)指定的动作很简单,即先向服务器发送“读取下级目录”的指令,再将该子目录的完整路径发送到服务器端,接着再调用recvDirInfo()。可以看到客户端和服务器之间的数据交互是同步发生的。
void configShowUI::cdNextDirOperation(QString pathname) { char cd[] = "cd"; if (cli.sendData(cd, strlen(cd)) == 0) { ui->statusLabel->setText("ERROR: send cd command error!"); exit(1); } if (cli.sendData(pathname.toUtf8().data(), pathname.size()) == 0) { ui->statusLabel->setText("ERROR: send pathname error!"); exit(1); } this->recvDirInfo(); }
在上述的recvDirInfo()中,如果客户端成功接收了服务器发送的字节流数据,那么就调用printCurDir()对字节流进行解析,然后以列表形式显示在客户端。这里的解析过程由具体的需求而定,再次不再赘述。