网络包net Cwinux中net包封装了网络层与传输层的TCP,UDP,Unix-Domain的协议。包括各自的网络地址对象、网络连接对象、网络连接建立对象、网络连接accept对象。net依赖cmn。
net包的代码相比cmn和app简单。
CwxIpcSap对文件名描述符的封装 在Linux环境网络编程中,建立的网络连接通过文件描述符fd来读写。在Cwinux中net包内,Cwinux对fd进行了封装。其中CwxIpcSap就是初步的封装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 class CWX_API CwxIpcSap { public: ///enable或disable asynchronous。返回值,0成功;-1:失败。 int setSigio(bool enable=true) const; ///enable或disable non-blocking I/O。返回值,0成功;-1:失败。 int setNonblock(bool enable=true) const; ///enable或disable close-on-exec。返回值,0成功;-1:失败。 int setCloexec (bool enable=true) const; ///enable或disable sigurg。返回值,0成功;-1:失败。 int setSigurg (bool enable=true) const; ///是否设置了sigio, -1:失败,0:没有,1:设置 int isSigio() const; ///是否设置了nonblock, -1:失败,0:没有,1:设置 int isNonBlock() const; ///是否设置了Cloexec, -1:失败,0:没有,1:设置 int isCloexec() const; ///是否设置了Sigurg, -1:失败,0:没有,1:设置 int isSigurg() const; /// Get the underlying handle. CWX_HANDLE getHandle (void) const; /// Set the underlying handle. void setHandle (CWX_HANDLE handle); public: ///通过fcntl设置F_SETFL状态,返回值,0成功;-1:失败。 static int setFlags (CWX_HANDLE handle, int flags); ///通过fcntl清空F_SETFL状态,返回值,0成功;-1:失败。 static int clrFlags (CWX_HANDLE handle, int flags); ///判断是否设置了flag,-1:失败,0:没有;1:设置 static int isFlags(CWX_HANDLE handle, int flags); ///enable或disable asynchronous。返回值,0成功;-1:失败。 static int setSigio(CWX_HANDLE handle, bool enable=true); ///enable或disable non-blocking I/O。返回值,0成功;-1:失败。 static int setNonblock(CWX_HANDLE handle, bool enable=true); ///enable或disable close-on-exec。返回值,0成功;-1:失败。 static int setCloexec (CWX_HANDLE handle, bool enable=true); ///enable或disable sigurg。返回值,0成功;-1:失败。 static int setSigurg (CWX_HANDLE handle, bool enable=true); ///是否设置了sigio, -1:失败,0:没有,1:设置 int isSigio(CWX_HANDLE handle) const; ///是否设置了nonblock, -1:失败,0:没有,1:设置 int isNonBlock(CWX_HANDLE handle) const; ///是否设置了Cloexec, -1:失败,0:没有,1:设置 int isCloexec(CWX_HANDLE handle) const; ///是否设置了Sigurg, -1:失败,0:没有,1:设置 int isSigurg(CWX_HANDLE handle) const; protected: // = Ensure that CwxIpcSap is an abstract base class. /// Default constructor. CwxIpcSap (void); /// Protected destructor. /** * Not a virtual destructor. Protected destructor to prevent * operator delete() from being called through a base class * CwxIpcSap pointer/reference. */ ~CwxIpcSap (void); private: /// Underlying I/O handle. CWX_HANDLE handle_; };
其中包含的成员变量 handle_ 就是文件描述符。该类的成员函数主要是用于属性获取和设置。
CwxSock* CwxSockBase继承自CwxIpcSap,封装了socket handle相关的方法。主要是open和close两个函数。open用于打开一个socket,它的实现即调用系统的socket(…)函数,然后进行必要的设置(包括属性设置,将socket函数返回的文件描述符赋值给handle_ 成员变量)。close函数用于关闭socket,它的实现即调用系统的close(…)函数关闭socket,并将handle_ 设为无效。
CwxSockIo继承自CwxSockBase,该类封装了操作文件描述符fd的系统函数read/recv/send/write等。
CwxSockStream继承自CwxSockIo,该类表示一个TCP连接流。类似地,CwxSockDgram继承自CwxSockIo,该类表示一个UDP连接流。
CwxSockConnector建立主动连接 CwxSockConnector类用于发起一个主动的网络连接。其中connect函数就是用于建立连接的函数,其代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 /** @brief 建立TCP主动连接。 @param [out] stream 主动连接。 @param [in] remoteAddr 连接的远程地址。 @param [in] localAddr 连接的本地地址,若为空,则有connect自己分配。 @param [in] timeout 连接的超时时间,若NULL,表示没有超时限制。若值为0,则表示若没有连接上,立即返回。 @param [in] protocol 连接的协议,就是socket()的protocol参数,由于一个协议家族的socket 类型,其protocol也是唯一的,为0就可以了。 @param [in] reuse_addr 是否重用本地地址。 @param [in] fn socket属性设置的function。 @return -1:错误,errno记录错误的原因;0:成功。 */ int CwxSockConnector::connect(CwxSockStream& stream, CwxAddr const& remoteAddr, CwxAddr const& localAddr, CwxTimeouter* timeout, int protocol, bool reuse_addr, CWX_NET_SOCKET_ATTR_FUNC fn, void* fnArg) { if ((stream.getHandle() == CWX_INVALID_HANDLE) && (stream.open(remoteAddr.getType(), SOCK_STREAM, protocol, reuse_addr) == -1)) { return -1; } if (localAddr != CwxAddr::sap_any) { sockaddr *laddr = reinterpret_cast<sockaddr *>(localAddr.getAddr()); int size = localAddr.getSize(); if (::bind(stream.getHandle(), laddr, size) == -1) { CwxErrGuard guard; stream.close(); return -1; } } // Enable non-blocking, if required. if ((timeout != 0) && (stream.setNonblock(true) == -1)) { CwxErrGuard guard; stream.close(); return -1; } if (fn) { if (0 != fn(stream.getHandle(), fnArg)) { CwxErrGuard guard; stream.close(); return -1; } } int result = ::connect(stream.getHandle(), reinterpret_cast<sockaddr *>(remoteAddr.getAddr()), remoteAddr.getSize()); if (result == -1 && timeout != 0) { // Check whether the connection is in progress. if (errno == EINPROGRESS || errno == EWOULDBLOCK) { // This expression checks if we were polling. if (*timeout->getTimeout() == CwxTimeValue::ZERO) { errno = EWOULDBLOCK; } // Wait synchronously using timeout. else if (this->complete(stream, 0, timeout) == -1) { } else { return 0; } } } // EISCONN is treated specially since this routine may be used to // check if we are already connected. if (result != -1 || errno == EISCONN) { // Start out with non-blocking disabled on the new_stream. result = stream.setNonblock(false); if (result == -1) { CwxErrGuard guard; stream.close(); } } else if (!(errno == EWOULDBLOCK || errno == ETIMEDOUT)) { CwxErrGuard guard; stream.close(); } return result; }
简单地说,connect封装了系统的connect函数。
首先,确保stream已经打开了一个socket用于建立连接。参数localAddr用于使用户可以指定一个地址,用这个地址去建立主动连接。如果没指定,则跳过这段代码。如果指定了,要将上一步stream中的socket与指定的地址进行绑定。参数中如果指定了建立连接的超时时间,则stream设置非阻塞。如果参数指定了设置属性的函数,则调用该函数。
接下来,调用系统的connect函数,建立主动连接。
如果建立连接失败,且参数指定了超时时间,则调用complete函数。complete函数用于设置连接完成后的连接标志。其实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 /** @brief 设置连接完成后的连接标志。 @param [out] stream 主动连接。 @param [in] remoteAddr 连接的远程地址,若不为空则获取远端地址。 @param [in] timeout 连接的超时时间,若NULL,表示没有超时限制。若值为0,则表示若没有连接上,立即返回。 @return -1:错误,errno记录错误的原因;0:成功。 */ int CwxSockConnector::complete(CwxSockStream &stream, CwxAddr *remoteAddr, CwxTimeouter* timeout) { if (timeout && timeout->isTimer()) { if (CwxSocket::handleReady(stream.getHandle(), timeout, false, true, fale, true) < 1) { CwxErrGuard guard; stream.close(); return -1; } } int sock_err = 0; socklen_t sock_err_len = sizeof(sock_err); int sockopt_ret = ::getsockopt(stream.getHandle(), SOL_SOCKET, SO_ERROR, &sock_err, &sock_err_len); if ((sockopt_ret < 0) || sock_err) { stream.close(); errno = sock_err; return -1; } if (remoteAddr != 0) { socklen_t len = remoteAddr->getSize(); sockaddr *addr = reinterpret_cast<sockaddr *>(remoteAddr->getAddr()); if (::getpeername(stream.getHandle(), addr, &len) == -1) { // Save/restore errno. CwxErrGuard guard; stream.close(); return -1; } } stream.setNonblock(false); return 0; }
connect函数首先调用CwxSocket::handleReady来先查询一下该文件描述符是否有可读/可写/例外事件,正常的连接应该会返回一个正整数(表示该连接至少可以写入数据)。然后connect函数开始读取该连接上的设置并peername(对方的地址和端口)。
CwxSockAcceptor建立监听 CwxSockAcceptor类用于建立监听并接受连接,因此该类主要提供两个函数listen和accept。
listen函数用于建立监听。
1 2 3 4 5 6 7 8 9 10 11 /** @brief 建立TCP主动连接。 @param [in] addr Accept的本地地址。 @param [in] reuse 当监听地址没被释放的话,是否re-use。true:是;false:不是。 @param [in] backlog listen最大排队的数量。 @param [in] domain 协议族,就是socket()中的domain,PF_UNSPEC表示采用addr中的协议族。 @param [in] protocol 连接的协议,就是socket()的protocol参数,由于一个协议家族的socket 类型,其protocol也是唯一的,为0就可以了。 @param [in] fn socket 设置的function。 @return -1:错误,errno记录错误的原因;0:成功。 */ int listen(CwxAddr const& addr, bool reuse= 0, int backlog = DEF_BACK_LOG, int domain = PF_INET, int protocol = 0, CWX_NET_SOCKET_ATTR_FUNC fn=NULL, void* fnArg=NULL);
其实现大致为:打开socket,调用bind函数进行地址绑定,然后调用系统的listen函数建立监听。
accept函数用于接受连接。
1 2 3 4 5 6 7 8 9 /** @brief 接收一个被动TCP连接。 @param [in] stream 返回接收到的被动连接。 @param [in] remote_addr 若不为空,则返回接收到的对端host的地址。 @param [in] timeout accept的超时时间,若为NULL,则无限等待。 @param [in] bRestart 若在等待过程中被信号中断,是否继续等待。true:继续等待;false:不等待。 @return -1:错误,errno记录错误的原因;0:成功。 */ int accept (CwxSockStream &stream, CwxAddr* remote_addr = 0, CwxTimeouter* timeout = NULL, bool bRestart = true) const;
其实现为调用系统的accept函数,然后设置stream参数,stream参数返回给调用者。
综上,net包主要是对Linux下网络编程的基本元素,如网络地址,连接等进行了封装。