QAbstractSocket类的头文件通常包含了一些重要的成员函数、信号和槽以及一些常量定义。
- 成员函数:
- connectToHost():用于与指定的主机和端口建立连接。
- writeData():向套接字写入数据。
- readData():从套接字读取数据。
- disconnectFromHost():关闭套接字连接。
- error():返回套接字的错误状态。
- 信号和槽:
- connected():在套接字成功连接到主机时触发的信号。
- readyRead():当套接字有可读数据时触发的信号。
- error():在套接字发生错误时触发的信号。
- 常量定义:
- ConnectedState:表示套接字已连接的状态。
- UnconnectedState:表示套接字未连接的状态。
- HostLookupState:表示套接字正在进行主机查找的状态。
QAbstractSocket类是一个抽象类,不能直接实例化,而是通过其具体子类(如QTcpSocket和QUdpSocket)来使用。它提供了一组通用的接口和功能,用于处理网络通信和数据传输。
1.connectToHost
设置套接字的参数并发起与指定主机和端口的连接操作。
void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
OpenMode openMode,
NetworkLayerProtocol protocol)
{
Q_D(QAbstractSocket);
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::connectToHost(\"%s\", %i, %i)...", qPrintable(hostName), port,
(int) openMode);
#endif
if (d->state == ConnectedState || d->state == ConnectingState
|| d->state == ClosingState || d->state == HostLookupState) {
qWarning("QAbstractSocket::connectToHost() called when already looking up or connecting/connected to \"%s\"", qPrintable(hostName));
d->setErrorAndEmit(OperationError, tr("Trying to connect while connection is in progress"));
return;
}
d->preferredNetworkLayerProtocol = protocol;
d->hostName = hostName;
d->port = port;
d->setReadChannelCount(0);
d->setWriteChannelCount(0);
d->abortCalled = false;
d->pendingClose = false;
if (d->state != BoundState) {
d->state = UnconnectedState;
d->localPort = 0;
d->localAddress.clear();
}
d->peerPort = 0;
d->peerAddress.clear();
d->peerName = hostName;
if (d->hostLookupId != -1) {
QHostInfo::abortHostLookup(d->hostLookupId);
d->hostLookupId = -1;
}
#ifndef QT_NO_NETWORKPROXY
// Get the proxy information
d->resolveProxy(hostName, port);
if (d->proxyInUse.type() == QNetworkProxy::DefaultProxy) {
// failed to setup the proxy
d->setErrorAndEmit(UnsupportedSocketOperationError,
tr("Operation on socket is not supported"));
return;
}
#endif
// Sync up with error string, which open() shall clear.
d->socketError = UnknownSocketError;
if (openMode & QIODevice::Unbuffered)
d->isBuffered = false;
else if (!d_func()->isBuffered)
openMode |= QAbstractSocket::Unbuffered;
QIODevice::open(openMode);
d->readChannelCount = d->writeChannelCount = 0;
#ifndef Q_OS_WINRT
d->state = HostLookupState;
emit stateChanged(d->state);
QHostAddress temp;
if (temp.setAddress(hostName)) {
QHostInfo info;
info.setAddresses(QList<QHostAddress>() << temp);
d->_q_startConnecting(info);
#ifndef QT_NO_NETWORKPROXY
} else if (d->proxyInUse.capabilities() & QNetworkProxy::HostNameLookupCapability) {
// the proxy supports connection by name, so use it
d->startConnectingByName(hostName);
return;
#endif
} else {
if (d->threadData.loadRelaxed()->hasEventDispatcher()) {
// this internal API for QHostInfo either immediately gives us the desired
// QHostInfo from cache or later calls the _q_startConnecting slot.
bool immediateResultValid = false;
QHostInfo hostInfo = qt_qhostinfo_lookup(hostName,
this,
SLOT(_q_startConnecting(QHostInfo)),
&immediateResultValid,
&d->hostLookupId);
if (immediateResultValid) {
d->hostLookupId = -1;
d->_q_startConnecting(hostInfo);
}
}
}
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::connectToHost(\"%s\", %i) == %s%s", hostName.toLatin1().constData(), port,
(d->state == ConnectedState) ? "true" : "false",
(d->state == ConnectingState || d->state == HostLookupState)
? " (connection in progress)" : "");
#endif
#else // !Q_OS_WINRT
// On WinRT we should always connect by name. Lookup and proxy handling are done by the API.
d->startConnectingByName(hostName);
#endif
}
函数的主要功能如下:
- 通过d->state检查当前套接字的状态,如果处于连接状态、连接中状态、关闭状态或主机查找状态,则输出警告信息并返回。
- 设置首选的网络层协议、主机名和端口,并重置读写通道的计数。
- 如果当前套接字的状态不是绑定状态(BoundState),将其设置为未连接状态,并清空本地地址和端口。
- 清空对等方地址和端口,并将对等方名称(peerName)设置为主机名(hostName)。
- 如果存在主机查找操作,中止它(abortHostLookup)。
- 如果启用了网络代理,解析代理信息d->resolveProxy(hostName, port)并检查代理设置是否成功。如果代理设置失败,设置错误信息并返回。
- 同步套接字错误状态(d->socketError)。
- 根据打开模式打开套接字。如果使用了
QIODevice::Unbuffered模式,禁用缓冲。如果套接字本身未启用缓冲且未指定QIODevice::Unbuffered模式,则将其添加到打开模式中。 - 重置读写通道计数。(d->readChannelCount = d->writeChannelCount = 0;)
- 根据操作系统类型,设置套接字状态为主机查找状态,并发出
stateChanged信号。如果主机名可以解析为IP地址,则创建QHostInfo对象,并调用_q_startConnecting槽函数开始连接。如果代理支持按名称连接,则使用代理进行连接。否则,如果具有事件分发器,使用qt_qhostinfo_lookup函数进行主机查找,如果立即返回结果,则直接调用_q_startConnecting槽函数。 - 输出调试信息。
2. writeData
根据套接字的状态和类型,将数据写入套接字的引擎或缓冲区,并处理相应的错误状态和通知机制。负责处理套接字数据的写入操作。
/*! \reimp
*/
qint64 QAbstractSocket::writeData(const char *data, qint64 size)
{
Q_D(QAbstractSocket);
if (d->state == QAbstractSocket::UnconnectedState
|| (!d->socketEngine && d->socketType != TcpSocket && !d->isBuffered)) {
d->setError(UnknownSocketError, tr("Socket is not connected"));
return -1;
}
if (!d->isBuffered && d->socketType == TcpSocket
&& d->socketEngine && d->writeBuffer.isEmpty()) {
// This code is for the new Unbuffered QTcpSocket use case
qint64 written = size ? d->socketEngine->write(data, size) : Q_INT64_C(0);
if (written < 0) {
d->setError(d->socketEngine->error(), d->socketEngine->errorString());
} else if (written < size) {
// Buffer what was not written yet
d->writeBuffer.append(data + written, size - written);
written = size;
d->socketEngine->setWriteNotificationEnabled(true);
}
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == %lli", data,
qt_prettyDebug(data, qMin((int)size, 32), size).data(),
size, written);
#endif
return written; // written = actually written + what has been buffered
} else if (!d->isBuffered && d->socketType != TcpSocket) {
// This is for a QUdpSocket that was connect()ed
qint64 written = d->socketEngine->write(data, size);
if (written < 0)
d->setError(d->socketEngine->error(), d->socketEngine->errorString());
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == %lli", data,
qt_prettyDebug(data, qMin((int)size, 32), size).data(),
size, written);
#endif
if (written >= 0)
d->emitBytesWritten(written);
return written;
}
// This is the code path for normal buffered QTcpSocket or
// unbuffered QTcpSocket when there was already something in the
// write buffer and therefore we could not do a direct engine write.
// We just write to our write buffer and enable the write notifier
// The write notifier then flush()es the buffer.
d->writeBuffer.append(data, size);
qint64 written = size;
if (d->socketEngine && !d->writeBuffer.isEmpty())
d->socketEngine->setWriteNotificationEnabled(true);
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == %lli", data,
qt_prettyDebug(data, qMin((int)size, 32), size).data(),
size, written);
#endif
return written;
}
-
代码开始的
Q_D(QAbstractSocket)用于获取 QAbstractSocket 的私有实例指针,以便访问该类的私有成员。 -
首先,代码检查套接字的状态。如果套接字的状态是未连接状态(UnconnectedState),或者套接字没有有效的 socketEngine(套接字引擎),且套接字类型不是 TcpSocket 且没有缓冲,那么设置套接字的错误状态为 "Socket is not connected",并返回 -1 表示写入失败。
-
接下来,代码检查套接字的类型和缓冲状态。如果套接字不是缓冲的且是 TcpSocket 类型,且具有有效的 socketEngine,并且写缓冲区为空,则执行特定于新的未缓冲(Unbuffered)QTcpSocket 的代码。在这种情况下,调用 socketEngine 的 write() 函数将数据直接写入套接字引擎,返回实际写入的字节数。如果写入的字节数小于数据的大小,则将未写入的部分存储在 writeBuffer 中,并启用写通知以便后续写入。
-
如果套接字不是缓冲的且类型不是 TcpSocket,那么执行相应于已连接的 QUdpSocket 的代码。在这种情况下,调用 socketEngine 的 write() 函数将数据直接写入套接字引擎,返回实际写入的字节数。如果写入失败,则设置错误状态。如果写入成功,则通过 emitBytesWritten() 函数发送写入的字节数。
-
如果是普通的带缓冲的 QTcpSocket 或者是已经有数据存在于写缓冲区中的未缓冲 QTcpSocket,将数据追加到 writeBuffer 中,并启用套接字引擎的写通知以便后续刷新缓冲区。
-
最后,函数返回写入的字节数。
3.readData
该函数用于从套接字中读取数据。
qint64 QAbstractSocket::readData(char *data, qint64 maxSize)
{
Q_D(QAbstractSocket);
// if we're not connected, return -1 indicating EOF
if (!d->socketEngine || !d->socketEngine->isValid() || d->state != QAbstractSocket::ConnectedState)
return maxSize ? qint64(-1) : qint64(0);
qint64 readBytes = (maxSize && !d->isBuffered) ? d->socketEngine->read(data, maxSize)
: qint64(0);
if (readBytes == -2) {
// -2 from the engine means no bytes available (EAGAIN) so read more later
readBytes = 0;
}
if (readBytes < 0) {
d->setError(d->socketEngine->error(), d->socketEngine->errorString());
d->resetSocketLayer();
d->state = QAbstractSocket::UnconnectedState;
} else {
// Only do this when there was no error
d->hasPendingData = false;
d->socketEngine->setReadNotificationEnabled(true);
}
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld [engine]",
data, qt_prettyDebug(data, 32, readBytes).data(), maxSize,
readBytes);
#endif
return readBytes;
}
-
代码开始的
Q_D(QAbstractSocket)用于获取 QAbstractSocket 的私有实例指针,以便访问该类的私有成员。 -
首先,代码检查套接字的状态。如果套接字引擎无效(socketEngine 为空或不是有效的套接字引擎)或套接字状态不是已连接状态(ConnectedState),则根据 maxSize 的值返回适当的结果。如果 maxSize 为零,则返回 0 表示没有可读取的数据;否则返回 -1 表示已到达文件末尾。
-
如果 maxSize 不为零且套接字未缓冲(isBuffered 为 false),则调用 socketEngine 的 read() 函数从套接字引擎中读取数据,并将读取的字节数赋值给 readBytes。否则,将 readBytes 设置为 0。
-
如果 readBytes 的值为 -2,表示套接字引擎中没有可用的字节(EAGAIN),因此将 readBytes 设置为 0。
-
如果 readBytes 小于 0,表示发生了读取错误,此时设置套接字的错误状态为套接字引擎的错误状态,并重置套接字的底层状态,并将套接字的状态设置为未连接状态(UnconnectedState)。
-
如果读取操作没有发生错误,则将 hasPendingData 设置为 false,表示没有待处理的数据,并启用套接字引擎的读通知。
-
最后,函数返回读取的字节数。
4.disconnectFromHost
该函数用于断开与当前主机的连接。
void QAbstractSocket::disconnectFromHost()
{
Q_D(QAbstractSocket);
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::disconnectFromHost()");
#endif
if (d->state == UnconnectedState) {
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::disconnectFromHost() was called on an unconnected socket");
#endif
return;
}
if (!d->abortCalled && (d->state == ConnectingState || d->state == HostLookupState)) {
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::disconnectFromHost() but we're still connecting");
#endif
d->pendingClose = true;
return;
}
// Disable and delete read notification
if (d->socketEngine)
d->socketEngine->setReadNotificationEnabled(false);
if (d->abortCalled) {
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::disconnectFromHost() aborting immediately");
#endif
if (d->state == HostLookupState) {
QHostInfo::abortHostLookup(d->hostLookupId);
d->hostLookupId = -1;
}
} else {
// Perhaps emit closing()
if (d->state != ClosingState) {
d->state = ClosingState;
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::disconnectFromHost() emits stateChanged()(ClosingState)");
#endif
emit stateChanged(d->state);
} else {
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::disconnectFromHost() return from delayed close");
#endif
}
// Wait for pending data to be written.
if (d->socketEngine && d->socketEngine->isValid() && (!d->allWriteBuffersEmpty()
|| d->socketEngine->bytesToWrite() > 0)) {
d->socketEngine->setWriteNotificationEnabled(true);
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::disconnectFromHost() delaying disconnect");
#endif
return;
} else {
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::disconnectFromHost() disconnecting immediately");
#endif
}
}
SocketState previousState = d->state;
d->resetSocketLayer();
d->state = UnconnectedState;
emit stateChanged(d->state);
emit readChannelFinished(); // we got an EOF
// only emit disconnected if we were connected before
if (previousState == ConnectedState || previousState == ClosingState)
emit disconnected();
d->localPort = 0;
d->peerPort = 0;
d->localAddress.clear();
d->peerAddress.clear();
d->peerName.clear();
d->setWriteChannelCount(0);
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::disconnectFromHost() disconnected!");
#endif
}
-
函数开始时,进行一些用于调试的输出。
-
检查套接字的当前状态。如果状态是UnconnectedState,表示套接字未连接到任何主机,函数直接返回。
-
如果套接字正在连接中(ConnectingState或HostLookupState),并且abortCalled标志未设置,则将pendingClose标志设置为true,表示需要延迟断开连接直到连接过程完成。然后函数返回。
-
如果套接字引擎可用,禁用读通知(setReadNotificationEnabled(false);)。这一步确保在断开连接后不会再接收到读通知。
-
如果abortCalled标志被设置,表示套接字将立即中止。如果套接字处于HostLookupState状态,使用QHostInfo::abortHostLookup()函数中止主机查找过程。然后继续下一步。
-
如果套接字不处于ClosingState状态,将状态设置为ClosingState,并发出stateChanged()信号。这一步表示套接字正在关闭的过程中。
-
接下来,检查是否有待写入的数据。如果写缓冲区中有数据待写入或还有待写的字节数,启用写通知并返回。这个延迟操作确保在断开连接之前将待写入的数据写入完成。
-
如果没有待写入的数据,立即进行断开连接的过程。
-
存储当前套接字的状态,并重置套接字层。然后将状态设置为UnconnectedState,并发出stateChanged()信号。
-
发出readChannelFinished()信号,表示没有更多数据可读取。
-
如果先前的状态是ConnectedState或ClosingState,发出disconnected()信号,表示套接字已断开连接。
-
重置与套接字的本地和远程地址、端口以及写通道计数等相关的成员变量。
-
最后,输出一条调试信息,表示断开连接过程完成。
5.error
返回套接字的错误状态。
QAbstractSocket::SocketError QAbstractSocket::error() const
{
return d_func()->socketError;
}

QAbstractSocket类提供了网络通信的基础接口,包括connectToHost(), writeData(), readData()和disconnectFromHost()等关键函数。它定义了连接状态如ConnectedState, UnconnectedState等,并使用信号和槽机制处理网络事件。该类作为QTcpSocket和QUdpSocket的基类,用于处理网络数据传输和错误管理。connectToHost()会检查套接字状态并启动连接流程,writeData()和readData()分别负责数据写入和读取,而disconnectFromHost()则用于断开连接。"
8175395,674828,深入理解MyBatis <dynamic> 标签,"['MyBatis框架', 'SQL动态生成', '数据库交互']
5998

被折叠的 条评论
为什么被折叠?



