最近由于要做一些关于QT方面的东西,需要访问网络,利用HTTP协议。刚刚好QT里实现了HTTP协议,即QHttp类,学习一下应该可以排上用场。 但是QT Assistant上说:The class works asynchronously, so there are no blocking functions.也就是说这个类里面的所有的函数都是异步的,不会阻塞。当你向服务器发送了一个request后,函数立即返回,不等服务器返回response,也不管结果是成功还是失败。在发送请求后,会触发一系列事件:
requestStarted(2) stateChanged(Connecting) stateChanged(Sending) dataSendProgress(77, 77) stateChanged(Reading) responseHeaderReceived(responseheader) dataReadProgress(5388, 0) readyRead(responseheader) dataReadProgress(18300, 0) readyRead(responseheader) stateChanged(Connected) requestFinished(2, false) done(false) 所有的结果的处理就是通过对触发的各种事件的响应来处理的。说到QT的事件响应机制,就不能不说connect方法,还有SIGNAL和SLOT机制。connect的原型如下: bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection ) 原理就是sender出发一个signal,然后receiver就会收到这个信号,然后调用slot指定的函数来处理这个事件。 说完了qt的现状,继续说我的需求:我想实现的系统需要的是一个同步的会阻塞的Http协议的实现,这跟QT的实现相悖。从网上搜索解决方案,找了好久,功夫不负有心人,终于找到了一些方法,但是这距离自己的实现还有一定的距离,毕竟没有亲自试验过,未必能用。网上给的思路很清楚,就是发起请求以后,让程序通过调用QEventLoop的exec方法,进入一个循环状态,等待事件相应,然后在需要的事件响应里,调用QEventLoop的exit方法强制退出循环,从而达到阻塞的目的。 有了这样一个方案,就开始朝着这个方向努力。阻塞调用http的类很快写好了,这个不太费力, 类有两个字段如下: private: QHttp *http; QEventLoop *loop; 简单起见,直接把http请求写到构造函数了,如下所示: MyHttp::MyHttp(char* host, char* url) { loop = new QEventLoop(this); // 创建QEventLoop对象 http = new QHttp(this); // 创建QHttp对象 connect(http, SIGNAL(done(bool)), // 绑定http的done事件和httpDone处理函数 this, SLOT(httpDone(bool))); http->setHost(host); // 设置要请求的服务器 http->get(url); // 调用get方法获取需要的网页的url cout<<"call get"<<endl; loop->exec(); // 进入循环等待 cout<<http->readAll().constData()<<endl; // 循环结束,打印输出结果 } 事件响应如下: void MyHttp::httpDone(bool res) { loop->exit(); // 处理done事件,强制退出循环等待 cout<<"the request is done: "; if (res) // 根据执行情况,打印执行结果 { cout<<"failed!"<<endl; } else { cout<<"successful!"<<endl; } } 调用这个类的main函数实现如下: int main(int argc, char** argv) { if (argc < 3) { cout<<"http <host> <page>"<<endl; return 0; } MyHttp *mh = new MyHttp(argv[1], argv[2]); // 创建MyHttp对象 return 0; } 这个是最开始的程序,编译通过,但是执行的时候却报错了:QEventLoop: Cannot be used without QApplication。错误说的很明白,没有QApplication不能使用QEventLoop,然后就想要加一个QApplication,然后,就把调用的main函数改成下面的样子: int main(int argc, char** argv) { if (argc < 3) { cout<<"http <host> <page>"<<endl; return 0; } QCoreApplication app(argc, argv); // 创建QCoreApplication对象 MyHttp *mh = new MyHttp(argv[1], argv[2]); return app.exec(); // 进入程序循环 } 然后,再编译运行,就搞定了。现在还有个问题就是程序运行完不会自动退出,但是这个已经不影响使用了,因为这个程序的目的是实现http的同步请求。 运行结果如下: [jason@localhost http]$ ./http www.baidu.com /index.htm call get the request is done: successful! <此处略去无数字:网页的源码实在太多了,省去了……> 这个过程其实并不是特别复杂,但是遇到了各种各样的问题,中间还是学到了一些东西,所以记下了这篇笔记,供以后参考用。
写于北航,2011年5月16日
by Jason