QT分析之网络编程(三)

3、读取信息
在QAbstractSocket中,有两个成员是收发数据用的:readData()、writeData()
readData()有两种读取方式:有缓冲和无缓冲方式。基本原理是一致的,简单其见只分析无缓冲直接读取方式。
qint64 QAbstractSocket::readData(char *data, qint64 maxSize)
{
    Q_D(QAbstractSocket);
    if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid())
        d->socketEngine->setReadNotificationEnabled(true);

    if (!d->isBuffered) {
        if (!d->socketEngine)
            return -1;          // no socket engine is probably EOF
        qint64 readBytes = d->socketEngine->read(data, maxSize);
        if (readBytes < 0) {
            d->socketError = d->socketEngine->error();
            setErrorString(d->socketEngine->errorString());
        }
        if (!d->socketEngine->isReadNotificationEnabled())
            d->socketEngine->setReadNotificationEnabled(true);
        return readBytes;
    }

    if (d->readBuffer.isEmpty())
        // if we're still connected, return 0 indicating there may be more data in the future
        // if we're not connected, return -1 indicating EOF
        return d->state == QAbstractSocket::ConnectedState ? qint64(0) : qint64(-1);

    // If readFromSocket() read data, copy it to its destination.
    if (maxSize == 1) {
        *data = d->readBuffer.getChar();
        return 1;
    }

    qint64 bytesToRead = qMin(qint64(d->readBuffer.size()), maxSize);
    qint64 readSoFar = 0;
    while (readSoFar < bytesToRead) {
        const char *ptr = d->readBuffer.readPointer();
        int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar),
                                            d->readBuffer.nextDataBlockSize());
        memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
        readSoFar += bytesToReadFromThisBlock;
        d->readBuffer.free(bytesToReadFromThisBlock);
    }

    return readSoFar;
}
从前面(二)可以知道,socketEngine->read()实际调用的是QNativeSocketEngine::read()
qint64 QNativeSocketEngine::read(char *data, qint64 maxSize)
{
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::read(), -1);
    Q_CHECK_STATES(QNativeSocketEngine::read(), QAbstractSocket::ConnectedState, QAbstractSocket::BoundState, -1);

    qint64 readBytes = d->nativeRead(data, maxSize);

    // Handle remote close
    if (readBytes == 0 && d->socketType == QAbstractSocket::TcpSocket) {
        d->setError(QAbstractSocket::RemoteHostClosedError,
                    QNativeSocketEnginePrivate::RemoteHostClosedErrorString);
        close();
        return -1;
    }
    return readBytes;
}
除了一些相关的检查,就是调用QNativeSocketPrivate::nativeRead()
qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength)
{
    qint64 ret = -1;
    WSABUF buf;
    buf.buf = data;
    buf.len = maxLength;
    DWORD flags = 0;
    DWORD bytesRead = 0;
#if defined(Q_OS_WINCE)
    WSASetLastError(0);
#endif
    if (::WSARecv(socketDescriptor, &buf, 1, &bytesRead, &flags, 0,0) ==  SOCKET_ERROR) {
        int err = WSAGetLastError();
        WS_ERROR_DEBUG(err);
        switch (err) {
        case WSAEWOULDBLOCK:
            ret = -2;
            break;
        case WSAEBADF:
        case WSAEINVAL:
            setError(QAbstractSocket::NetworkError, ReadErrorString);
            break;
        case WSAECONNRESET:
        case WSAECONNABORTED:
            // for tcp sockets this will be handled in QNativeSocketEngine::read
            ret = 0;
            break;
        default:
            break;
        }
    } else {
        if (WSAGetLastError() == WSAEWOULDBLOCK)
            ret = -2;
        else
            ret = qint64(bytesRead);
    }

    return ret;
}
至此,调用Windows API读取数据。

4、发送数据
同样分有缓存与无缓存方式,对无缓存方式:
qint64 QAbstractSocket::writeData(const char *data, qint64 size)
{
    Q_D(QAbstractSocket);
    if (d->state == QAbstractSocket::UnconnectedState) {
        d->socketError = QAbstractSocket::UnknownSocketError;
        setErrorString(tr("Socket is not connected"));
        return -1;
    }

    if (!d->isBuffered) {
        qint64 written = d->socketEngine->write(data, size);
        if (written < 0) {
            d->socketError = d->socketEngine->error();
            setErrorString(d->socketEngine->errorString());
        } else if (!d->writeBuffer.isEmpty()) {
            d->socketEngine->setWriteNotificationEnabled(true);
        }
        if (written >= 0)
            emit bytesWritten(written);
        return written;
    }

    char *ptr = d->writeBuffer.reserve(size);
    if (size == 1)
        *ptr = *data;
    else
        memcpy(ptr, data, size);

    qint64 written = size;

    if (d->socketEngine && !d->writeBuffer.isEmpty())
        d->socketEngine->setWriteNotificationEnabled(true);
    return written;
}
查看QNativeSocketEngine::write():
qint64 QNativeSocketEngine::write(const char *data, qint64 size)
{
    Q_D(QNativeSocketEngine);
    Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::write(), -1);
    Q_CHECK_STATE(QNativeSocketEngine::write(), QAbstractSocket::ConnectedState, -1);
    return d->nativeWrite(data, size);
}

qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
{
    Q_Q(QNativeSocketEngine);
    qint64 ret = 0;
    // don't send more than 49152 per call to WSASendTo to avoid getting a WSAENOBUFS
    for (;;) {
        qint64 bytesToSend = qMin<qint64>(49152, len - ret);
        WSABUF buf;
        buf.buf = (char*)data + ret;
        buf.len = bytesToSend;
        DWORD flags = 0;
        DWORD bytesWritten = 0;

        int socketRet = ::WSASend(socketDescriptor, &buf, 1, &bytesWritten, flags, 0,0);

        ret += qint64(bytesWritten);

        if (socketRet != SOCKET_ERROR) {
            if (ret == len)
                break;
            else
                continue;
        } else if (WSAGetLastError() == WSAEWOULDBLOCK) {
            break;
        } else {
            int err = WSAGetLastError();
            WS_ERROR_DEBUG(err);
            switch (err) {
            case WSAECONNRESET:
            case WSAECONNABORTED:
                ret = -1;
                setError(QAbstractSocket::NetworkError, WriteErrorString);
                q->close();
                break;
            default:
                break;
            }
            break;
        }
    }
    return ret;
}
至此分析完毕。

时间: 2024-10-14 16:50:11

QT分析之网络编程(三)的相关文章

QT分析之网络编程(一)

首先对Windows下的网络编程总结一下: 如果是服务器,其WinSDK调用分别为: WSAStartup() -> socket() -> htons() / htonl() -> bind() -> listen() -> accept() -> recv() / send() -> closesocket() -> WSACleanup() 如果是客户端程序,其调用序列为: WSAStartup() -> socket() -> htons

QT分析之网络编程(二)

前面分析(一)之前没有看QT自带的文档,看了doc之后对QT的网络体系有一个大致的了解: QNatvieSocketEnginePrivate是OS相关的API封装,和QNativeSocketEngine一起构成具体平台SOCKET实现: QTcpSocket.QUdpSocket.QTcpServer构成底层的应用API:QSslSocket是SSL加密相关API: QHttp.QFtp构成高层次应该API: QNetworkAccessManager.QNetworkRequest.QNe

QT分析之网络编程(七)

接上面,进一步分析QNetworkAccessManager::createRequest()的实现.去除不重要的分支末节,看其调用的QNetworkReplyImplPrivate::setup()和QNetworkAccessManagerPrivate::findBackend()的代码. void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,

QT分析之网络编程(六)

在动手分析前,简单介绍一下HTTP协议.HTTP协议是一种为分布式,合作式,超媒体信息系统.它是一种通用的,无状态(stateless)的协议,除了应用于超文本传输外,它也可以应用于诸如名称服务器和分布对象管理系统之类的系统,这可以通过扩展它的请求方法,错误代码和报头来实现.HTTP的一个特点是数据表现形式是可输入的和可协商性的,这就允许系统能被建立而独立于数据传输.HTTP在1990年WWW全球信息刚刚起步的时候就得到了应用.该规范定义的协议用"HTTP/1.1"表示,是对RFC26

QT分析之网络编程(五)

今天分析QNetworkAccessManager.QNetworkRequest和QNetworkReply组成的高级抽象API序列.在动手之前,把doc中有关QNetworkAccessManager的介绍看了一遍.其使用方法大致是: QNetworkAccessManager * manager = new QNetworkAccessManager(this); QNetworkRequest request; request.setUrl(QUrl("http://www.baidu.

QT分析之网络编程(八)

话说昨日走到QNetworkReplyImplPrivate::_q_startOperation(),勾引出QNetworkAccessHttpBackend::open(),今日接着欣赏QT之美丽. void QNetworkAccessHttpBackend::open() {     QUrl url = request().url();     bool encrypt = url.scheme().toLower() == QLatin1String("https");  

QT分析之网络编程(四)

前面分析中,一个问题一直没有解决:新生成的SOCKET是什么时候加入WSASelect()的?另外还有一个不是很大的问题,close流程. 在QEventDispatcherWin32Private::doWsaAsyncSelect()中WSAAsyncSelect()设置一个断点,观察call stack: >    QtCored4.dll!QEventDispatcherWin32Private::doWsaAsyncSelect(int socket=0x00001628)  行633

实例分析J2ME网络编程的两种方法

编程|网络 本文描述了在J2me中开发主要使用的网络连接方法,分别详细介绍了使用http和socket两种方法. HttpConnection 首先我们先来看一个简单的例子吧: 主要用到的java包: javax.microedition.io.*; public String requestGET(String URLString,String URL) throws IOException{ // ===============================================

5.关于QT中的网络编程,QTcpSocket,QUdpSocket

 1 新建一个项目:TCPServer.pro A  修改TCPServer.pro,注意:如果是想使用网络库,需要加上network SOURCES += \     TcpServer.cpp \     TcpClient.cpp   HEADERS += \     TcpServer.h \     TcpClient.h   QT += gui widgets network   CONFIG += C++11 B 新建如下文件,因为要用到网络库,所以加上network C 编写