📄 qunixsocket.cpp
字号:
/*! Construct a QUnixSocket instance, with \a parent. The read buffer is initially set to \a readBufferSize bytes, and the rights buffer to \a rightsBufferSize entries. \sa QUnixSocket::readBufferSize() QUnixSocket::rightsBufferSize() */QUnixSocket::QUnixSocket(qint64 readBufferSize, qint64 rightsBufferSize, QObject * parent): QIODevice(parent), d(new QUnixSocketPrivate(this)){ Q_ASSERT(readBufferSize > 0 && rightsBufferSize >= 0); setOpenMode(QIODevice::NotOpen); setReadBufferSize(readBufferSize); setRightsBufferSize(rightsBufferSize);}/*! Destroys the QUnixSocket instance. Any unsent data is discarded. */QUnixSocket::~QUnixSocket(){ abort(); delete d;}/*! Attempt to connect to \a path. This method is synchronous and will return true if the connection succeeds and false otherwise. In the case of failure, \l QUnixSocket::error() will be set accordingly. Any existing connection will be aborted, and all pending data will be discarded. \sa QUnixSocket::close() QUnixSocket::abort() QUnixSocket::error() */bool QUnixSocket::connect(const QByteArray & path){ int _true; int crv;#ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Connect requested to '" << path << "'";#endif abort(); // Reset any existing connection if(UnconnectedState != d->state) // abort() caused a signal and someone messed // with us. We'll assume they know what // they're doing and bail. Alternative is to // have a special "Connecting" state return false; if(path.isEmpty() || path.size() > UNIX_PATH_MAX) { d->error = InvalidPath; return false; } // Create the socket d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0); if(-1 == d->fd) {#ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to create socket (" << strerror(errno) << ")";#endif d->error = ResourceError; goto connect_error; } // Set socket options _true = 1; crv = ::setsockopt(d->fd, SOL_SOCKET, SO_PASSCRED, (void *)&_true, sizeof(int)); if(-1 == crv) {#ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to configure socket (" << ::strerror(errno) << ")";#endif d->error = ResourceError; goto connect_error; } // Construct our unix address struct ::sockaddr_un addr; addr.sun_family = AF_UNIX; ::memcpy(addr.sun_path, path.data(), path.size()); if(path.size() < UNIX_PATH_MAX) addr.sun_path[path.size()] = '\0'; // Attempt the connect crv = ::connect(d->fd, (sockaddr *)&addr, sizeof(sockaddr_un)); if(-1 == crv) {#ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to connect (" << ::strerror(errno) << ")";#endif if(ECONNREFUSED == errno) d->error = ConnectionRefused; else if(ENOENT == errno) d->error = NonexistentPath; else d->error = UnknownError; goto connect_error; } // We're connected! d->address = path; d->state = ConnectedState; d->readNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d); d->writeNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Write, d); QObject::connect(d->readNotifier, SIGNAL(activated(int)), d, SLOT(readActivated())); QObject::connect(d->writeNotifier, SIGNAL(activated(int)), d, SLOT(writeActivated())); d->readNotifier->setEnabled(true); d->writeNotifier->setEnabled(false); setOpenMode(QIODevice::ReadWrite); emit stateChanged(ConnectedState);#ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Connected to " << path;#endif return true;connect_error: // Cleanup failed connection if(-1 != d->fd) {#ifdef QUNIXSOCKET_DEBUG int closerv =#endif ::close(d->fd);#ifdef QUNIXSOCKET_DEBUG if(0 != closerv) { qDebug() << "QUnixSocket: Unable to close file descriptor after " "failed connect (" << ::strerror(errno) << ")"; }#endif } d->fd = -1; return false;}/*! Sets the socket descriptor to use to \a socketDescriptor, bypassing QUnixSocket's connection infrastructure, and return true on success and false on failure. \a socketDescriptor must be in the connected state, and must be a Unix domain socket descriptor. Following a successful call to this method, the QUnixSocket instance will be in the Connected state and will have assumed ownership of \a socketDescriptor. Any existing connection will be aborted, and all pending data will be discarded. \sa QUnixSocket::connect()*/bool QUnixSocket::setSocketDescriptor(int socketDescriptor){ abort(); if(UnconnectedState != state()) // See QUnixSocket::connect() return false; // Attempt to set the socket options if(-1 == socketDescriptor) {#ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: User provided socket is invalid";#endif d->error = ResourceError; return false; } // Set socket options int _true = 1; int crv = ::setsockopt(socketDescriptor, SOL_SOCKET, SO_PASSCRED, (void *)&_true, sizeof(int)); if(-1 == crv) {#ifdef QUNIXSOCKET_DEBUG qDebug() << "QUnixSocket: Unable to configure client provided socket (" << ::strerror(errno) << ")";#endif d->error = ResourceError; return false; } d->fd = socketDescriptor; d->state = ConnectedState; d->address = QByteArray(); setOpenMode(QIODevice::ReadWrite); d->readNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, d); d->writeNotifier = new QSocketNotifier(d->fd, QSocketNotifier::Write, d); QObject::connect(d->readNotifier, SIGNAL(activated(int)), d, SLOT(readActivated())); QObject::connect(d->writeNotifier, SIGNAL(activated(int)), d, SLOT(writeActivated())); d->readNotifier->setEnabled(true); d->writeNotifier->setEnabled(false); emit stateChanged(d->state); return true;}/*! Returns the socket descriptor currently in use. This method will return -1 if the QUnixSocket instance is in the UnconnectedState \l {QUnixSocket::state()}{state. } \sa QUnixSocket::setSocketDescriptor() */int QUnixSocket::socketDescriptor() const{ return d->fd;}/*! Abort the connection. This will immediately disconnect (if connected) and discard any pending data. Following a call to QUnixSocket::abort() the object will always be in the disconnected \link QUnixSocket::state() state. \endlink \sa QUnixSocket::close()*/void QUnixSocket::abort(){ setOpenMode(QIODevice::NotOpen); // We want to be able to use QUnixSocket::abort() to cleanup our state but // also preserve the error message that caused the abort. It is not // possible to reorder code to do this: // abort(); // d->error = SomeError // as QUnixSocket::abort() might emit a signal and we need the error to be // set within that signal. So, if we want an error message to be preserved // across a *single* call to abort(), we set the // QUnixSocketPrivate::CausedAbort flag in the error. if(d->error & QUnixSocketPrivate::CausedAbort) d->error = (QUnixSocket::SocketError)(d->error & ~QUnixSocketPrivate::CausedAbort); else d->error = NoError; if( UnconnectedState == d->state) return;#ifdef QUNIXSOCKET_DEBUG int closerv =#endif ::close(d->fd);#ifdef QUNIXSOCKET_DEBUG if(0 != closerv) { qDebug() << "QUnixSocket: Unable to close socket during abort (" << strerror(errno) << ")"; }#endif // Reset variables d->fd = -1; d->state = UnconnectedState; d->dataBufferLength = 0; d->flushAncillary(); d->address = QByteArray(); if(d->readNotifier) { d->readNotifier->setEnabled(false); delete d->readNotifier; } if(d->writeNotifier) { d->writeNotifier->setEnabled(false); delete d->writeNotifier; } d->readNotifier = 0; d->writeNotifier = 0; d->writeQueue.clear(); d->writeQueueBytes = 0; if(d->closingTimer) { d->killTimer(d->closingTimer); } d->closingTimer = 0; emit stateChanged(d->state);}/*! Close the connection. The instance will enter the Closing \l {QUnixSocket::state()}{state } until all pending data has been transmitted, at which point it will enter the Unconnected state. Even if there is no pending data for transmission, the object will never jump directly to Disconnect without first passing through the Closing state. \sa QUnixSocket::abort() */void QUnixSocket::close(){ if(ConnectedState != state()) return; d->state = ClosingState; if(d->writeQueue.isEmpty()) { d->closingTimer = d->startTimer(0); // Start a timer to "fake" // completing writes } emit stateChanged(d->state);}/*! Writes all unsent data to the socket, and blocks until transmission has completed. \b {Warning:} Calling this function from your main (GUI) thread may cause your user interface to freeze. */void QUnixSocket::flush(){ while(!d->writeQueue.isEmpty()) d->writeActivated();}/*! Returns the last error to have occurred on this object. This method is not destructive, so multiple calls to QUnixSocket::error() will return the same value. The error is only reset by a call to \l QUnixSocket::connect() or \l QUnixSocket::abort() */QUnixSocket::SocketError QUnixSocket::error() const{ return (QUnixSocket::SocketError) (d->error & ~QUnixSocketPrivate::CausedAbort);}/*! Returns the connection state of this instance. */QUnixSocket::SocketState QUnixSocket::state() const{ return d->state;}/*! Returns the Unix path address passed to \l QUnixSocket::connect(). This method will return an empty path if the object is in the Unconnected \l {QUnixSocket::state()}{state } or was connected through a call to \l QUnixSocket::setSocketDescriptor() \sa QUnixSocket::connect() QUnixSocket::setSocketDescriptor() */QByteArray QUnixSocket::address() const{ return d->address;}/*! Returns the number of bytes available for immediate retrieval through a call to \l QUnixSocket::read(). */qint64 QUnixSocket::bytesAvailable() const{ return QIODevice::bytesAvailable() + d->dataBufferLength;}/*! Returns the number of enqueued bytes still to be written to the socket. */qint64 QUnixSocket::bytesToWrite() const{ return d->writeQueueBytes;}/*! Returns the size of the read buffer in bytes. The read buffer size determines the amount of byte data that can be read from the socket in one go. The read buffer size caps the maximum value that can be returned by \l QUnixSocket::bytesAvailable() and will always be greater than zero. By default, the read buffer size is 1024 bytes. The size of the read buffer is independent of the rights buffer, which can be queried by \l QUnixSocket::rightsBufferSize(). \sa QUnixSocket::setReadBufferSize() */qint64 QUnixSocket::readBufferSize() const{ return d->dataBufferCapacity;}/*! Sets the \a size of the socket's read buffer in bytes. The size of the read buffer is independent of the rights buffer, which can be set by \l QUnixSocket::setRightsBufferSize(). Attempting to reduce the buffer size while bytes are available for reading (ie. while the buffer is in use) will fail. \sa QUnixSocket::readBufferSize() */void QUnixSocket::setReadBufferSize(qint64 size){ Q_ASSERT(size > 0); if(size == d->dataBufferCapacity || d->dataBufferLength) return; if(d->dataBuffer) delete [] d->dataBuffer; d->dataBuffer = new char[size]; d->dataBufferCapacity = size;}/*! Returns the size of the rights buffer in rights entries. The rights buffer size determines the number of rights transferences that can be received in any message. Unlike byte stream data which can be fragmented into many smaller messages if the \link QUnixSocket::readBufferSize() read buffer \endlink is not large enough to contain all the available data, rights data is transmitted as unfragmentable datagrams. If the rights buffer is not large enough to contain this unfragmentable datagram, the datagram will be truncated and rights data irretrievably lost. If truncation occurs, the \l QUnixSocketMessage::rightsWereTruncated() flag will be set. By default the rights buffer size is 0 entries - rights data cannot be received. The size of the rights buffer is independent of the read buffer, which can be queried by \l QUnixSocket::readBufferSize(). \sa QUnixSocket::setRightsBufferSize() */qint64 QUnixSocket::rightsBufferSize() const{ return d->ancillaryBufferCount;}/*! Sets the \a size of the socket's rights buffer in rights entries. The size of the rights buffer is independent of the read buffer, which can be set by \l QUnixSocket::setReadBufferSize(). Attempting to reduce the buffer size while bytes are available for reading (ie. while the buffer is in use) will fail.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -