织梦CMS - 轻松建站从此开始!

罗索

Linux下的select封装(暂时只对socket)

jackyhwei 发布于 2011-12-27 19:33 点击:次 
最近在写一个Linux下的多线程的网络聊天软件,以前一直都是来一个连接开一个线程去recv,最近刚看了select函数的用法,就把select封成了一个类,外界只要创建一个该类的对象,然后把socket注册进来就可以。
TAG:

最近在写一个Linux下的多线程的网络聊天软件,以前一直都是来一个连接开一个线程去recv,最近刚看了select函数的用法,就把select封成了一个类,外界只要创建一个该类的对象,然后把socket注册进来就可以。

本人新手,如果有什么问题或者bug或者可以改进的地方请各位多指教!!

初始化后开一个线程select,同时监听一个udp端口,用于注册socket或注销socket时主线程通知select的线程。

加锁和端口管理未实现…………

socketNotifier.hpp

  1. #ifndef SOCKETNOTIFIER_HPP_ 
  2. #define SOCKETNOTIFIER_HPP_ 
  3.  
  4. #include <map> 
  5. #include <set> 
  6. #include <pthread.h> 
  7.  
  8. #ifdef WIN32 
  9. #include <WinSock2.h> 
  10. #include <WS2tcpip.h> 
  11. //typedef int socklen_t 
  12. #else 
  13. #include <sys/socket.h> 
  14. #include <netinet/in.h> 
  15. #include <arpa/inet.h> 
  16. #endif 
  17.  
  18. #include <sys/types.h> 
  19.  
  20. /** CSocketNotifier socket通知类 
  21. *  总管socket的通知,对select作一层封装 
  22. */ 
  23.  
  24. class CSocketNotifier 
  25. public
  26.     CSocketNotifier(); 
  27.     ~CSocketNotifier(); 
  28.  
  29. public
  30.     /** 回调函数 */ 
  31.     typedef bool ( *sockCallBack )( void* ); 
  32.     typedef struct tagSockParam 
  33.     { 
  34.         int sock_fd; 
  35.         void* pUsr; 
  36.     }SOCKPARAM; 
  37.  
  38.     /** 获取空闲的端口 
  39.     * 
  40.     *  获取一个当前未使用的端口 
  41.     *  @return:  unsigned short 
  42.     */ 
  43.     unsigned short getFreePort(); 
  44.  
  45.     /** 注册socket 
  46.     * 
  47.     *  把一个socket注册到类中 
  48.     *  @param:  int sock_fd 
  49.     *  @param:  sockCallBack pFun 
  50.     *  @param:  void * pParam 
  51.     *  @return:  bool 
  52.     */ 
  53.     bool registerSocket( int sock_fd, sockCallBack pFun, void* pParam ); 
  54.  
  55.     /** 注销socket 
  56.     * 
  57.     *  把一个socket从类中注销 
  58.     *  @param:  int sock_fd 
  59.     *  @return:  bool 
  60.     */ 
  61.     bool unregisterSocket( int sock_fd ); 
  62.  
  63.     /** 等待时间的发生 
  64.     * 
  65.     *  用select函数等待 
  66.     *  @param:  void * 
  67.     *  @return:  void* 
  68.     */ 
  69.     static void* waitForEvent( void * ); 
  70.  
  71.     /** 更新socket集合 
  72.     * 
  73.     *  更新socket集合 
  74.     *  @param:  const std::set<int> &fdVec 
  75.     *  @return:  void* 
  76.     */ 
  77.     static bool renewFD( int event_fd, std::set<int> &fdSet
  78. , fd_set *pre_fdsr, int &iMaxFD ); 
  79.  
  80.     /** 事件发生后通知 
  81.     *  遍历所有注册进来的socket,通知 
  82.     *  @param:  int iEventNum 
  83.     *  @return:  bool 
  84.     */ 
  85.     bool notify( fd_set* pfdsr, int iEventNum );  
  86. private
  87.     /** 通知线程socket变化 
  88.     *  通知线程socket变化 
  89.     *  @param:  int sock_fd 
  90.     *  @return:  bool 
  91.     */ 
  92.     bool sendNoticeToThread( int sock_fd ); 
  93.  
  94. private
  95.     /** 回调函数集合,int对应socket句柄,sockCallBack对应函数,void*对应参数 */ 
  96.     std::map< int, std::pair < sockCallBack, void* > > m_aFun; 
  97.     pthread_t m_threadID; 
  98.     /** 停止时退出线程的标志 */ 
  99.     bool m_bRun; 
  100.  
  101.     /** 初始化是否成功 */ 
  102.     bool m_bInit; 
  103.  
  104.     /** 用于向线程发送信令用 */ 
  105.     int m_socketFD; 
  106.     int m_socketForNotify; 
  107.     unsigned short m_uPort; 
  108.  
  109.     /** 最多的socket数量 */ 
  110.     static const int MAX_SOCKET_NUM = 32; 
  111.  
  112.     /** 最大的命令长度 */ 
  113.     static const int MAX_COM_LEN = 20; 
  114.     const unsigned short SOCKET_NOTIFY_PORT; 
  115. }; 
  116.  
  117. #endif 

socketNotifier.cpp

#include "StdAfx.h"
#include "SocketNotifier.h"

#include <iostream>

CSocketNotifier::CSocketNotifier()
: m_bRun( true )
, SOCKET_NOTIFY_PORT( 1924 )
{
    /** do-while 为了初始化失败时break */
    do
    {
        /** 线程中用于接受主线程发出的控制信令的socket */
        if ( ( m_socketFD = socket( AF_INET, SOCK_DGRAM, 0 ) ) == -1 )
        {
            m_bInit = false;
            perror( "m_socketFD init failed!" );
            break;
        }

        /** 用于向线程发送信令的socket */
        if ( ( m_socketForNotify = socket( AF_INET, SOCK_DGRAM, 0 ) ) == -1 )
        {
            m_bInit = false;
            perror( "m_socketForNotify init failed!" );
            break;
        }
        sockaddr_in localAddr;
        m_uPort = getFreePort();
        localAddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
        localAddr.sin_family = AF_INET;
        localAddr.sin_port = htons( m_uPort );
        if ( bind( m_socketFD, ( sockaddr* )&localAddr, sizeof( sockaddr ) ) == -1 )
        {
            m_bInit = false;
            perror( "Socket bind failed!" );
            break;
        }
        SOCKPARAM* pParam = new SOCKPARAM;
        pParam->pUsr = ( void* )this;
        pParam->sock_fd = m_socketFD;
        /** 开线程select */
        int ret = pthread_create( &m_threadID, NULL, waitForEvent, pParam );
        if ( 0 != ret )
        {
            perror( "CSocketNotifier:CSocketNotifier" );
            m_bInit = false;
        }
        else
        {
            m_bInit = true;
        }
    } while ( 0 );
}

CSocketNotifier::~CSocketNotifier()
{
    m_bRun = false;
    pthread_join( m_threadID, NULL );
}

bool CSocketNotifier::registerSocket( int sock_fd, sockCallBack pFun, void* pParam )
{
    if ( sock_fd < 0 )
    {
        std::cout<<"Illegal param!"<<std::endl;
        return false;
    }
    /** 如果找到就说明已经有注册过,不用再注册 */
    if ( m_aFun.find( sock_fd ) != m_aFun.end() )
    {
        return true;
    }
    /** 如果没找到则要添加 */
    m_aFun[sock_fd] = std::make_pair<sockCallBack, void*>( pFun, pParam);
    /** 通知select线程添加新的socket */
    if ( !sendNoticeToThread( sock_fd ) )
    {
        return false;
    }

    return true;
}

bool CSocketNotifier::unregisterSocket( int sock_fd )
{
    if ( sock_fd < 0 )
    {
        std::cout<<"Illegal param!"<<std::endl;
        return false;
    }
    /** 找到就删 */
    if ( m_aFun.find( sock_fd ) != m_aFun.end() )
    {
        m_aFun.erase( sock_fd );
    }
    /** 通知select线程删除socket */
    if ( !sendNoticeToThread( sock_fd ) )
    {
        return false;
    }

    return true;
}

bool CSocketNotifier::renewFD( int event_fd, std::set<int> &fdSet, fd_set *pre_fdsr, int &iMaxFD )
{
    /** 是已经有了的socket,说明是要移除 */
    if ( FD_ISSET( event_fd, pre_fdsr ) )
    {
        /** 移除后找到最大的socket */
        iMaxFD = 0;
        std::set<int>::const_iterator it = fdSet.begin();
        for ( ; it != fdSet.end(); ++it )
        {
            if ( *it > iMaxFD )
            {
                iMaxFD = *it;
            }
        }
        FD_CLR( event_fd, pre_fdsr );
        fdSet.erase( event_fd );
        std::cout<<"Remove a fd"<<std::endl;
    }
    /** 是已新来的socket,说明是要添加 */
    else
    {
        if ( event_fd == iMaxFD )
        {
            if ( event_fd > iMaxFD )
            {
                iMaxFD = event_fd;
            }
        }
        FD_SET( event_fd, pre_fdsr );
        fdSet.insert( event_fd );
        std::cout<<"Add a fd"<<std::endl;
    }

    return true;
}

void* CSocketNotifier::waitForEvent( void* param )
{
    SOCKPARAM* pParam = ( SOCKPARAM* )param;
    CSocketNotifier *pSocketNotifier = ( CSocketNotifier * )( pParam->pUsr );
    std::set<int> fdSet;

    fd_set fdsr, pre_fdsr;
    FD_ZERO( &pre_fdsr );
    FD_ZERO( &pre_fdsr );
    FD_SET( pParam->sock_fd, &pre_fdsr );
    fdSet.insert( pParam->sock_fd );
    struct timeval tv;
    tv.tv_sec = 30;
    tv.tv_usec = 0;

    /** 最大的sock */
    int iMaxFD = pParam->sock_fd;
    int iEvenNum;

    while ( 1 )
    {
        memcpy( &fdsr, &pre_fdsr, sizeof( fd_set ) );
        iEvenNum = select( iMaxFD + 1, &fdsr, NULL, NULL, &tv );
        /** 时间到 */
        if ( 0 == iEvenNum )
        {
            continue;
        }
        /** 出错 */
        else if ( 0 > iEvenNum )
        {
            perror( "select" );
            break;
        }
        /** 正常处理 */
        else
        {
            /** 主线程发来的控制信令 */
            if ( FD_ISSET( pParam->sock_fd, &fdsr ) )
            {
                char szRecvBuf[MAX_COM_LEN + 1];
                unsigned int uiRecvLen;
                socklen_t iSockAddrLen = sizeof( struct sockaddr );
                struct sockaddr_in stClientAddr;
                int event_fd;
                int iRet = recvfrom( pParam->sock_fd,szRecvBuf,MAX_COM_LEN,0,( struct sockaddr * ) ( &stClientAddr ),&iSockAddrLen );
                /** 网络错误或关闭时返回 */
                if ( 0 == uiRecvLen || -1 == uiRecvLen )
                {
                    perror( "CSocketNotifier: recvfrom" );
                    break;
                }
                memcpy( &event_fd, szRecvBuf, sizeof( int ) );
                /** 更新socket集合 */
                renewFD( event_fd, fdSet, &pre_fdsr, iMaxFD );
                --iEvenNum;
            }

            /** 通知回调函数 */
            pSocketNotifier->notify( &fdsr, iEvenNum );
        }
    }

    delete pParam;

    return NULL;
}

bool CSocketNotifier::notify( fd_set* pfdsr, int iEventNum )
{
    /** RemainToDo 加锁 */
    std::map< int, std::pair< sockCallBack, void* > >::const_iterator it = m_aFun.begin();
    for ( ; ( it != m_aFun.end() ) && ( iEventNum != 0 ); ++it )
    {
        if ( FD_ISSET( it->first, pfdsr ) )
        {
            ( *( it->second.first ) )( it->second.second );
            --iEventNum;
        }
    }
    return true;
}

unsigned short CSocketNotifier::getFreePort()
{
    /** 暂时未实现 */
    return SOCKET_NOTIFY_PORT;
}

bool CSocketNotifier::sendNoticeToThread( int sock_fd )
{
    int iRet;
    char buf[ MAX_COM_LEN ] = { 0 };
    sockaddr_in localAddr;
    localAddr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
    localAddr.sin_port = htons( m_uPort );
    localAddr.sin_family = AF_INET;
    memset( localAddr.sin_zero, 0, sizeof( localAddr.sin_zero ) );
    memcpy( buf, &sock_fd, sizeof( int ) );

    iRet = sendto( m_socketForNotify,
        buf,
        MAX_COM_LEN,
        0,
        ( sockaddr* )&localAddr,
        sizeof( sockaddr ) );
    if ( -1 == iRet)
    {
        perror( "CSocketNotifier:sendNoticeToThread" );
        return false;
    }
    return true;
}
 

(hustxyj)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201112/15549.html]
本文出处:CSDN博客 作者:hustxyj 原文
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容