C++ Socket封装

TC_Socket是一个socket的封装类,封装了socket的处理,该封装类的主要操作功能包括
  • 生成socket,根据指定的socket类型,调用socket系统调用,并进行异常处理,如果sock存在,则先关闭,打开新的描述符。
void  TC_Socket::createSocket(  int  iSocketType int  iDomain)
{
    assert (iSocketType == SOCK_STREAM || iSocketType == SOCK_DGRAM);
    close();

     _iDomain     = iDomain;
     _sock        = socket(iDomain, iSocketType , 0);

     if (  _sock  < 0)
    {
         _sock  =  INVALID_SOCKET ;
         throw   TC_Socket_Exception (  "[TC_Socket::createSocket] create socket error! :"  + string(strerror( errno)));
    }
}

  • 关闭sock;
void  TC_Socket::close()
{
     if  (  _sock  !=  INVALID_SOCKET )
    {
        :: close( _sock  );
         _sock  =  INVALID_SOCKET ;
    }
}

  • 获取对点的ip和端口,对AF_INET的socket有效,构建sockaddr,利用getPeerName从sock描述符获取IP以及端口地址,通过inet_ntop把数值型地址转换为字符串,通过ntohs把端口的网络顺序转换成为主机顺序,sPeerAddress= sAddr是一个复制拷贝。
void  TC_Socket:: getPeerName(string &sPeerAddress,  uint16_t  &iPeerPort)
{
    assert ( _iDomain  == AF_INET);

     struct   sockaddr  stPeer;
    bzero (&stPeer,  sizeof  (  struct   sockaddr ));
    socklen_t iPeerLen =  sizeof  ( sockaddr  );

    getPeerName (&stPeer, iPeerLen);

     char  sAddr[ INET_ADDRSTRLEN] =  "\0"  ;
     struct   sockaddr_in  *p = (  struct   sockaddr_in  *)&stPeer;

    inet_ntop ( _iDomain  , &p->sin_addr, sAddr,  sizeof (sAddr));

    sPeerAddress= sAddr;
    iPeerPort   = ntohs(p->sin_port);
}

  • 获取自己的ip和端口,对AF_INET的socket有效. 
void  TC_Socket:: getSockName(string &sSockAddress,  uint16_t  &iSockPort)
{
    assert ( _iDomain  == AF_INET);

     struct   sockaddr  stSock;
    bzero (&stSock,  sizeof  (  struct   sockaddr ));
    socklen_t iSockLen =  sizeof  ( sockaddr  );

    getSockName (&stSock, iSockLen);

     char  sAddr[ INET_ADDRSTRLEN] =  "\0"  ;
     struct   sockaddr_in  *p = (  struct   sockaddr_in  *)&stSock;

    inet_ntop ( _iDomain  , &p->sin_addr, sAddr,  sizeof (sAddr));

    sSockAddress = sAddr;
    iSockPort = ntohs(p->sin_port);
}

  • 修改,获取socket选项值. 
int  TC_Socket:: setSockOpt(  int  opt,  const   void  *pvOptVal, socklen_t optLen,  int  level)
{
     return  setsockopt( _sock  , level, opt, pvOptVal, optLen);
}

int  TC_Socket::getSockOpt(  int  opt,  void  *pvOptVal, socklen_t &optLen,  int  level)
{
     return  getsockopt( _sock  , level, opt, pvOptVal, &optLen);
}

  • accept服务端sock,accept是阻塞的操作,并是一个不可重入函数,会被信号处理等中断,此处需要重复操作,返回一个客户端TC_SOCK
int  TC_Socket:: accept( TC_Socket  &tcSock,  struct   sockaddr  *pstSockAddr, socklen_t &iSockLen)
{
    assert (tcSock. _sock  ==  INVALID_SOCKET );

     int  ifd;

     while  ((ifd = :: accept( _sock  , pstSockAddr, &iSockLen)) < 0 && errno == EINTR);

    tcSock.  _sock     = ifd;
    tcSock.  _iDomain  =  _iDomain ;

     return  tcSock.  _sock ;
}

  • bind操作,将sock绑定到指定的地址与端口
void  TC_Socket:: bind(  const   char  *sPathName)
{
    assert ( _iDomain  == AF_LOCAL);

    unlink (sPathName);

     struct   sockaddr_un  stBindAddr;
    bzero (&stBindAddr,  sizeof  (  struct   sockaddr_un ));
    stBindAddr. sun_family =  _iDomain  ;
    strncpy (stBindAddr.sun_path, sPathName,  sizeof  (stBindAddr.sun_path));

     try
    {
        bind((  struct   sockaddr  *)&stBindAddr,  sizeof  (stBindAddr));
    }
     catch (...)
    {
         throw   TC_Socket_Exception (  "[TC_Socket::bind] bind '"  + string(sPathName) +  "' error" errno);
    }
}

void  TC_Socket::bind(  struct   sockaddr  * pstBindAddrsocklen_t iAddrLen)
{
       //如果服务器终止后,服务器可以第二次快速启动而不用等待一段时间
       int  iReuseAddr = 1;

     //设置
    setSockOpt (SO_REUSEADDR, (  const   void  *)&iReuseAddr,  sizeof  ( int  ), SOL_SOCKET);

     if (:: bind( _sock  pstBindAddr, iAddrLen) < 0)
    {
         throw   TC_Socket_Exception (  "[TC_Socket::bind] bind error" errno );
    }
}

  • 连接其他服务,对AF_INET的socket有效(同步连接)
int  TC_Socket::connect(  struct   sockaddr  *pstServerAddr, socklen_t serverLen)
{
     return  :: connect( _sock  , pstServerAddr, serverLen);

}
  • 监听其他端口(同步连接),能收指定连接队列

void  TC_Socket::listen(  int  iConnBackLog)
{
     if  (:: listen( _sock  , iConnBackLog) < 0)
    {
         throw   TC_Socket_Exception (  "[TC_Socket::listen] listen error" errno );
    }
}

  • 发送,接收数据(一般用于tcp). 
int  TC_Socket::recv(  void  *pvBuf, size_t iLen,  int  iFlag)
{
     return  :: recv( _sock  , pvBuf, iLen, iFlag);
}

int  TC_Socket::send(  const   void  *pvBuf, size_t iLen,  int  iFlag)
{
     return  :: send( _sock  , pvBuf, iLen, iFlag);
}

  • 单向关闭连接
void  TC_Socket:: shutdown(  int  iHow)
{
     if  (:: shutdown( _sock  , iHow) < 0)
    {
         throw   TC_Socket_Exception (  "[TC_Socket::shutdown] shutdown error" errno );
    }
}


  • 设置是否阻塞,利用fcntl,先F_GETFL操作,加上或者消除bBlock后F_SETFL
void  TC_Socket:: setblock(  int  fd,  bool  bBlock)
{
     int  val = 0;

     if  ((val = fcntl(fd, F_GETFL, 0)) == -1)
    {
         throw   TC_Socket_Exception (  "[TC_Socket::setblockfcntl [F_GETFL] error" errno );
    }

     if (!bBlock)
    {
        val |= O_NONBLOCK;
    }
     else
    {
        val &= ~ O_NONBLOCK;
    }

     if  ( fcntl(fd, F_SETFL, val) == -1)
    {
         throw   TC_Socket_Exception (  "[TC_Socket::setblockfcntl [F_SETFL] error" errno );
    }
}

  • 静态工具方法,生成管道,并且将管道设置的读写设置为指定的阻塞设置。
void  TC_Socket:: createPipe(  int  fds[2],  bool  bBlock)
{
     if (:: pipe(fds) != 0)
    {
         throw   TC_Socket_Exception (  "[TC_Socket::createPipe] error" errno );
    }

     try
    {
        setblock(fds[0], bBlock);
        setblock(fds[1], bBlock);
    }
     catch (...)
    {
        :: close(fds[0]);
        :: close(fds[1]);
         throw ;
    }
}

  • 静态工具方法,获取所有本地地址,主要借助于ioctl函数,不断的递增分配存储空间,直到空间能够容纳所有的地址为止,表示取到了所有的地址;
vector< stringTC_Socket:: getLocalHosts()
{
    vector <stringresult;

     TC_Socket  ts;
    ts.createSocket (SOCK_STREAMAF_INET);

     int  cmd = SIOCGIFCONF;

     struct   ifconf  ifc;

     int  numaddrs = 10;

     int  old_ifc_len = 0;

     while (  true )
    {
         int  bufsize = numaddrs *  static_cast <  int >(  sizeof (  struct   ifreq ));
        ifc. ifc_len = bufsize;
        ifc. ifc_buf = (  char *)malloc (bufsize);
         int  rs = ioctl(ts.getfd(), cmd, &ifc);

         if (rs == -1)
        {
            free(ifc.ifc_buf );
             throw   TC_Socket_Exception  ( "[TC_Socket::getLocalHosts] ioctl error"  errno);
        }
         else   if (ifc. ifc_len == old_ifc_len)
        {
             break ;
        }
         else
        {
            old_ifc_len = ifc. ifc_len;
        }
   
        numaddrs += 10;
        free(ifc.ifc_buf);
    }

    numaddrs = ifc. ifc_len /  static_cast <  int >(  sizeof (  struct   ifreq ));
     struct   ifreq * ifr = ifc. ifc_req;
     for (  int  i = 0; i < numaddrs; ++i)
    {
         if (ifr[i]. ifr_addr.sa_family == AF_INET)
        {
             struct   sockaddr_in * addr =  reinterpret_cast <  struct   sockaddr_in *>(&ifr[i]. ifr_addr);
          if (addr-> sin_addr.s_addr != 0)
         {
                 char  sAddr[ INET_ADDRSTRLEN] =  "\0" ;
                inet_ntop(AF_INET , &(*addr).sin_addr, sAddr,  sizeof  (sAddr));
             result.push_back (sAddr);
         }
        }
    }

    free (ifc.ifc_buf);

     return  result;
}

  • 静态工具方法,解析地址, 从字符串(ip或域名), 解析到in_addr结构.
void  TC_Socket:: parseAddr(  const  string &sAddr,  struct  in_addr  &stSinAddr)
{
     int  iRet = inet_pton(AF_INET, sAddr. c_str(), &stSinAddr);
     if (iRet < 0)
    {
         throw   TC_Socket_Exception (  "[TC_Socket::parseAddr] inet_pton error" errno );
    }
     else   if (iRet == 0)
    {
         struct   hostent  stHostent;
         struct   hostent  *pstHostent;
         char  buf[2048] =  "\0" ;
         int  iError;

        gethostbyname_r(sAddr.c_str(), &stHostent, buf,  sizeof  (buf), &pstHostent, &iError);

         if  (pstHostent == NULL)
        {
             throw   TC_Socket_Exception (  "[TC_Socket::parseAddr] gethostbyname_r error! :"  + string(hstrerror(iError)));
        }
         else
        {
            stSinAddr = *(  struct   in_addr  *) pstHostent-> h_addr;
        }
    }
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值