网络包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: int setSigio (bool enable=true ) const ; int setNonblock (bool enable=true ) const ; int setCloexec (bool enable=true ) const ; int setSigurg (bool enable=true ) const ; int isSigio () const ; int isNonBlock () const ; int isCloexec () const ; int isSigurg () const ; CWX_HANDLE getHandle (void ) const ; void setHandle (CWX_HANDLE handle) ; public: static int setFlags (CWX_HANDLE handle, int flags) ; static int clrFlags (CWX_HANDLE handle, int flags) ; static int isFlags (CWX_HANDLE handle, int flags) ; static int setSigio (CWX_HANDLE handle, bool enable=true ) ; static int setNonblock (CWX_HANDLE handle, bool enable=true ) ; static int setCloexec (CWX_HANDLE handle, bool enable=true ) ; static int setSigurg (CWX_HANDLE handle, bool enable=true ) ; int isSigio (CWX_HANDLE handle) const ; int isNonBlock (CWX_HANDLE handle) const ; int isCloexec (CWX_HANDLE handle) const ; int isSigurg (CWX_HANDLE handle) const ; protected: CwxIpcSap (void ); ~CwxIpcSap (void ); private : 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 /** 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 ; } } 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 ) { if (errno == EINPROGRESS || errno == EWOULDBLOCK) { if (*timeout->getTimeout() == CwxTimeValue::ZERO) { errno = EWOULDBLOCK; } else if (this ->complete(stream, 0 , timeout) == -1 ) { } else { return 0 ; } } } if (result != -1 || errno == EISCONN) { 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 /** 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 ) { 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 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 /** int accept (CwxSockStream &stream, CwxAddr* remote_addr = 0 , CwxTimeouter* timeout = NULL, bool bRestart = true ) const ;
其实现为调用系统的accept函数,然后设置stream参数,stream参数返回给调用者。
综上,net包主要是对Linux下网络编程的基本元素,如网络地址,连接等进行了封装。