📄 qunixsocket.cpp
字号:
is flushed. Caller retains ownership of the iovec structs. */QUnixSocketMessage::QUnixSocketMessage(const ::iovec* data, int vecLen ): d(new QUnixSocketMessagePrivate()){ for ( int v = 0; v < vecLen; v++ ) d->dataSize += data[v].iov_len; d->vec = const_cast<iovec*>(data); d->iovecLen = vecLen;}/*! Assign the contents of \a other to this object. */QUnixSocketMessage & QUnixSocketMessage::operator=(const QUnixSocketMessage & other){ d = other.d; return *this;}/*! Destroy this instance. */QUnixSocketMessage::~QUnixSocketMessage(){}/*! Set the data portion of the message to \a bytes. \sa QUnixSocketMessage::bytes() */void QUnixSocketMessage::setBytes(const QByteArray & bytes){ d.detach(); d->bytes = bytes;}/*! Set the rights portion of the message to \a rights. A message with rights data but an empty byte data payload cannot be transmitted by the system. \sa QUnixSocketMessage::rights() */void QUnixSocketMessage::setRights(const QList<QUnixSocketRights> & rights){ d.detach(); d->rights = rights;}/*! Return the rights portion of the message. \sa QUnixSocketMessage::setRights() */const QList<QUnixSocketRights> & QUnixSocketMessage::rights() const{ return d->rights;}/*! Returns true if the rights portion of the message was truncated on reception due to insufficient buffer size. The rights buffer size can be adjusted through calls to the \l QUnixSocket::setRightsBufferSize() method. \l QUnixSocket contains a discussion of the buffering and truncation characteristics of the Unix domain protocol. \sa QUnixSocket QUnixSocket::setRightsBufferSize() */bool QUnixSocketMessage::rightsWereTruncated() const{ return d->state & QUnixSocketMessagePrivate::Truncated;}/*! Return the data portion of the message. \sa QUnixSocketMessage::setBytes() */const QByteArray & QUnixSocketMessage::bytes() const{ return d->bytes;}/*! Returns the process id credential associated with this message. \sa QUnixSocketMessage::setProcessId() */pid_t QUnixSocketMessage::processId() const{ if(QUnixSocketMessagePrivate::Credential & d->state) return d->pid; else return ::getpid();}/*! Returns the user id credential associated with this message. \sa QUnixSocketMessage::setUserId() */uid_t QUnixSocketMessage::userId() const{ if(QUnixSocketMessagePrivate::Credential & d->state) return d->uid; else return ::geteuid();}/*! Returns the group id credential associated with this message. \sa QUnixSocketMessage::setGroupId() */gid_t QUnixSocketMessage::groupId() const{ if(QUnixSocketMessagePrivate::Credential & d->state) return d->gid; else return ::getegid();}/*! Set the process id credential associated with this message to \a pid. Unless you are the root user, setting a fraudulant credential will cause this message to fail. \sa QUnixSocketMessage::processId() */void QUnixSocketMessage::setProcessId(pid_t pid){ if(!(d->state & QUnixSocketMessagePrivate::Credential)) { d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential ); d->uid = ::geteuid(); d->gid = ::getegid(); } d->pid = pid;}/*! Set the user id credential associated with this message to \a uid. Unless you are the root user, setting a fraudulant credential will cause this message to fail. \sa QUnixSocketMessage::userId() */void QUnixSocketMessage::setUserId(uid_t uid){ if(!(d->state & QUnixSocketMessagePrivate::Credential)) { d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential ); d->pid = ::getpid(); d->gid = ::getegid(); } d->uid = uid;}/*! Set the group id credential associated with this message to \a gid. Unless you are the root user, setting a fraudulant credential will cause this message to fail. \sa QUnixSocketMessage::groupId() */void QUnixSocketMessage::setGroupId(gid_t gid){ if(!(d->state & QUnixSocketMessagePrivate::Credential)) { d->state = (QUnixSocketMessagePrivate::AncillaryDataState)( d->state | QUnixSocketMessagePrivate::Credential ); d->pid = ::getpid(); d->uid = ::geteuid(); } d->gid = gid;}/*! Return true if this message is valid. A message with rights data but an empty byte data payload cannot be transmitted by the system and is marked as invalid. */bool QUnixSocketMessage::isValid() const{ return d->rights.isEmpty() || !d->bytes.isEmpty();}///////////////////////////////////////////////////////////////////////////////// class QUnixSocket///////////////////////////////////////////////////////////////////////////////#define QUNIXSOCKET_DEFAULT_READBUFFER 1024#define QUNIXSOCKET_DEFAULT_ANCILLARYBUFFER 0/*! \class QUnixSocket \internal \brief The QUnixSocket class provides a Unix domain socket. \omit \ingroup Platform::DeviceSpecific \ingroup Platform::OS \ingroup Platform::Communications \endomit \ingroup qws Unix domain sockets provide an efficient mechanism for communications between Unix processes on the same machine. Unix domain sockets support a reliable, stream-oriented, connection-oriented transport protocol, much like TCP sockets. Unlike IP based sockets, the connection endpoint of a Unix domain socket is a file on disk of type socket. In addition to transporting raw data bytes, Unix domain sockets are able to transmit special ancillary data. The two types of ancillary data supported by the QUnixSocket class are: \list \o Credential Data - Allows a receiver to reliably identify the process sending each message. \o \l {QUnixSocketRights}{Rights Data } - Allows Unix file descriptors to be transmitted between processes. \endlist Because of the need to support ancillary data, QUnixSocket is not a QIODevice, like QTcpSocket and QUdpSocket. Instead, QUnixSocket contains a number of read and write methods that clients must invoke directly. Rather than returning raw data bytes, \l QUnixSocket::read() returns \l QUnixSocketMessage instances that encapsulate the message's byte data and any other ancillary data. Ancillary data is transmitted "out of band". Every \l QUnixSocketMessage received will have credential data associated with it that the client can access through calls to \l QUnixSocketMessage::processId(), \l QUnixSocketMessage::groupId() and \l QUnixSocketMessage::userId(). Likewise, message creators can set the credential data to send through calls to \l QUnixSocketMessage::setProcessId(), \l QUnixSocketMessage::setGroupId() and \l QUnixSocketMessage::setUserId() respectively. The authenticity of the credential values is verified by the system kernel and cannot be fabricated by unprivileged processes. Only processes running as the root user can specify credential data that does not match the sending process. Unix file descriptors, known as "rights data", transmitted between processes appear as though they had been dup(2)'d between the two. As Unix domain sockets present a continuous stream of bytes to the receiver, the rights data - which is transmitted out of band - must be "slotted" in at some point. The rights data is logically associated with the first byte - called the anchor byte - of the \l QUnixSocketMessage to which they are attached. Received rights data will be available from the \l QUnixSocketMessage::rights() method for the \l QUnixSocketMessage instance that contains the anchor byte. In addition to a \l QUnixSocket::write() that takes a \l QUnixSocketMessage instance - allowing a client to transmit both byte and rights data - a number of convenience overloads are provided for use when only transmitting simple byte data. Unix requires that at least one byte of raw data be transmitted in order to send rights data. A \l QUnixSocketMessage instance with rights data, but no byte data, cannot be transmitted. Unix sockets present a stream interface, such that, for example, a single six byte transmission might be received as two three byte messages. Rights data, on the other hand, is conceptually transmitted as unfragmentable datagrams. If the receiving buffer is not large enough to contain all the transmitted rights information, the data is truncated and irretreivably lost. Users should use the \l QUnixSocket::setRightsBufferSize() method to control the buffer size used for this data, and develop protocols that avoid the problem. If the buffer size is too small and rights data is truncated, the \l QUnixSocketMessage::rightsWereTruncated() flag will be set. \sa QUnixSocketMessage QUnixSocketRights*//*! \enum QUnixSocket::SocketError The SocketError enumeration represents the various errors that can occur on a Unix domain socket. The most recent error for the socket is available through the \l QUnixSocket::error() method. \value NoError No error has occurred. \value InvalidPath An invalid path endpoint was passed to \l QUnixSocket::connect(). As defined by unix(7), invalid paths include an empty path, or what more than 107 characters long. \value ResourceError An error acquiring or manipulating the system's socket resources occurred. For example, if the process runs out of available socket descriptors, a ResourceError will occur. \value NonexistentPath The endpoing passed to \l QUnixSocket::connect() does not refer to a Unix domain socket entity on disk. \value ConnectionRefused The connection to the specified endpoint was refused. Generally this means that there is no server listening on that endpoint. \value UnknownError An unknown error has occurred. \value ReadFailure An error occurred while reading bytes from the connection. \value WriteFailure An error occurred while writing bytes into the connection. *//*! \enum QUnixSocket::SocketState The SocketState enumeration represents the connection state of a QUnixSocket instance. \value UnconnectedState The connection is not established. \value ConnectedState The connection is established. \value ClosingState The connection is being closed, following a call to \l QUnixSocket::close(). While closing, any pending data will be transmitted, but further writes by the application will be refused. *//* \fn QUnixSocket::bytesWritten(qint64 bytes) This signal is emitted every time a payload of data has been written to the connection. The \a bytes argument is set to the number of bytes that were written in this payload. \sa QUnixSocket::readyRead()*//* \fn QUnixSocket::readyRead() This signal is emitted once every time new data is available for reading from the connection. It will only be emitted again once new data is available. \sa QUnixSocket::bytesWritten()*//*! \fn QUnixSocket::stateChanged(SocketState socketState) This signal is emitted each time the socket changes connection state. \a socketState will be set to the socket's new state.*/class QUnixSocketPrivate : public QObject {Q_OBJECTpublic: QUnixSocketPrivate(QUnixSocket * _me) : me(_me), fd(-1), readNotifier(0), writeNotifier(0), state(QUnixSocket::UnconnectedState), error(QUnixSocket::NoError), writeQueueBytes(0), messageValid(false), dataBuffer(0), dataBufferLength(0), dataBufferCapacity(0), ancillaryBuffer(0), ancillaryBufferCount(0), closingTimer(0) { QObject::connect(this, SIGNAL(readyRead()), me, SIGNAL(readyRead())); QObject::connect(this, SIGNAL(bytesWritten(qint64)), me, SIGNAL(bytesWritten(qint64))); } ~QUnixSocketPrivate() { if(dataBuffer) delete [] dataBuffer; if(ancillaryBuffer) delete [] ancillaryBuffer; } enum { CausedAbort = 0x70000000 }; QUnixSocket * me; int fd; QSocketNotifier * readNotifier; QSocketNotifier * writeNotifier; QUnixSocket::SocketState state; QUnixSocket::SocketError error; QQueue<QUnixSocketMessage> writeQueue; unsigned int writeQueueBytes; bool messageValid; ::msghdr message; inline void flushAncillary() { if(!messageValid) return; ::cmsghdr * h = (::cmsghdr *)CMSG_FIRSTHDR(&(message)); while(h) { if(SCM_RIGHTS == h->cmsg_type) { int * fds = (int *)CMSG_DATA(h); int numFds = (h->cmsg_len - CMSG_LEN(0)) / sizeof(int); for(int ii = 0; ii < numFds; ++ii) ::close(fds[ii]); } h = (::cmsghdr *)CMSG_NXTHDR(&(message), h); } messageValid = false; } char * dataBuffer; unsigned int dataBufferLength; unsigned int dataBufferCapacity; char * ancillaryBuffer; inline unsigned int ancillaryBufferCapacity() { return CMSG_SPACE(sizeof(::ucred)) + CMSG_SPACE(sizeof(int) * ancillaryBufferCount); } unsigned int ancillaryBufferCount; QByteArray address; int closingTimer; virtual void timerEvent(QTimerEvent *) { me->abort(); killTimer(closingTimer); closingTimer = 0; }signals: void readyRead(); void bytesWritten(qint64);public slots: void readActivated(); void writeActivated();};/*! Construct a QUnixSocket instance, with \a parent. The read buffer is initially set to 1024 bytes, and the rights buffer to 0 entries. \sa QUnixSocket::readBufferSize() QUnixSocket::rightsBufferSize() */QUnixSocket::QUnixSocket(QObject * parent): QIODevice(parent), d(new QUnixSocketPrivate(this)){ setOpenMode(QIODevice::NotOpen); setReadBufferSize(QUNIXSOCKET_DEFAULT_READBUFFER); setRightsBufferSize(QUNIXSOCKET_DEFAULT_ANCILLARYBUFFER);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -