Proposal for the new socket and name-resolution routines for KDE 4 Note that it might be still introduced in KDE 3 if we don't clash with names with the existing classes. And those might be changed to use the new structure. The new classes will be located in the KDE::Net namespace, which means we avoid clashing with the existing classes. -> implementation notes and questions are marked with a preceding -> Note for Qt =========== This proposal can also be used to enhance Qt if some of the functionality here proposed is instead added there instead of KDE. This doesn't mean there won't be KDE socket classes. Instead, it would be helpful if most of the infrastructure were implemented inside Qt, especially the base types. The KDE classes would, then, be wrap-arounds against those classes, implementing needed functionality, like SOCKS support. That is, Qt would implement all the proposed classes here, without the KDE-specific functionality. KDE would then derive from those classes to provide its own functionality. Introduction ============ Name resolution --------------- We're completely dumping Qt's name-resolution routines and address representation. It's far from acceptable. QHostAddress doesn't allow us to represent the data satisfactorily (for instance, Unix sockets cannot be represented), while QDns won't honour /etc/nsswitch.conf. (This later problem (about nsswitch.conf) is known to TrollTech.) Thus, we're going to use KSocketAddress and derived classes to represent socket addresses. Those classes might be still improved upon if they are integrated with the proposed classes for simple addresses (no ports or other information), available in krfb. Classes and routines for obtaining addresses from interfaces have been proposed, as well as notification for those changes. However, since a connection requires more information than a socket address (namely the protocol and the socket type, since the family is represented in a socket address), we introduce a new class to contain that information. In the current structure, this class is the simple KAddressInfo class. I have been thinking of changing that name to KResolverEntry or something like that. The proposed KResolver class should be able to handle any kind of name-resolution task, be it synchronous or asynchronous. It might require a background worker class or even a singleton class to handle all requests. The idea is to allow several lower level resolution methods while still keeping the same public API: we would then support normal, synchronous name resolution; asynchronous name resolution; SRV-based name resolution, etc. * Note on asynchronous resolution: for the asynchronous resolution, it is here proposed to use threads and make normal getaddrinfo(3) calls. In fact, a synchronous resolution would be nothing more than an asynchronous one run on the same thread as the caller. We should also limit the number of concurrent resolving threads -- and if possible allow a program to change those values. Sockets ------- For the socket part, Qt already has a lot of useful functionality in the low-level QSocketDevice and in the buffering done by QSocket. However, these classes lack some functionality we want added and they use the name-resolution API we want to dump. One idea is to create derived classes from them and override the functions not wanted. Another is to completely dump those classes and work on our own framework. From my point of view, the ideal thing would be to have a generic abstract base class, one low-level (much like QSocketDevice) class built on top of it and specialised classes that work on different kinds of sockets. For the specialised socket classes, the following specialisations (and sub-specialisations) have been proposed: @ client (active) socket * streaming socket - buffered streaming socket * datagram socket - multicast datagram socket @ server (passive) socket Thus the class tree would be: QIODevice [abstract] \- KAsyncIO [abstract] \- KBaseSocket [abstract] +- KSocketDevice +- KClientSocket [abstract] | +- KStreamSocket | | \- KBufferedSocket | \- KDatagramSocket | \- KMulticastSocket \- KServerSocket The abstract base class would contain methods common to all implementations (low-level and high-level alike), as well as pure virtual methods for what must be implemented in derived classes -- reading, writing, etc. (note: those are already inherited from QIODevice). KAsyncIO is in the diagram above, but its functionality should instead be in QIODevice: signals notifying of the possibility of reading and writing. Note for server sockets: an extra signal called readyAccept() should be added. Class description ================= KResolver: public QObject Resolves host/service combinations into binary (address, port, scope, etc.) forms. Can do it synchronous or asynchronously. This should completely replace KExtendedSocket's name resolution functions, both the ones used in objects and the static ones. * constructor and setAddress/setHost/setService functions much like the current KExtendedSocket * setFlags for getaddrinfo-like flags (AI_*) but don't use the system constants! * startSync and startAsync * signal resultsReady() * KResolverResults results() -> should we implement the socket type restrictions here, during resolution? I.e., tell the resolver if we want only Internet sockets, or Unix sockets as well, etc.? KResolverResults: public QList<> or QArray<> Keeps an array or list of results from a name resolution. Should be one of the QList, QArray, so that we can use iterators and etc. Currently, this is represented by a QValueList, as returned by the static member function KExtendedSocket::lookup. KResolverEntry or KAddressInfo The internal type carried by KResolverResults. Like the current KAddressInfo, one has to be able to access the internal data in KSocketAddress form. -> make them more easily copiable -> they don't derive from QObject -> deep copy KBaseSocket: public KAsyncIO [abstract] This is the base, abstract class from which all socket types will be derived. This class defines the socket functionality that should be present in any socket and that are not already inherited from QIODevice. Here follows a list of proposed methods for the class, but I wouldn't say the list is exhaustive. Only when writing the classes themselves will anyone know what is common to all of them, and can thus be bumped up in the hierarchy. The same is true for the methods in KClientSocket. * create(void): create the socket, but do nothing on it. This could be later hidden in derived classes -> pure virtual? -> should we have this method? * setSocketFamilies: set the allowed socket families, like Unix or Internet (IPv4 and v6). * socketDevice/setSocketDevice: sets the socket device, of type KSocketDevice* * state and error management: we'd use QIODevice's IO_* constants to describe what kind of error happened (read, write, accept, listen, connect, etc.), and a set of error codes describing what exactly the error. This second set should be source-compatible with QSocket's and wrap around getaddrinfo()'s EAI_* error codes. errno should be recorded too. * m_fd: file descriptor protected member * setBlockingMode, setAddressReusable * QSocketNotifiers KSocketDevice: public KBaseSocket Simple socket that doesn't do name resolution or buffering or anything (like QSocketDevice). While this is not derived from QSocketDevice, which would be desireable if multiple inheritance were permitted, we can "fake" that condition by providing cast operators to QSocketDevice*, and sending an internal derived class in that place. That class will also be the one actually doing the I/O operations, using Qt's already established implementation. We'd accomplish the "fake" inheritance by having: class ourOwnQSocketDevice: public QSocketDevice { operator KSocketDevice* (); } and in KSocketDevice: operator ourOwnQSocketDevice* (); This class (whose name should be something else, of course), would be the real brains behind the scenes, doing almost if not all the work. KSocketDevice would only redirect to it. If TrollTech adds this functionality to Qt, however, the point is moot: QSocketDevice will be the class we want. It will do only the low-level socket operations: * create (of a family, type and protocol) * bind(KSocketAddress/KResolverResult) * connect(KSocketAddress/KResolverResult) * listen(int backlog) * accept(KSocketDevice*&) * read, recv, recvfrom * write, send, sendto * retrieve addresses (local & peer) * select/poll -> bind, connect will override QSocketDevice's ugly QHostAddress functions. We shall provide KSocketAddress and KResolverResult variants. -> verify which QSocketDevice functions require overriding: listen accept (set)addressReusable bytesAvailable waitForMore KClientSocket: public KBaseSocket [abstract] Most of the socket functionality that wraps around the socket device and the resolution routines. No buffering or anything else is done. The idea is to wrap around KSocketDevice and name-resolution and add a bit of functionality for client sockets (i.e., reading and writing), that is common to all kinds of sockets, datagram and streaming alike. Even though no buffering is to be done, asynchronous name-lookup and connection are to be available. * setRemoteAddress(QString host, QString service) and KExtendedSocket-like variants * setRemoteAddress(KResolverResult/KSocketAddress) * setLocalAddress(QString host, QString service) and variants * setLocalAddress(KResolverResult/KSocketAddress) * read, recv, recvfrom * peek * write, send, sendto * other QSocket- and KExtendedSocket-like operations, like bytesAvailable, etc. * canRead, canWrite * signal hostFound(), error(int) * signal readyRead(), readyWrite(), connectionClosed() * public members bytesSent, bytesReceived -> KServerSockets also have setLocalAddress. Should that be moved up in the hierarchy to KBaseSocket? -> bytesWritten/bytesReceived is proposed public so that the user may, if he wants, reset it to any value. Who knows what kind of accounting he will want? -> the I/O operations here are just calls wrapping around the internal KSocketDevice object KStreamSocket: public KClientSocket This is an unbuffered streaming socket. That is, it implements SOCK_STREAM kind of sockets, which are the most commonly used ones. The difference from QSocket is that this class does not do buffering. * open, create, connect -> recvfrom, sendto are probably to be hidden. A connected socket cannot send to or receive from other addresses than that to which it is connected. KBufferedSocket: public KStreamSocket This should be a fully-buffered socket and work very much like KSocket, but better ;-) We'd inherit all the KStreamSocket functionality and also provide buffering. Also to be noted: for changing a socket from buffered to non-buffered, one would create a new class and set the socket device. Also note that this class should be source-compatible with QSocket, except for what we want to dump (QHostAddress and IPv4-isms). * signal bytesWritten(int count) * signal delayedCloseFinished() * override close and provide closeNow() -> modify KBufferedIO so that it can be used to buffer (i.e., make it a KBuffer class -- we'd use one for input and the other for the output buffer) KDatagramSocket: public KClientSocket This is a datagram socket. That is, it's a normal client socket, but of SOCK_DGRAM type. It should also implement connectionless functionality. * open, create * connect/disconnect Note: you can connect more than once when using a datagram socket and you can also disconnect by connecting to a certain address. KMulticastSocket: public KDatagramSocket This is also a datagram socket, but it's by definition connectionless (so we can hide the connect routines), and we should implement some multicast functionality: * joining, leaving groups * setting scope (TTL in IPv4) Note that multicast sockets are tricky. For one thing, multicast isn't even mandatory in IPv4. And in IPv6, it's already very different. This KMulticastSocket class should be regarded as a low-priority one and left to be implemented only if we are certain of what we're doing. If the IPv4 and v6 differences are too great, splitting this class in two might be needed. I have no idea how multicasting works on non-Internet protocols. KServerSocket: public KBaseSocket This is a passive socket, which will listen and accept socket connections. We should be able to set some socket identifications, like datagrams or even buffering. That way, one could use this class to get also several kinds of specialised sockets. QServerSocket works on the assumption that one will derive from the class and implement one function to handle it. That is not always desired, and for that it is proposed that KServerSocket not be source-compatible with QServerSocket. * setLocalAddress(QString host, QString service) * setLocalAddress(KResolverResult/KSocketAddress) * listen(int backlog) * accept(KClientSocket*&): returns a KStreamSocket or a KDatagramSocket depending on whether we've been set to stream or datagram. Multicast sockets can't accept. * signal readyAccept()