|
|
/* * This file is part of the KDE libraries * Copyright (C) 2000,2001 Thiago Macieira* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef KEXTSOCK_H #define KEXTSOCK_H #include #include #include #include #include #include #include "kbufferedio.h" #include "ksockaddr.h" /* External reference to netdb.h */ struct addrinfo; struct kde_addrinfo; class KAddressInfo; /* our abstraction of it */ /* * This is extending QIODevice's error codes * * According to qiodevice.h, the last error is IO_UnspecifiedError * These errors will never occur in functions declared in QIODevice * (except open, but you shouldn't call open) */ #define IO_ListenError (IO_UnspecifiedError+1) #define IO_AcceptError (IO_UnspecifiedError+2) #define IO_LookupError (IO_UnspecifiedError+3) /** * The extended socket class. * * This class should be used instead of @ref KSocket whenever the user needs * fine-grained control over the socket being created. Unlike KSocket, which * does everything at once, without much intervention, KExtendedSocket allows * intervention at every step of the process and the setting of parameters. * * This class allows for the creation of both server and client sockets. The * only difference is that the passiveSocket flag must be passed either to * the constructor or to @ref setSocketFlags(). If passiveSocket is used, the class will * enable functions @ref listen() and @ref accept() and related signals, and will * also disable @ref readBlock() and @ref writeBlock(). * * To create a Unix socket, one would pass flag unixSocket to the constructor * or @ref setSocketFlags(). The hostname and service/port can be set to whatever is * necessary. If no hostname is given, but a service/port is, the socket created * will be implementation dependant (usually in /tmp). In any other case, the * fields will be concatenated. * * To create an Internet socket, inetSocket flag can be used. If, on the other * hand a specific IP protocol is desired, ipv4Socket and/or ipv6Socket can be * used. * * Note that the socket type selection flags are cumulative. One could select * Unix and Internet sockets by using unixSocket | inetSocket. Or, for instance, * to make sure only IPv4 and IPv6 sockets are selected, even if future implementations * support newer IP protocols, ipv4Socket | ipv6Socket is your guy. * * @author Thiago Macieira * @version $Id: kextsock_h.html 132191 2002-01-17 21:32:13Z dfaure $ * @short an extended socket */ class KExtendedSocket: public KBufferedIO // public QObject, public QIODevice { Q_OBJECT public: /** * flags that can be passed down to the member functions */ enum Flags { /* socket address families */ /* * NOTE: if you change this, you have to change function valid_socket() as well * These values are hard coded! */ anySocket = 0x00, knownSocket = 0x01, unixSocket = knownSocket | 0x02, inetSocket = knownSocket | 0x04, ipv4Socket = inetSocket | 0x100, ipv6Socket = inetSocket | 0x200, passiveSocket = 0x1000, /* passive socket (i.e., one that accepts connections) */ canonName = 0x2000, /* request that the canon name be found */ noResolve = 0x4000, /* do not attempt to resolve, treat as numeric host */ streamSocket = 0x8000, /* request a streaming socket (e.g., TCP) */ datagramSocket = 0x10000, /* request a datagram socket (e.g., UDP) */ rawSocket = 0x20000, /* request a raw socket. This probably requires privileges */ inputBufferedSocket = 0x200000, /* buffer input in this socket */ outputBufferedSocket = 0x400000, /* buffer output in this socket */ bufferedSocket = 0x600000 /* make this a fully buffered socket */ }; /** * status of the class * The status are sequential. If a change to one status is requested, * all the prior status will be passed and their actions, performed */ enum SockStatus { // the numbers are scattered so that we leave room for future expansion error = -1, // invalid status! nothing = 0, // no status, the class has just been created lookupInProgress = 4, // lookup is in progress. Signals will be sent lookupDone = 5, // lookup has been done. Flags cannot be changed // from this point on created = 10, // ::socket() has been called, a socket exists bound = 11, // socket has been bound connecting = 20, // socket is connecting (not passiveSocket) connected = 21, // socket has connected (not passiveSocket) listening = 20, // socket is listening (passiveSocket) accepting = 21, // socket is accepting (passiveSocket) closing = 35, // socket is closing (delayed close) done = 40 // socket has been closed }; public: /** * Creates an empty KExtendedSocket */ KExtendedSocket(); /** * Creates a socket with the given hostname and port * @param host the hostname * @param port the port number * @param flags flags */ KExtendedSocket(const QString& host, int port, int flags = 0); /** * Creates a socket with the given hostname and service * @param host the hostname * @param serv the service * @param flags flags */ KExtendedSocket(const QString& host, const QString& service, int flags = 0); /** * Destroys the socket, disconnecting if still connected and * freeing any related resources still being kept. */ virtual ~KExtendedSocket(); /* * --- status, flags and internal variables --- * */ /** * Returns the class status */ inline int socketStatus() const { return m_status; } /** * Returns the related system error code * Except for IO_LookupError errors, these are codes found in * errno */ inline int systemError() const { return m_syserror; } /** * Sets the given flags. Will return the new flags status, or * -1 if flags can no longer be set. * @param flags the flags to be set */ int setSocketFlags(int flags); /** * Returns the current flags */ inline int socketFlags() const { return m_flags; } /** * Sets the hostname to the given value * Returns true on success, false on error * @param host the hostname */ bool setHost(const QString& host); /** * Returns the hostname */ QString host() const; /** * Sets the port/service * @param port the port */ bool setPort(int port); bool setPort(const QString& service); /** * Returns the port/service */ QString port() const; /** * Sets the address where we will connect to * @param host the hostname * @param port port number */ bool setAddress(const QString& host, int port); /** * Sets the address where we will connect to * @param host the hostname * @param serv the service */ bool setAddress(const QString& host, const QString& serv); /** * Sets the hostname to which we will bind locally before connecting. * Returns false if this is a passiveSocket, otherwise true. * @param host the hostname */ bool setBindHost(const QString& host); /** * Unsets the bind hostname. That is, don't request a binding host. */ bool unsetBindHost(); /** * Returns the hostname to which the socket will be/is bound */ inline QString bindHost() const; /** * Sets the port/service to which we will bind before connecting * @param port the port number */ bool setBindPort(int port); bool setBindPort(const QString& service); /** * Unsets the bind port/service. */ bool unsetBindPort(); /** * Returns the service to which the socket will be/is bound. */ QString bindPort() const; /** * Sets both host and port to which we will bind the socket. Will return * -1 if this is a passiveSocket * @param host the hostname * @param port the port number */ bool setBindAddress(const QString& host, int port); /** * Sets both host and service to which we will bind the socket. Will return * -1 if this is a passiveSocket * @param host the hostname * @param serv the service */ bool setBindAddress(const QString& host, const QString& service); /** * Unsets the bind address for the socket. That means that we won't * attempt to bind to an address before connecting */ bool unsetBindAddress(); /** * Sets the timeout value for the connection, if this is not passiveSocket, or * acception, if this is a passiveSocket. In the event the given function * (connect or accept) returns due to time out, it's possible to call it again. * Setting the timeout to 0 disables the timeout feature. * Returns false if setting timeout makes no sense in the context. * @param secs the timeout length, in seconds * @param usecs the timeout complement, in microseconds */ bool setTimeout(int secs, int usecs = 0); /** * Returns the timeout value for the connection */ timeval timeout() const; /** * Sets/unsets blocking mode for the socket. When non-blocking mode is enabled, * I/O operations might return error and set errno to EWOULDBLOCK. Also, * it's not recommended to use this when using signals. Returns false on * error. * @param enable if true, set blocking mode. False, non-blocking mode. */ bool setBlockingMode(bool enable); /** * Returns the current blocking mode for this socket */ bool blockingMode(); /** * Sets/unsets address reusing flag for this socket. * This function returns true if the value was set correctly. That is NOT * the result of the set. * @param enable if true, set address reusable */ bool setAddressReusable(bool enable); /** * Returns whether this socket can be reused */ bool addressReusable(); /** * Sets the buffer sizes for this socket. * * This implementation allows any size for both parameters. The value given * will be interpreted as the maximum size allowed for the buffers, after * which the functions will stop buffering. The value of -1 will be * interpreted as "unlimited" size. * * Note: changing the buffer size to 0 for any buffer will cause the given * buffer's to be discarded. Likewise, setting the size to a value less than * the current size will cause the buffer to be shrunk to the wanted value, * as if the data had been read. * * Note 2: if we are not doing input buffering, the #ref closed signal will * not be emitted for remote connection closing. That happens because we aren't * reading from the connection, so we don't know when it closed. * @param rsize read buffer size * @param wsize write buffer size */ virtual bool setBufferSize(int rsize, int wsize = -2); /** * Returns the local socket address */ const KSocketAddress *localAddress(); /** * Returns the peer socket address. Use KExtendedSocket::resolve() to * resolve this to a human-readable hostname/service or port. */ const KSocketAddress *peerAddress(); /** * Returns the file descriptor */ inline int fd() const { return sockfd; } /* * -- socket creation -- * */ /** * Performs lookup on the addresses we were given before * Returns 0 or an error. Codes are the same as for getaddrinfo * This will perform lookups on the bind addresses if they were given */ virtual int lookup(); /** * Starts an asynchronous lookup for the addresses given * This function returns 0 on success or -1 on error. Note that * returning 0 means that either we are in the process of doing * lookup or that it has finished already. * When the lookup is done, the lookupReady signal will be emitted. * Note that, depending on the parameters for the lookup, this function might * know the results without the need for blocking or queueing an * asynchronous lookup. That means that the lookupReady signal might be * emitted by this function, so your code should be prepared for that. * One such case is when noResolve flag is set. * If this function were able to determine the results without queueing * and we found an error during lookup, this function will return -1. */ virtual int startAsyncLookup(); /** * Cancels any on-going asynchronous lookups */ virtual void cancelAsyncLookup(); /** * Place the socket in listen mode. The parameters are the same as for * the system listen() call. Returns 0 on success, -1 on system error (errno * available) and -2 if this is not a passiveSocket. * @param N the queue length for pending connections */ virtual int listen(int N = 5); // 5 is arbitrary /** * Accepts an incoming connection from the socket. If this socket is in * blocking mode, this function will block until a connection is received. * Otherwise, it might return with error. The sock parameter will be * initialised with the newly created socket. * Returns 0 on success, -1 on system error (errno set) and -2 if this is * not a passiveSocket and -3 if this took too long (time out) * @param sock a pointer to an KExtendedSocket variable */ virtual int accept(KExtendedSocket *&sock); /** * Attempts to connect to the remote host. The return values are: * 0: success * -1: system error, errno was set accordingly * -2: this socket cannot connect(); this is a passiveSocket. It can also * mean that the function was unable to make a connection with the given * bind address or that an asynchronous connection attempt is already * in progress. * -3: connection timed out */ virtual int connect(); /** * Starts an asynchronous connect. This works exactly the same as @ref connect, * except that the connection result won't be returned. This function will * return either 0 on successful queueing of the connect or -1 on error. If * this function returns 0, then the connectionSuccess or the connectionFailed * signals will be emitted. * Note that those signals might be emitted before this function returns, so your * code should be prepared for that condition. */ virtual int startAsyncConnect(); /** * Cancels any on-going asynchronous connection attempt. */ virtual void cancelAsyncConnect(); /** * Implementation of QIODevice's open() pure virtual function. * This depends on the target host address already being there. * If this is a passiveSocket, this is identical to call listen(); else, if * this is not a passiveSocket and no connection attempt is in progress, this * is like connect(). If one is in progress, this function will fail. * @param mode the open mode. Must be IO_Raw | IO_ReadWrite */ virtual bool open(int mode = IO_Raw | IO_ReadWrite); /** * Closes the socket. If we have data still in the write buffer yet to be * sent, the socket won't be closed right now. It'll be closed after we managed * to send everything out. * If you want to close the socket now, you may want to call @ref flush first, * and then @ref closeNow. */ virtual void close(); /** * Closes the socket now, discarding the contents of the write buffer, if any. * The read buffer's contents are kept until they are emptied by read operations * or the class is destroyed. */ virtual void closeNow(); /** * Releases the socket and anything we have holding on it. The class cannot * be used anymore. In other words, this is just like closeNow(), but it does * not actually close the socket. * This is useful if you just want to connect and don't need the rest of the * class. * Note that the buffers' contents will be discarded. And usage of this * method is discouraged, because the socket created might be such that * normal library routines can't handle (read, write, close, etc.) */ virtual void release(); /* * -- I/O -- */ /** * Flushes the socket buffer. You need not call this method during normal * operation as we will try and send everything as soon as possible. * However, if you want to make sure that data in the buffer is being sent * at this moment, you can call this function. It will try to send as much * data as possible, but it will stop as soon as the kernel cannot receive * any more data, and would possibly block. * By repeatedly calling this function, the behaviour will be like that of * a blocking socket. Indeed, if this function is called with the kernel not * ready to receive data, it will block, unless this is a non-blocking socket. * This function does not touch the read buffer. You can empty it by calling * @ref readBlock with a null destination buffer. */ virtual void flush(); /** * Returns length of this socket. This call is not supported on sockets */ virtual inline uint size() const { return 0; } /** * Returns relative position from start. This call is not supported on sockets */ virtual inline int at() const { return 0; } /** * Returns true if we are at position. This is not supported on sockets */ virtual inline bool at(int) { return true; } /** * Returns true if we are at the end. This is not supported on sockets, but * we always are at the end in a socket... */ virtual inline bool atEnd() const { return false; } /** * reads a block of data from the socket * * If the socket is not buffered, this function will simply call the underlying * read method. This function will block if the socket is not on non-blocking mode * (see @ref setBlockingMode) and there is not enough data to be read in the * Operating System yet. If we are in non-blocking operation, the call will * fail in this case. * However, if we are buffering, this function will instead read from the * buffer while there is available data. This function will never block * in buffering mode, which means that if you try to read while the buffers * are empty, this function will always return -1 and set the system error to * EWOULDBLOCK (aka EAGAIN), so as to mimic non-blocking operation. * * The return value for this function is the number of bytes effectively * read. If the @p data param is not null, then this is also the number * of bytes copied into that buffer. If the return value is different than * @p maxlen, then this function encountered a situation in which no more * bytes were available. Subsequent calls might cause this function to one * of these behaviours: * - block, if we are not buffering and we are not in non-blocking mode * - return an error, with EWOULDBLOCK system error, if we buffering * or we are in non-blocking mode * This function returns 0, if the function detected end-of-file condition * (socket was closed) * * @param data where we will write the read data to * @param maxlen maximum length of data to be read */ virtual int readBlock(char *data, uint maxlen); /** * writes a block of data to the socket * * If the socket is not buffered, this function will simply call the underlying * write method. This means that the function might block if that method blocks * as well. That situation is possible if we are not in non-blocking mode and * the operating system buffers are full for this socket. If we are in * non-blocking mode and the operating system buffers are full, this function * will return -1 and the system error will be set to EWOULDBLOCK. * If we are buffering, this function will simply transfer the data into the * write buffer. This function will always succeed, as long as there is * enough room in the buffer. If the buffer size was limited and that limit * is reached, this function will copy no more bytes than that limit. Trying * to write with a full buffer will return -1 and set system error to * EWOULDBLOCK. * * The function returns the number of bytes written from @p data buffer. * The return value might be less than @p len if the output buffers cannot * accomodate that many bytes. * * @param data the data to write * @param len the length of data to write */ virtual int writeBlock(const char *data, uint len); /** * peeks at a block of data from the socket * This is exactly like read, except that the data won't be flushed from the * read buffer. * If this socket is not buffered, this function will always return with * 0 bytes copied. * The return value of 0 does not mean end-of-file condition. * @param data where to store the data * @param maxlen how many bytes to copy, at most */ virtual int peekBlock(char *data, uint maxlen); /** * reimplementation of unreadBlock method. This is so because unreading in * sockets doesn't make sense, so this function will always return -1 (error) * and set the system error to ENOSYS. */ virtual int unreadBlock(const char *data, uint len); /** * Waits @p msec milliseconds for more data to be available, or 0 to * wait forever. The return value is the amount of data available for * read in the read buffer. * This function returns -1 in case of system error and -2 in case of * invalid socket state * @param msec milliseconds to wait */ virtual int waitForMore(int msec); /** * gets a single character from the stream */ virtual int getch(); /** * writes a single character to the stream * @param ch character to write, converted to char */ virtual int putch(int ch); /** * unreads one character from the stream. This is not possible on sockets */ virtual int ungetch(int) { return -1; } /** * Toggles the emission of the readyRead signal * Note that this signal is emitted every time more data is available to be * read, so you might get flooded with it being emitted every time, when in * non-buffered mode. However, in buffered mode, this signal will be * emitted only when there is data coming in from the wire. * By default, this flag is set to false, i.e., signal not being emitted. * @param enable if true, the signal will be emitted */ virtual void enableRead(bool enable); /** * Toggles the emission of the readyWrite signal * Note that this signal is emitted only when the OS is ready to receive more * data, which means that the write buffer is empty. And when that is reached, * this signal will possibly be emitted on every loop, so you might * want to disable it. By default, this flag is set to false. * @param enable if true, the signal will be emitted */ virtual void enableWrite(bool enable); signals: /** * This signal is emitted whenever an asynchronous lookup process is done. * The parameter @p count tells how many results were found. */ void lookupFinished(int count); /** * This signal is emitted whenever we connected asynchronously to a host. */ void connectionSuccess(); /** * This signal is emitted whenever our asynchronous connection attempt * failed to all hosts listed. * @param error the errno code of the last connection attempt */ void connectionFailed(int error); protected: int m_flags; // current flags int m_status; // status int m_syserror; // the system error code int sockfd; // file descriptor of the socket protected slots: void socketActivityRead(); void socketActivityWrite(); void dnsResultsReady(); void startAsyncConnectSlot(); void connectionEvent(); private: class KExtendedSocketPrivate; KExtendedSocketPrivate *d; // protection against accidental use KExtendedSocket(KExtendedSocket&); KExtendedSocket& operator=(KExtendedSocket&); protected: /** * Sets the error code */ void setError(int errorkind, int error); inline void cleanError() { setError(IO_Ok, 0); } /** * This is actually a wrapper around getaddrinfo() * @internal */ static int doLookup(const QString& host, const QString& serv, addrinfo& hint, kde_addrinfo** result); public: /** * Performs resolution on the given socket address * That is, tries to resolve the raw form of the socket address into a textual * representation. * @param sockaddr the socket address * @param host where the hostname will be written * @param port where the service-port will be written * @param flags the same flags as getnameinfo() */ static int resolve(sockaddr* sock, ksocklen_t len, QString& host, QString& port, int flags = 0); static int resolve(KSocketAddress* sock, QString& host, QString& port, int flags = 0); /** * Performs lookup on the given hostname/port combination and returns a list * of matching addresses. * The error code can be transformed into string by @ref KExtendedSocket::strError * with code of IO_LookupError. * * IMPORTANT: the result values of the QList must be deleted after use. So, * if you don't copy the KAddressInfo objects, the best way to assure that * is to call setAutoDelete(true) on the list right after this function * returns. If you do copy the results out, you must assure that the objects * get deleted when they are not needed any more. * * @param host the hostname to look up * @param port the port/service to look up * @param flags flags to be used when looking up, @see Flags * @param error pointer to a variable holding the error code */ static QList lookup(const QString& host, const QString& port, int flags = 0, int *error = 0); /** * Returns the local socket address * Remember to delete the returned object when it is no longer needed. * @param fd the file descriptor */ static KSocketAddress *localAddress(int fd); /** * Returns the peer socket address. Use KExtendedSocket::resolve() to * resolve this to a human-readable hostname/service or port. * Remember to delete the returned object when it is no longer needed. * @param fd the file descriptor */ static KSocketAddress *peerAddress(int fd); /** * Returns the representing text of this error code * @param code the error code, as seen in status() * @param syserr the system error, as from systemError() */ static QString strError(int code, int syserr); }; class KAddressInfo { private: addrinfo *ai; KSocketAddress *addr; inline KAddressInfo() : ai(0), addr(0) { } KAddressInfo(addrinfo *ai); KAddressInfo(KAddressInfo&) { } KAddressInfo& operator=(KAddressInfo&) { return *this; } public: ~KAddressInfo(); inline operator const KSocketAddress*() const { return addr; } inline operator const addrinfo&() const { return *ai; } inline operator const addrinfo*() const { return ai; } inline const KSocketAddress* address() const { return addr; } int flags() const; int family() const; int socktype() const; int protocol() const; const char* canonname() const; inline int length() const { if (addr) return addr->size(); return 0; } friend class KExtendedSocket; }; #endif // KEXTSOCK_H
Generated by: dfaure on kde.faure.org on Thu Jan 17 22:15:01 2002, using kdoc 2.0a53. |