基于MinGW和Msys使用QT、ACE
目前在Linux图像界面开发方面QT大行其道,尤其在嵌入式方面。不能忽视他的存在啊,打算先在Windows的GNU环境中将QT用起来,底层开发库可以使用ACE做补充。
QT开发库可以在http://qt-project.org/downloads下载,选择了
Qt libraries 4.8.4 for Windows (minGW 4.4, 317 MB),和
Qt Creator 2.6.1 for Windows (52 MB)。另外必须另行下载minGW4.4。ACE在这里下载:http://www.cs.wustl.edu/~schmidt/ACE.html 。QT都是预编译版的直接安装即可,ACE参照ACE-INSTALL.html中的MinGW部分也可很快编译完成。
使用QT Creator编译ACE程序时,修改 pro文件,加上类似内容即可:
LIBS += -L C:\ACE_wrappers\ace -lACE
使用QT Creator编译ACE程序时,修改 pro文件,加上类似内容即可:
LIBS += -L C:\ACE_wrappers\ace -lACE
INCLUDEPATH += -I C:\ACE_wrappers
测试例子如下:
#include <QApplication>#include <QLabel>#include <QString>#include "ace/OS.h" #include <iostream>
#include <stdio.h>
#include<QtGui>
#include <QDebug>
int main(int argc, char *argv[])
{
ACE_Time_Value today = ACE_OS::gettimeofday();
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
qDebug() << "当前日期秒数 = " << today.sec();
return(0);
}
/*
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel *label = new QLabel("Hello Qt!");
label->show();
return app.exec();
}
*/
能编译通过,但有如下警告信息: C:\ACE_wrappers\ace\OS_main.h:213: 警告:"main" redefined C:\Qt\4.8.4\src\gui\kernel\qwindowdefs.h:158: 警告:this is the location of the previous definition 发现QT和ACE都对"main"进行了预编译处理,还好ACE可以关闭这个功能,在config.h中添加一行: #define ACE_DOESNT_DEFINE_MAIN 重新编译ACE,就不会再有这个重复定义main的问题了。
分享到:
阅读(64)
|
评论(0)
|
转载
(0)
|
举报
QT与ACE在TCP服务端设计方面的比对
|字号 订阅
使用《C++ GUI Qt4编程(第二版)》第15章节的服务端和客户端例子,运行环境windows ,QT creator。
图1 tcp服务端断点图
本打算通过查看代码找到readyRead信号的发送根源,这样才能清楚qt底层是如何分派io事件的,由于还不熟悉qt底层结构,没找到她。只能采用QT Creator的断点调试来看了,直接在槽readClient中设置个断点,利用客户端程序发送一包数据即可。服务端程序停止在了readClient,图下部分展示了tcp服务端接收到数据后的调用层次关系,看的出qt_internal_proc为应用程序回调接口。
那么新链接的socket句柄是如何传递给内核的呢?在
void
TripServer::
incomingConnection(
int
socketId)函数中设置断点,顺藤摸瓜。
bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, SocketState socketState,OpenMode openMode)
{
Q_D(QAbstractSocket);
#ifndef QT_NO_OPENSSL
if (QSslSocket *socket = qobject_cast<QSslSocket *>(this))
return socket->setSocketDescriptor(socketDescriptor, socketState, openMode);
#endif
d->resetSocketLayer();
d->socketEngine = QAbstractSocketEngine::createSocketEngine(socketDescriptor, this);
if (!d->socketEngine) {
d->socketError = UnsupportedSocketOperationError;
setErrorString(tr("Operation on socket is not supported"));
return false;
}
#ifndef QT_NO_BEARERMANAGEMENT
//copy network session down to the socket engine (if it has been set)
d->socketEngine->setProperty("_q_networksession", property("_q_networksession"));
#endif
bool result = d->socketEngine->initialize(socketDescriptor, socketState);
if (!result) {
d->socketError = d->socketEngine->error();
setErrorString(d->socketEngine->errorString());
return false;
}
if (d->threadData->eventDispatcher)
d->socketEngine->setReceiver(d);
QIODevice::open(openMode);
if (d->state != socketState) {
d->state = socketState;
emit stateChanged(d->state);
}
d->pendingClose = false;
d->socketEngine->setReadNotificationEnabled(true);
d->localPort = d->socketEngine->localPort();
d->peerPort = d->socketEngine->peerPort();
d->localAddress = d->socketEngine->localAddress();
d->peerAddress = d->socketEngine->peerAddress();
d->cachedSocketDescriptor = socketDescriptor;
return true;
}
当新链接到达后setSocketDescriptor函数被调用,在上面加粗部分d->socketEngine->setReadNotificationEnabled(true);将调用下面的函数:
void
QNativeSocketEngine::
setReadNotificationEnabled(
bool
enable)
{
Q_D(QNativeSocketEngine);
if (d->readNotifier) {
d->readNotifier->setEnabled(enable);
} else if (enable && d->threadData->eventDispatcher) {
d->readNotifier = new QReadNotifier(d->socketDescriptor, this);
d->readNotifier->setEnabled(true);
}
}
新到的socket连接,读通知器尚未创立,加粗部分被执行:
class QReadNotifier : public QSocketNotifier{public:
QReadNotifier(int fd, QNativeSocketEngine *parent): QSocketNotifier(fd, QSocketNotifier::Read, parent){ engine = parent; }protected:
bool event(QEvent *);QNativeSocketEngine *engine;};
QSocketNotifier::QSocketNotifier(int socket, Type type, QObject *parent): QObject(parent){if (socket < 0)qWarning("QSocketNotifier: Invalid socket specified");sockfd = socket;sntype = type;snenabled = true;Q_D(QObject);if (!d->threadData->eventDispatcher) {qWarning("QSocketNotifier: Can only be used with threads started with QThread");} else {d->threadData->eventDispatcher->registerSocketNotifier(this);}
}最终他向事件分配器eventDispatcher注册了这个socket通知器,我们看看这个事件分派器是如何注册这个socket的,在windows平台事件分派器为QEventDispatcherWin32,他的registerSocketNotifier过程如下:
void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier){
Q_ASSERT(notifier);
int sockfd = notifier->socket();
int type = notifier->type();
#ifndef QT_NO_DEBUG
if (sockfd < 0) {
qWarning("QSocketNotifier: Internal error");
return;
} else if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
return;
}
#endif
Q_D(QEventDispatcherWin32);
QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except };
QSNDict *dict = sn_vec[type];
if (QCoreApplication::closingDown()) // ### d->exitloop?
return; // after sn_cleanup, don't reinitialize.
if (dict->contains(sockfd)) {
const char *t[] = { "Read", "Write", "Exception" };
/* Variable "socket" below is a function pointer. */
qWarning("QSocketNotifier: Multiple socket notifiers for "
"same socket %d and type %s", sockfd, t[type]);
}
QSockNot *sn = new QSockNot;
sn->obj = notifier;
sn->fd = sockfd;
dict->insert(sn->fd, sn);
if (d->internalHwnd)
d->doWsaAsyncSelect(sockfd);
}
void QEventDispatcherWin32Private::doWsaAsyncSelect(int socket){Q_ASSERT(internalHwnd);int sn_event = 0;if (sn_read.contains(socket))sn_event |= FD_READ | FD_CLOSE | FD_ACCEPT;if (sn_write.contains(socket))sn_event |= FD_WRITE | FD_CONNECT;if (sn_except.contains(socket))sn_event |= FD_OOB;// BoundsChecker may emit a warning for WSAAsyncSelect when sn_event == 0// This is a BoundsChecker bug and not a Qt bugWSAAsyncSelect(socket, internalHwnd, sn_event ? unsigned(WM_QT_SOCKETNOTIFIER) : unsigned(0), sn_event);}找到瓜瓜了,原来最后使用了异步ioWSAAsyncSelect,当注册的事件发生时,回调给internalHwnd句柄,消息号为:WM_QT_SOCKETNOTIFIER。这个internalHwnd这里可以找到:oid QEventDispatcherWin32::createInternalHwnd(){Q_D(QEventDispatcherWin32);Q_ASSERT(!d->internalHwnd);if (d->internalHwnd)return;d->internalHwnd = qt_create_internal_window(this);#ifndef Q_OS_WINCE// setup GetMessage hook needed to drive our posted eventsd->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());if (!d->getMessageHook) {qFatal("Qt: INTERNALL ERROR: failed to install GetMessage hook");}
#endif
// register all socket notifiersQList<int> sockets = (d->sn_read.keys().toSet()+ d->sn_write.keys().toSet()+ d->sn_except.keys().toSet()).toList();for (int i = 0; i < sockets.count(); ++i)d->doWsaAsyncSelect(sockets.at(i));
// start all normal timersfor (int i = 0; i < d->timerVec.count(); ++i)d->registerTimer(d->timerVec.at(i));// trigger a call to sendPostedEvents()wakeUp();}
static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher){// make sure that multiple Qt's can coexist in the same processQString className = QLatin1String("QEventDispatcherWin32_Internal_Widget") + QString::number(quintptr(qt_internal_proc));WNDCLASS wc;wc.style = 0;wc.lpfnWndProc = qt_internal_proc;
wc.cbClsExtra = 0;wc.cbWndExtra = 0;wc.hInstance = qWinAppInst();wc.hIcon = 0;wc.hCursor = 0;wc.hbrBackground = 0;wc.lpszMenuName = NULL;wc.lpszClassName = reinterpret_cast<const wchar_t *> (className.utf16());RegisterClass(&wc);HWND wnd = CreateWindow(wc.lpszClassName, // classname
wc.lpszClassName, // window name
0, // style
0, 0, 0, 0, // geometry
0, // parent
0, // menu handle
qWinAppInst(), // application
0); // windows creation data.
if (!wnd) {qWarning("QEventDispatcher: Failed to create QEventDispatcherWin32 internal window: %d\n", (int)GetLastError());}
#ifdef GWLP_USERDATASetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)eventDispatcher);#else
SetWindowLong(wnd, GWL_USERDATA, (LONG)eventDispatcher);#endif
return wnd;}好了,这就回到了开头,接收到客户端数据后qt_internal_proc被调用,最终发送给readyRead信号的槽函数!另外qt程序消息分派:bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags){Q_D(QEventDispatcherWin32);if (!d->internalHwnd)createInternalHwnd();
d->interrupt = false;emit awake();bool canWait;bool retVal = false;bool seenWM_QT_SENDPOSTEDEVENTS = false;bool needWM_QT_SENDPOSTEDEVENTS = false;do {DWORD waitRet = 0;HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];QVarLengthArray<MSG> processedTimers;while (!d->interrupt) {DWORD nCount = d->winEventNotifierList.count();Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);MSG msg;bool haveMessage;if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {// process queued user input eventshaveMessage = true;msg = d->queuedUserInputEvents.takeFirst();} else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {// process queued socket eventshaveMessage = true;msg = d->queuedSocketEvents.takeFirst();} else {haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)&& ((msg.message >= WM_KEYFIRST&& msg.message <= WM_KEYLAST)|| (msg.message >= WM_MOUSEFIRST&& msg.message <= WM_MOUSELAST)|| msg.message == WM_MOUSEWHEEL|| msg.message == WM_MOUSEHWHEEL|| msg.message == WM_TOUCH#ifndef QT_NO_GESTURES|| msg.message == WM_GESTURE|| msg.message == WM_GESTURENOTIFY#endif
|| msg.message == WM_CLOSE)) {// queue user input events for later processinghaveMessage = false;d->queuedUserInputEvents.append(msg);}
if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)&& (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {// queue socket events for later processinghaveMessage = false;d->queuedSocketEvents.append(msg);}
}
if (!haveMessage) {// no message - check for signalled objectsfor (int i=0; i<(int)nCount; i++)pHandles[i] = d->winEventNotifierList.at(i)->handle();waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {// a new message has arrived, process itcontinue;}
}
if (haveMessage) {#ifdef Q_OS_WINCE// WinCE doesn't support hooks at all, so we have to call this by hand :((void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg);#endif
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {if (seenWM_QT_SENDPOSTEDEVENTS) {// when calling processEvents() "manually", we only want to send posted// events onceneedWM_QT_SENDPOSTEDEVENTS = true;continue;}
seenWM_QT_SENDPOSTEDEVENTS = true;} else if (msg.message == WM_TIMER) {// avoid live-lock by keeping track of the timers we've already sentbool found = false;for (int i = 0; !found && i < processedTimers.count(); ++i) {const MSG processed = processedTimers.constData()[i];found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);}
if (found)continue;processedTimers.append(msg);
} else if (msg.message == WM_QUIT) {if (QCoreApplication::instance())QCoreApplication::instance()->quit();return false;}
if (!filterEvent(&msg)) {TranslateMessage(&msg);
DispatchMessage(&msg);}
} else if (waitRet < WAIT_OBJECT_0 + nCount) {d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));} else {// nothing todo so breakbreak;}
retVal = true;}
// still nothing - wait for message or signalled objectscanWait = (!retVal&& !d->interrupt&& (flags & QEventLoop::WaitForMoreEvents));if (canWait) {DWORD nCount = d->winEventNotifierList.count();Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);for (int i=0; i<(int)nCount; i++)pHandles[i] = d->winEventNotifierList.at(i)->handle();emit aboutToBlock();waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);emit awake();if (waitRet < WAIT_OBJECT_0 + nCount) {d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));retVal = true;}
}
} while (canWait);if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) {// when called "manually", always send posted eventsQCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);}
if (needWM_QT_SENDPOSTEDEVENTS)PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);return retVal;}小结:QT:QT所有的事件包括io、定时器、windows消息等都在主线程中分派和处理。如果某个io事件处理耗时较长,将直接影响用户界面的操作体验,当然可以通过尽量缩短处理时间和分线程处理的方式来改善。QT在Socket io方面使用了WSAAsyncSelect,IO性能不如完成端口模式。
ACE:ACE通过反应器模式来调度io和定时器,他根据运行的平台和io事件分离方式提供了几种不同的反应器实现版本。基于select方式实现的ACE_Select_Reactor,基于多线程和select实现的ACe_TP_Reactor,基于windows的完成端口模式实现的ACE_WFMO_Reactor,基于linux/unix的/dev/poll或/dev/epoll实现的ACE_Dev_Poll_reactor。具体使用哪个反应器实现不会影响基于他的上层应用。
评论这张
转发至微博
分享到:
阅读(25)
|
评论(0)
|
转载
(0)
|
举报