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:
}
至此分析完毕。