index为FTP功能模块,controlthread控制连接状态,断线重连,使用的QThread。
index.cpp
#include "index.h"
#include "mainwindow.h"
#include "controlthread.h"
#include <QBuffer>
#include <string>
#include <sys/time.h>
//获取buffer
extern BYTE *g_readBuf;
extern unsigned char * g_pRgbBuffer;
extern Width_Height g_W_H_INFO;
extern struct timeval t1, t2; //计时器起点
extern QString value_freq; //传输间隔
extern int failCount;
//构造函数
Index::Index()
{
this->pCommon = new Common;
if(!this->initSocketLib()){
this->error(this->errorMsg);
exit(-1);
}
}
//析构函数
Index::~Index()
{
closesocket(this->ctrlSock);
closesocket(this->dataConnSock);
closesocket(this->dataTransSock);
WSACleanup();
}
//程序开始
void Index::appStart()
{
cout << endl << "欢迎使用sindftp! (本软件出自www.sindsun.com)" <<endl << endl;
if(!this->login()){
this->error(this->errorMsg);
cout << "error code is " << replyCode << endl;
Sleep(3000);
system("cls");
this->appStart();
}
this->menuList();
}
//登录
bool Index::login()
{
this->ctrlSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(this->ctrlSock == static_cast<unsigned>(SOCKET_ERROR)){
this->error("创建socket失败");
exit(-1);
}
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(static_cast<unsigned short>(this->portNum));
addr.sin_addr.s_addr = inet_addr(this->hostName.c_str());
int connStatus = connect(this->ctrlSock, reinterpret_cast<sockaddr *>(&addr), sizeof (addr));
if(connStatus == -1){
this->errorMsg = "连接服务器失败";
return false;
}
if(!this->recvFromRemote() || this->replyCode != 220){
this->errorMsg = "连接服务器错误";
return false;
}
//进入登录
this->requestString = "USER " + this->userName;
if(!sendToRemote(true) || this->replyCode != 331){
this->errorMsg = "用户名错误";
return false;
}
this->requestString = "PASS " + this->userPwd;
if(!sendToRemote(true) || this->replyCode != 230){
this->errorMsg = "用户名或密码错误";
return false;
}
return true;
}
//菜单列表
void Index::menuList()
{
cout << "----------- sindftp ------------" << endl << endl;
cout << "--------------------------------" << endl << endl;
cout << "显示菜单\t menu" << endl << endl;
cout << "切换目录\t cwd dirname" << endl << endl;
cout << "文件列表\t list dirname" << endl << endl;
//cout << "文件重命名\t rename filename" << endl << endl;
cout << "上传文件\t upload filename" << endl << endl;
cout << "下载文件\t download filename" << endl << endl;
cout << "删除文件\t del filename" << endl << endl;
cout << "帮助信息\t help" << endl << endl;
cout << "清除屏幕\t clear" << endl << endl;
cout << "退出平台\t quit" << endl << endl;
cout << "--------------------------------" << endl;
cout << endl;
localInput();
}
//本地命令输入处理
void Index::localInput()
{
requestString = "";
responseString = "";
errorMsg = "";
string userInputCmd = "";
cout << endl;
cout << "input cmd ### " ;
cin.sync();
getline(cin, userInputCmd);
if(userInputCmd.length() < 1){
errorMsg = "请输入正确的命令!";
localInput();
return;
}
//分割命令与参数
string cmd = "";
string param = "";
bool cmdOver = false;
for(char ch:userInputCmd){
if(ch == ' '){
cmdOver = true;
continue;
}
if(cmdOver){
param += ch;
}else{
cmd += ch;
}
}
//处理命令
if(cmd == "help"){ //帮助
requestString = "HELP";
sendToRemote(true);
}else if(cmd == "menu"){ //菜单
menuList();
}else if(cmd == "cwd"){ //改变目录
//改变数据目录
requestString = "CWD "+param;
sendToRemote(true);
}else if(cmd == "list"){ //列表
getList(param);
}else if(cmd == "rename"){ // 重命名
}else if(cmd == "upload"){ //上传
uploadFile(param);
}else if(cmd == "download"){ //下载
downloadFile(param);
}else if(cmd == "del"){ //删除
deleteFile(param);
}else if(cmd == "clear"){ //清除屏幕
system("cls");
menuList();
}else if(cmd == "quit"){ //退出
cout << "Bye bye!" <<endl;
exit(-1);
}else{
//errorMsg = "请输入正确的命令!";
//error(errorMsg);
requestString = userInputCmd;
sendToRemote(true);
}
localInput();
}
//远程命令发送
bool Index::sendToRemote(bool returnReplyCode)
{
if(this->requestString.length() < 1){
this->errorMsg = "ftp命令不能为空";
return false;
}
cout << endl << setw(10) << "发送命令: " << setw(10) << requestString << endl;
requestString += "\r\n";
requestString = this->pCommon->gbkToUtf8(requestString.c_str());
unsigned int cmdLength = requestString.length();
char *tmpCmdStr = new char[cmdLength];
memset(tmpCmdStr, 0, cmdLength);
memcpy(tmpCmdStr, requestString.c_str(), cmdLength);
int sendStatus = send(this->ctrlSock, tmpCmdStr, static_cast<int>(cmdLength), 0);
delete [] tmpCmdStr;
if(sendStatus == -1){
this->errorMsg = "请求失败";
return false;
}
if(returnReplyCode){
return recvFromRemote();
}
return true;
}
//接收远程响应
bool Index::recvFromRemote()
{
if(this->ctrlSock == INVALID_SOCKET){
this->errorMsg = "服务已断开 ";
return false;
}
char buf[MAX_MSG_SIZE];
int recvStatus = -1;
responseString = "";
while(true){
memset(buf, 0, MAX_MSG_SIZE);
recvStatus = recv(this->ctrlSock, buf, MAX_MSG_SIZE, MSG_PARTIAL);
//cout << "######### " << buf << endl;
if(recvStatus > 0){
responseString = buf;
// ofstream myfile("recvFromRemote.txt", ios::app);
// myfile << responseString << '=' << recvStatus<< '=' <<'\n';
// myfile << buf<< '\n';
// myfile.close();
break;
}
Sleep(50);
}
if(responseString.length() == 0){
this->errorMsg = "接收数据失败";
return false;
}
this->success(responseString);
return this->getReplyCode();
}
//处理远程响应,返回响应码
bool Index::getReplyCode()
{
string tmpMsg = "";
tmpMsg = responseString.substr(0, 3);
char charCode[4] = {};
memcpy(charCode, tmpMsg.c_str(), tmpMsg.length());
charCode[3] = '\0';
this->replyCode = static_cast<unsigned int>(atoi(charCode));
// ofstream myfile("replyCode.txt", ios::app);
// myfile << responseString<<'\n';
// myfile << tmpMsg<< '\n';
// myfile << charCode<< '\n';
// myfile << replyCode << ' ' << tmpMsg.length() << '\n';
if( !(this->replyCode >= 1 && this->replyCode <= 1000)){
this->errorMsg = "getReplyCode"; //"无法获取到响应码";
// myfile <<"getReplyCode ErrorCode " <<replyCode << '\n';
// myfile.close();
return false;
}
// myfile.close();
return true;
}
//写日志文件
bool Index::writeLogs(string msg)
{
if(msg.length() == 0){
return false;
}
SYSTEMTIME sTime;
GetLocalTime(&sTime);
char fullTime[20];
memset(fullTime, 0, 20);
sprintf(fullTime, "%4d-%02d-%02d %02d:%02d:%02d", sTime.wYear, sTime.wMonth, sTime.wDay,
sTime.wHour, sTime.wMinute, sTime.wSecond);
msg = "\r\n" + msg;
msg = fullTime + msg;
fstream fs;
fs.open("c:/tools/datasock_recv_logs.log", ios::app);
fs << msg << endl;
fs.close();
return true;
}
//显示错误
void Index::error(string errorStr)
{
if(errorStr.length() > 0){
cout << endl;
cout << "Error: " << errorStr;
cout << endl;
}
}
//显示成功
void Index::success(string sucStr)
{
if(sucStr.length() > 0){
cout << endl;
cout << "服务响应:" << endl;
cout << sucStr;
cout << endl;
}
}
//按值选择数据传输模式
bool Index::transModelSelect()
{
if(transModeVal == TransMode::PORT){
return portMode();
}else if(transModeVal == TransMode::PASV){
return pasvMode();
}else{
return (pasvMode() || portMode());
}
}
//PORT主动传输模式
bool Index::portMode()
{
dataConnSock = socket(AF_INET, SOCK_STREAM, 0);
if(dataConnSock == static_cast<unsigned>(SOCKET_ERROR)){
errorMsg = "初始化数据socket失败";
return false;
}
portDataConnCount += 1;
sockaddr_in addr;
int addrLength = sizeof (addr);
int getsocknameStatus = getsockname(ctrlSock, reinterpret_cast<sockaddr *>(&addr), &addrLength);
if(getsocknameStatus == -1){
errorMsg = "获取socket信息失败";
return false;
}
int localPort = ntohs(addr.sin_port) + portDataConnCount; //获取机地随机端口+1
addr.sin_family = AF_INET;
addr.sin_port = htons(static_cast<u_short>(localPort));
//addr.sin_addr.s_addr = htonl(INADDR_ANY);
int countBind = 0;
int bindStatus = -1;
while(bindStatus != 0){
bindStatus = bind(dataConnSock, reinterpret_cast<sockaddr *>(&addr), sizeof (addr));
countBind ++;
if(countBind >= 10){
break;
}
}
if(bindStatus == -1){
errorMsg = "绑定失败";
return false;
}
//主动模式下的监听一下要放到发送了请求数据命令之后
int listenStatus = listen(dataConnSock, 64);
if(listenStatus == -1){
errorMsg = "监听失败";
return false;
}
char *ipAddr = new char[32];
memset(ipAddr, 0, 32);
ipAddr = inet_ntoa(addr.sin_addr);
//计算端口号
int port1 = localPort/256;
int port2 = localPort%256;
if(portDataConnCount == 5000)
{
portDataConnCount = 0;
}
char charPortNum1[5];
char *portNum1 = itoa(port1, charPortNum1, 10);
char charPortNum2[5];
char *portNum2 = itoa(port2, charPortNum2, 10);
string portModelStr = ipAddr;
portModelStr += ",";
portModelStr += portNum1;
portModelStr += ",";
portModelStr += portNum2;
//替换里面的特殊字符
for(char &c:portModelStr){
if(c == '.'){
c = ',';
}
}
requestString = "PORT " + portModelStr;
if(!sendToRemote(true)){
errorMsg = "发送port命令失败";
ofstream myfile("portError.txt", ios::app);
myfile << errorMsg << endl;
myfile.close();
return false;
}
transModeVal = TransMode::PORT;
success("PORT模式开启成功");
return true;
}
//允许远程连接本机
bool Index::allowAccept()
{
if(transModeVal != TransMode::PORT){
return true;
}
sockaddr_in acceptAddr;
int acceptAddrLength = sizeof (acceptAddr);
dataTransSock = accept(dataConnSock, reinterpret_cast<sockaddr *>(&acceptAddr), &acceptAddrLength);
if(dataTransSock == static_cast<u_short>(INVALID_SOCKET)){
errorMsg = "接受请求失败";
return false;
}
return true;
}
//PASV连接模式
bool Index::pasvMode()
{
dataTransSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(dataTransSock == static_cast<u_short>(SOCKET_ERROR)){
errorMsg = "PASV socket初始化失败";
return false;
}
requestString = "PASV";
if(!sendToRemote(true) || replyCode != 227){
errorMsg = "PASV设置失败";
return false;
}
//处理字符串
char *newChar = strrchr(responseString.c_str(), '(') + 1;
string tmpStr = newChar + 1;
int spCount = 0;
string p1 = "";
string p2 = "";
for(char ch:tmpStr){
if(ch == ')'){
break;
}
if(ch == ','){
spCount ++;
continue;
}
if(spCount > 3){
if(spCount == 4){
p1 += ch;
}else{
p2 += ch;
}
}
}
int dataPort = atoi(p1.c_str()) * 256 + atoi(p2.c_str());
cout << "p1 " << p1 << endl;
cout << "p2 " << p2 << endl;
cout << "port " << dataPort << endl;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(static_cast<u_short>(dataPort));
addr.sin_addr.s_addr = inet_addr(hostName.c_str());
int ret = connect(dataTransSock, reinterpret_cast<sockaddr *>(&addr), sizeof (addr));
if(ret == -1){
errorMsg = "PASV连接服务器失败";
error(errorMsg);
closesocket(dataTransSock);
return false;
}
transModeVal = TransMode::PASV;
success("PASV模式开启成功");
return true;
}
//获取文件列表
bool Index::getList(string dirPath)
{
if(!transModelSelect()){
error(errorMsg);
return false;
}
requestString = "LIST "+dirPath;
if(!sendToRemote(true) || replyCode != 150 ){
error(errorMsg);
return false;
}
// if(!allowAccept()){
// error(errorMsg);
// return false;
// }
char *recvData = new char[MAX_MSG_SIZE];
memset(recvData, 0 , MAX_MSG_SIZE);
//如果是150,需要再接收一次数据发送结束的通知
int recvStatus = recv(dataTransSock, recvData, MAX_MSG_SIZE, 0);
if(recvStatus == -1){
error("接收数据失败");
return false;
}
success(pCommon->utf8ToGbk(recvData));
writeLogs(recvData);
success("写入日志文件成功");
delete [] recvData;
return true;
}
//上传文件
bool Index::uploadFile(string filePath)
{
if(!transModelSelect()){
return false;
}
char *fileName = strrchr(filePath.c_str(), '/');
requestString = "STOR ";
requestString += fileName +1;
if(!sendToRemote(true)){
errorMsg = "命令发送失败";
return false;
}
if(!allowAccept()){
return false;
}
fstream fs;
fs.open(filePath, ios::in);
if(!fs.is_open())
{
return false;
}
char buf[MAX_MSG_SIZE];
memset(buf, 0, sizeof(MAX_MSG_SIZE));
bool status = true;
while(!fs.eof()){
fs.read(buf, MAX_MSG_SIZE);
int storStatus = send(dataTransSock, buf, sizeof (buf), 0);
if(storStatus == -1){
errorMsg = "上传文件异常";
status = false;
break;
}
}
//closesocket(dataTransSock);
if(!status){
return false;
}
return true;
}
//上传Buffer
bool Index::uploadBuffer(QImage image)
{
//设置传输buffer
QByteArray data;
QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "BMP");
if(!transModelSelect()){
errorMsg = "transModelSelect";
return false;
}
//设置写入命令
string filePath = "./result.BMP";
char *fileName = strrchr(filePath.c_str(), '/');
requestString = "STOR ";
requestString += fileName +1;
if(!sendToRemote(true)){
errorMsg = "命令发送失败";
ofstream myfile("sendToRemote.txt", ios::app);
myfile << errorMsg << endl;
myfile.close();
return false;
}
if(!allowAccept()){
errorMsg = "allowAccept";
ofstream myfile("allowAccept.txt", ios::app);
myfile << errorMsg << endl;
myfile.close();
return false;
}
if(transModeVal == TransMode::PORT)
{
//开启等待回复
int on = 0;
setsockopt(dataTransSock, IPPROTO_TCP, TCP_NODELAY, (char *)&on, sizeof(int));
// //关闭缓冲区
// int nZero=1;
// setsockopt(dataTransSock, SOL_SOCKET, SO_SNDBUF, (char *)&nZero,sizeof(nZero));
// //设置超时时间
// int timeout = 2 * 1000;
// setsockopt(dataTransSock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int));
// setsockopt(dataTransSock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int));
}
//看看哪里出了问题
int err;
int len = sizeof(int);
int nOpt = getsockopt(dataTransSock, SOL_SOCKET, SOCKET_ERROR, (char*)err, &len);
if(0 && nOpt == SOCKET_ERROR)
{
int nErrorCode = WSAGetLastError();
ofstream myfile("socketError.txt", ios::app);
myfile << nErrorCode << ' ' << dataTransSock << ' ' << ctrlSock;
myfile << " \n";
myfile.close();
switch(nErrorCode)
{
case WSAEINTR:
case WSAEINPROGRESS:
case WSAEWOULDBLOCK:
//一般错误,不处理
break;
default:
//接收错误,关闭Socket
break;
}
errorMsg = "getsockopt";
return false;
}
//传输数据
int uploadStatus = send(dataTransSock, data.data(), data.size(), 0);
//重置计时器起点
gettimeofday(&t1, NULL);
failCount = 0;
//设置间隔,默认16帧
if(value_freq.isEmpty() || value_freq.isNull() || value_freq == 0)
{
Sleep(65);
}
else
{
Sleep(int(1000 / value_freq.toInt()));
}
if(uploadStatus == -1)
{
int nErrorCode = WSAGetLastError();
ofstream myfile("transmitionError.txt", ios::app);
myfile << uploadStatus << ' ' << nErrorCode << '\n';
myfile.close();
myfile << "11111";
myfile.close();
return false;
}
// //测速
// gettimeofday(&t2, NULL);
// timeuse = (t2.tv_sec - t1.tv_sec) + (double)(t2.tv_usec - t1.tv_usec)/1000000.0;
// ofstream myfile("delay.txt", ios::app);
// myfile << timeuse;
// myfile << " ";
// myfile.close();
closesocket(dataTransSock);
closesocket(dataConnSock);
return true;
}
//下载文件
bool Index::downloadFile(string fileName)
{
//先获取文件大小
requestString = "SIZE ";
requestString += fileName;
if(!sendToRemote(true)){
errorMsg = "命令发送失败";
return false;
}
int fileSize = atoi(responseString.substr(4).c_str());
if(!transModelSelect()){
return false;
}
requestString = "RETR " + fileName;
if(!sendToRemote(true)){
errorMsg = "命令发送失败";
return false;
}
if(!allowAccept()){
errorMsg = "连接失败";
return false;
}
//开始进行下载动作
string localPath = "d:/test/d_" + fileName;
fstream fs;
fs.open(localPath, ios::app);
int countFileSize = 0;
char *buf = new char[MAX_MSG_SIZE];
while (countFileSize < fileSize) {
memset(buf, 0, MAX_MSG_SIZE);
recv(dataTransSock, buf, MAX_MSG_SIZE, 0);
if(strlen(buf) < 1){
break;
}
fs << buf ;
countFileSize += strlen(buf);
}
fs.close();
return true;
}
//删除文件
bool Index::deleteFile(string fileName)
{
if(!transModelSelect()){
return false;
}
requestString = "DELE " + fileName;
if(!sendToRemote(true)){
errorMsg = "命令发送失败";
return false;
}
return true;
}
//网络组件初始化
bool Index::initSocketLib()
{
WSADATA wsa;
WORD vCode = MAKEWORD(2, 2);
int wsaStartupStatus = WSAStartup(vCode, &wsa);
if(wsaStartupStatus == -1){
this->errorMsg = "初始化套接字库失败,请重试";
return false;
}
//检查套接字版本号
if(LOBYTE(wsa.wVersion) != 2 || HIBYTE(wsa.wHighVersion) != 2){
WSACleanup();
this->errorMsg = "套接字版本号不符合,请重新配置";
return false;
}
return true;
}
index.h
#ifndef INDEX_H
#define INDEX_H
#include <iostream>
#include <string>
#include <winsock2.h>
#include <fstream>
#include <windows.h>
#include <iomanip>
#include <locale>
#include <QBuffer>
#include <QRgb>
#include "common.h"
using namespace std;
#define MAX_MSG_SIZE 1024
class Index{
public:
Index();
~Index();
//程序开始
void appStart();
public:
Common *pCommon;
enum TransMode{
AUTO, PORT, PASV
};
//主机名
string hostName = "";
//端口号
int portNum = 0;
//用户名
string userName = "";
//密码
string userPwd = "";
//操作命令socket
SOCKET ctrlSock;
//数据连接socket
SOCKET dataConnSock;
//数据传输socket
SOCKET dataTransSock;
//命令字符串
string requestString = "";
//接收的响应字符串
string responseString = "";
//响应码
unsigned int replyCode = 0;
//主动模式端口号计数
int portDataConnCount = 0;
//错误消息
string errorMsg = "";
//选择的连接模式
TransMode transModeVal = TransMode::PORT;
//网络组件初始化
bool initSocketLib();
//登录
bool login();
//退出登录
bool logout();
//菜单列表
void menuList();
//本地命令输入处理
void localInput();
//远程命令发送
bool sendToRemote(bool returnReplyCode=false);
//接收远程响应
bool recvFromRemote();
//处理远程响应,返回响应码
bool getReplyCode();
//写日志文件
bool writeLogs(string msg);
//显示错误
void error(string errorStr);
//显示成功
void success(string sucStr);
//按值选择数据传输模式
bool transModelSelect();
//PORT主动传输模式
bool portMode();
//允许远程连接本机
bool allowAccept();
//PASV连接模式
bool pasvMode();
//ftp参数初始化设置
bool initFtpParam();
//获取文件列表
bool getList(string dirPath);
//编辑名称
bool setRename(string fileName);
//上传文件
bool uploadFile(string filePath);
//上传QImage
bool uploadBuffer(QImage image);
//下载文件
bool downloadFile(string fileName);
//删除文件
bool deleteFile(string fileName);
};
#endif // INDEX_H
controlthread.cpp
#include "controlthread.h"
#include "mainwindow.h"
#include "index.h"
#include <QDebug>
#include <sys/time.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
extern bool startCounting;
extern UploadThread *m_thread2;
struct timeval t1, t2; //global
extern bool counterThreadSwitch;
double timeuse;
int failCount;
using namespace std;
ControlThread::ControlThread(QObject *parent) : QThread(parent)
{
}
void ControlThread::run()
{
//测速起点
gettimeofday(&t1, NULL);
forever
{
if(startCounting && counterThreadSwitch)
{
countDown();
}
else if(!startCounting && counterThreadSwitch)
{
startCounting = true;
failCount = 0;
gettimeofday(&t1, NULL);
m_thread2->start();
}
else if(!counterThreadSwitch)
{
break;
}
}
}
void ControlThread::countDown()
{
//测速终点
gettimeofday(&t2, NULL);
timeuse = (t2.tv_sec - t1.tv_sec) + (double)(t2.tv_usec - t1.tv_usec)/1000000.0;
// ofstream myfile("countDown.txt", ios::app);
// myfile << "good" << " " << timeuse<< '\n';
// myfile.close();
if(timeuse > 5)
{
//连续超时2次以后判定为失败
failCount += 1;
gettimeofday(&t1, NULL);
if(failCount == 2)
{
m_thread2->terminate();
while(m_thread2->isRunning())
{
Sleep(50);
}
startCounting = false;
ofstream myfile("countDownTimeout.txt", ios::app);
myfile << "bad" << " "<< timeuse << '\n';
myfile.close();
}
}
}
controlthread.h
#ifndef CONTROLTHREAD_H
#define CONTROLTHREAD_H
#include <QObject>
#include <QDebug>
#include <QThread>
class ControlThread : public QThread
{
Q_OBJECT
public:
explicit ControlThread(QObject *parent = 0);
public:
void run();
void countDown();
signals:
private:
public slots:
};
#endif // CONTROLTHREAD_H
参考:
Sindsun
http://www.sindsun.com
文章评论