对于网络的调用的包装一直都是做为一个工程的支撑,使用ACE固然是不错的选择,但是对于一个小工程来说未免有些喧宾夺主的味道。ACE中使用面向对象的技术去避免很多接口的误用,却造成了整个工程的规模变得很庞大难用。下面给出一个简单的实现。
SocketImp.h
#ifndef _SOCKETIMP_H_ #define _SOCKETIMP_H_ #include <string> typedef int SOCKET; class SocketImp { public: SOCKET sock; SocketImp(); ~SocketImp(); bool isBound(); bool close(); bool setReuseAddress(bool on); void setTimeout(int timeout); static const int STREAM; static const int DGRAM; void setSocket(SOCKET value) { sock = value; } SOCKET getSocket() { return sock; } void setLocalAddress(const char *addr) { localAddr = (addr != NULL) ? addr : ""; //StripIPv6ScopeID(addr, localAddr); } const char *getLocalAddress() { return localAddr.c_str(); } void setLocalPort(int port) { localPort = port; } int getLocalPort() { return localPort; } int getType() { return type; } protected: void setType(int value) { type = value; } private: int type; std::string localAddr; int localPort; }; void SocketStartup(); void SocketCleanup(); #endif
SocketImp.cpp
#include <net/SocketImp.h> #include <net/SocketUtil.h> #include <net/HostInterface.h> #include <util/Mutex.h> #include <util/StringUtil.h> #include <fcntl.h> #include <signal.h> //////////////////////////////////////////////// // Constants //////////////////////////////////////////////// const int SocketImp::STREAM = 1; const int SocketImp::DGRAM = 2; //////////////////////////////////////////////// // SocketInit/Close //////////////////////////////////////////////// static int socketCnt = 0; static Mutex sockMutex; void CyberNet::SocketStartup() { sockMutex.lock(); if (socketCnt == 0) { signal(SIGPIPE, SIG_IGN); } socketCnt++; sockMutex.unlock(); } void CyberNet::SocketCleanup() { sockMutex.lock(); socketCnt--; if (socketCnt <= 0) { signal(SIGPIPE, SIG_DFL); } sockMutex.unlock(); } //////////////////////////////////////////////// // Constructor/Destructor //////////////////////////////////////////////// SocketImp::SocketImp() { SocketStartup(); setType(0); setLocalAddress(""); setLocalPort(0); setSocket(-1); } SocketImp::~SocketImp() { SocketCleanup(); } bool SocketImp::isBound() { return (0 <= sock) ? true : false; } //////////////////////////////////////////////// // close //////////////////////////////////////////////// bool SocketImp::close() { if (isBound() == true) return true; int flag = fcntl(sock, F_GETFL, 0); if (0 <= flag) fcntl(sock, F_SETFL, flag | O_NONBLOCK); shutdown(sock, 2); ::close(sock); setSocket(-1); return true; } //////////////////////////////////////////////// // Socket Option //////////////////////////////////////////////// bool SocketImp::setReuseAddress(bool flag) { int sockOptRet; int optval = (flag == true) ? 1 : 0; sockOptRet = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)); setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char *)&optval, sizeof(optval)); return (sockOptRet == 0) ? true : false; } void SocketImp::setTimeout(int timeout) { setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout)); setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout)); }
以上是Socket的一个代理的基类,下面是流式的Socket
Socket.h
#ifndef _SOCKET_H_ #define _SOCKET_H_ #include <net/SocketImp.h> class Socket : public SocketImp { public: Socket(); ~Socket(); bool listen(); bool bind(int port, const char *addr); bool accept(Socket *socket); bool connect(const char *addr, int port); int send(const char *cmd, int cmdLen); int send(const char *cmd); int recv(char *buffer, int bufferLen); }; #endif
Socket.cpp
#include <net/Socket.h> #include <util/StringUtil.h> #include <util/TimeUtil.h> #include <net/SocketUtil.h> #include <stdio.h> //////////////////////////////////////////////// // Socket //////////////////////////////////////////////// Socket::Socket() { setType(STREAM); } Socket::~Socket() { close(); } //////////////////////////////////////////////// // listen //////////////////////////////////////////////// bool Socket::listen() { int ret = ::listen(sock, SOMAXCONN); return (ret == 0) ? true: false; } //////////////////////////////////////////////// // bind //////////////////////////////////////////////// bool Socket::bind(int bindPort, const char *bindAddr) { setLocalAddress(""); setLocalPort(0); if (bindPort <= 0 || bindAddr == NULL) return false; struct addrinfo *addrInfo; if (toSocketAddrInfo(SOCK_STREAM, bindAddr, bindPort, &addrInfo, true) == false) return false; sock = socket(addrInfo->ai_family, addrInfo->ai_socktype, 0); if (sock == -1) { close(); return false; } int ret = ::bind(sock, addrInfo->ai_addr, addrInfo->ai_addrlen); freeaddrinfo(addrInfo); if (ret != 0) return false; setLocalAddress(bindAddr); setLocalPort(bindPort); return true; } //////////////////////////////////////////////// // accept //////////////////////////////////////////////// bool Socket::accept(Socket *socket) { SOCKET clientSock; struct sockaddr_storage sockClientAddr; socklen_t nLength = sizeof(sockClientAddr); clientSock = ::accept(sock, (struct sockaddr *)&sockClientAddr, &nLength); if (clientSock < 0) return false; socket->setSocket(clientSock); socket->setLocalAddress(getLocalAddress()); socket->setLocalPort(getLocalPort()); return true; } //////////////////////////////////////////////// // connect //////////////////////////////////////////////// bool Socket::connect(const char *addr, int port) { struct addrinfo *toaddrInfo; if (toSocketAddrInfo(SOCK_STREAM, addr, port, &toaddrInfo, true) == false) return false; if (isBound() == false) sock = socket(toaddrInfo->ai_family, toaddrInfo->ai_socktype, 0); int ret = ::connect(sock, toaddrInfo->ai_addr, toaddrInfo->ai_addrlen); freeaddrinfo(toaddrInfo); return (ret == 0) ? true : false; } //////////////////////////////////////////////// // recv //////////////////////////////////////////////// int Socket::recv(char *buffer, int bufferLen) { int recvLen; recvLen = ::recv(sock, buffer, bufferLen, 0); return recvLen; } //////////////////////////////////////////////// // send //////////////////////////////////////////////// const int CG_NET_SOCKET_SEND_RETRY_CNT = 10; const int CG_NET_SOCKET_SEND_RETRY_WAIT_MSEC = 1000; int Socket::send(const char *cmd, int cmdLen) { if (cmdLen <= 0) return 0; int nTotalSent = 0; int cmdPos = 0; int retryCnt = 0; do { int nSent = ::send(sock, cmd + cmdPos, cmdLen, 0); if (nSent <= 0) { retryCnt++; if (CG_NET_SOCKET_SEND_RETRY_CNT < retryCnt) break; WaitRandom(CG_NET_SOCKET_SEND_RETRY_WAIT_MSEC); continue; } nTotalSent += nSent; cmdPos += nSent; cmdLen -= nSent; retryCnt = 0; } while (0 < cmdLen); return nTotalSent; } int Socket::send(const char *cmd) { return send(cmd, StringLength(cmd)); }