天天看點

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);

        case WSAECONNRESET:

        case WSAECONNABORTED:

            // for tcp sockets this will be handled in QNativeSocketEngine::read

            ret = 0;

        default:

    } else {

        if (WSAGetLastError() == WSAEWOULDBLOCK)

        else

            ret = qint64(bytesRead);

    return ret;

至此,調用Windows API讀取資料。

4、發送資料

同樣分有緩存與無緩存方式,對無緩存方式:

qint64 QAbstractSocket::writeData(const char *data, qint64 size)

    if (d->state == QAbstractSocket::UnconnectedState) {

        d->socketError = QAbstractSocket::UnknownSocketError;

        setErrorString(tr("Socket is not connected"));

        qint64 written = d->socketEngine->write(data, size);

        if (written < 0) {

        } 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_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) {

        } else {

            int err = WSAGetLastError();

            WS_ERROR_DEBUG(err);

            switch (err) {

            case WSAECONNRESET:

            case WSAECONNABORTED:

                ret = -1;

                setError(QAbstractSocket::NetworkError, WriteErrorString);

                q->close();

            default:

            }

至此分析完畢。

繼續閱讀