基于MinGW和Msys使用QT、ACE QT与ACE在TCP服务端设计方面的比对

基于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
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。

QT与ACE在TCP服务端设计方面的比对 - yu_hongchang - 中原大鱼的博客
                                                                   图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 bug
    WSAAsyncSelect(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 events
    d->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 notifiers
    QList<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 timers
    for (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 process
    QString 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_USERDATA
    SetWindowLongPtr(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 events
                haveMessage = true;
                msg = d->queuedUserInputEvents.takeFirst();
            } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
                // process queued socket events
                haveMessage = 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 processing
                    haveMessage = false;
                    d->queuedUserInputEvents.append(msg);
                }
                if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
                    && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
                    // queue socket events for later processing
                    haveMessage = false;
                    d->queuedSocketEvents.append(msg);
                }
            }
            if (!haveMessage) {
                // no message - check for signalled objects
                for (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 it
                    continue;
                }
            }
            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 once
                        needWM_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 sent
                    bool 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 break
                break;
            }
            retVal = true;
        }
        // still nothing - wait for message or signalled objects
        canWait = (!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 events
        QCoreApplicationPrivate::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)  | 举报

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值