实现1对N可配置Socket服务器程序的大致思路
来源:优易学  2011-11-26 10:45:02   【优易学:中国教育考试门户网】   资料下载   IT书店
  大致思路:框架是MFC单文档程序, 从CSocket派生一个CListen类,然后在Doc类:CWebDoc中的OnNewDocument函数中new一个CListen对象,将该对象指针保存到文档类成员m_pSocket中,并把文档指针传递给CListen类:
  m_pSocket = new CListen ( this ) ;
  OnNewDocument接着调用CListen的Create:
  m_pSocket->Create ( theApp.m_wwwPort );
  OnNewDocument接着调用CListen的Listen,开始监听服务器m_wwwPort端口:
  m_pSocket->Listen();
  当服务器m_wwwPort端口侦听到连接请求时,CListen类的OnAccept重载函数会被调用。在CListen类的OnAccept中调用我们在Doc类中自建的一个OnAccept()函数。
  void CListen::OnAccept(int nErrorCode)
  {
  CSocket::OnAccept(nErrorCode);
  m_pDoc->OnAccept() ; // process this in the context of the document
  }
  显然,一旦某个客户的连接请求到来,我们在Doc类中自建的OnAccept()函数,马上被调用,代码如上。Doc类的OnAccept()函数马上new一个CClient,并把文档指针传递给CClient类:
  CClient *pClient = new CClient ( this ) ;
  接下来用CListen指针m_pSocket来调用Accept,并把刚刚new的CClient对象作为参数:
  m_pSocket->Accept ( *pClient );
  如无意外,一个套接字连接就建立了。因为服务器要1对N服务,为了维护这些连接,我们在文档类增加了一个CPtrList成员:m_listConnects; 连接一旦建立,立即测试当前m_listConnects中的已有连接数是否已经达到最大连接限制:
  if ( m_listConnects.GetCount() >= (int)theApp.m_nMaxConnects )
  {
  // 友好提示客户,服务器已经满载,请稍后再试。。。
  pClient->SendCannedMsg ( 503 ) ;
  delete pClient;
  }
  当然,如果连接List还没有满载,马上调用m_listConnects的AddTail把CClient指针加进指针链表成员中,以便后来
  服务器与各客户收发报文使用。
  在CClient的OnReceive函数中,我们接受客户发来的数据,并分析数据,处理数据,完后发送响应报文:
  void CClient::OnReceive(int nErrorCode)
  {
  CSocket::OnReceive(nErrorCode);
  DWORD dwBytes ;
  IOCtl ( FIONREAD, &dwBytes ) ; // anything to get?
  if ( dwBytes == 0 )
  return ( TRUE ) ; // we must be done!
  byte* buf = (byte*)malloc ( dwBytes )
  if ( Receive ( buf, dwBytes, 0 ) )
  ProcessReq();
  }
  在CClient的OnClose函数中,我们可以记载客户关闭套接字的事件:
  void CClient::OnClose(int nErrorCode)
  {
  CSocket::OnClose(nErrorCode);
  m_pDoc->Message ( "OnClose::\n" ) ;
  }
  为了及时关闭和释放那些长期不活动的客户连接,CClient类维护了一个LOG_RECORD成员m_LogRec,记载了其相关的信息。例举一个LOG_RECORD结构大致如下:
  typedef struct
  {
  CString client ; // 客户机器IP或名称
  CTime datetime ; // 最后报文发送时间
  LONG reqID; // 最后报文请求ID
  ......
  } LOG_RECORD, *LPLOG_RECORD;
  在CMainFrame类开启一个Timer以1分钟为间隔,定期清理释放不活跃客户:
  CXXXDoc* pDoc = (CXXXDoc*)GetActiveDocument() ;
  pDoc->CheckIdleConnects() ;
  在文档类的CheckIdleConnects函数中:
  time_t tNow ;
  time( &tNow ) ;
  CTime cNow ( tNow ) ;
  CTimeSpan cTimeOut ( 0, 0, 0, theApp.m_nTimeOut ) ;
  cNow -= cTimeOut ;
  for ( POSITION pos = m_listConnects.GetHeadPosition() ; pos != NULL ; )
  {
  CClient* pClient = (CClient*)m_listConnects.GetNext ( pos ) ;
  if ( pClient->m_LogRec.datetime < cNow )
  {
  m_listConnects.RemoveAt ( pos ) ;
  delete pClient;
  }
  }

责任编辑:小草

文章搜索:
 相关文章
热点资讯
资讯快报
热门课程培训