一个管理监听、连接、发送、接收、组包的CSocket派生类

原创 2004年06月28日 13:27:00

简介:

    CAdmitSocket,从CSocket派生,它比CSocket增加的内容就是管理Accept后的Socket连接,重新定义Send函数。管理接收数据并组包,并在收到整包数据后,调用OnRecResponse(  LPBYTE, int )虚函数。

使用要点:

    你所使用的Socket必须从CAdmitSocket派生,并重载OnRecResponse()、OnSocketClose()。你可能需要重载OnAcceptSocket()。其中:

OnRecResponse()  : 当收到一个整包数据时,被调用。

OnAcceptSocket() :  表明一个客户连接已经被Accept。

OnSocketClose()   :  被接受的Socket已经关闭时,它和OnAcceptSocket()正好相反。

    你的派生类(假设为CMySocket)必须声明DECLARE_DYNCREATE(CMySocket)和IMPLEMENT_DYNCREATE(CMySocket,CAdmitSocket)。

    CPackHead是包头信息,其中字段:

            WORD    m_PackFlag;   //包标志,自定义的。

            UINT       m_PackSize;   //包长度,包括包头长度和数据长度

   如果你并不想要使用这个类,你也可以参照这个类的源代码,写你自己的Socket代码。

   附加:

   关于CSocket、CAsyncSocket使用的几点注意,否则将导致不必要的麻烦:
1、CSocket及其基类CAsyncSocket采用Windows消息机制,即Socket事件通过Post消息方式发往内建的窗口,并在该窗口内调用虚函数OnAccept()、OnConnect()、OnSend()、OnReceive()和OnClose()等,所以CSocket所在的线程必须要有消息循环。
2、一个CSocket对象要在多个线程里传递时,不能直接传递(指针)。因为CSocket被尽力设计为线程安全的,它的内部句柄m_hSocket同一时刻只能被一个线程拥有。因此,在线程间应该传递句柄而不是指针。方法是:当前拥有CSocket对象的线程先Detach(),然后将返回的句柄传给另一线程。在另一线程中,再Attach()。
3、CSocket在多线程中使用时,请对VS6.0打SP5。因为不打SP5和打SP5,AfxSockInit()函数的内部实现源代码是不同的。而后者则支持多线程。并且每个用到CSocket的线程,请都调用AfxSockInit()
4、对一次OnReceive()事件,请将Socket内部缓冲区的到达数据接收完,否则可能导致以后OnReceive()不再触发。原因是:OnSend()、OnReceive()都是当缓冲区从有到无或者从无到有数据才触发的。
5、Socket内部的默认缓冲区大小一般是4096或者8192,默认值大小特性是我们关注的,虽然不必要知道究竟有多大,但得知道有这么个特性,因此,除非是一应一答的通信,你应该在发送和接收时组包(包括自己定义包的边界和标志等信息)。

     头文件:AdmitSocket.h

#if !defined(AFX_ADMITSOCKET_H__68E0BD89_4E1E_48CB_B3E3_4235CED0C14B__INCLUDED_)
#define AFX_ADMITSOCKET_H__68E0BD89_4E1E_48CB_B3E3_4235CED0C14B__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <afxsock.h>
#include <afxtempl.h>

#ifdef _AFX_PACKING
#pragma pack(push, _AFX_PACKING)
#endif

//有关文件数据传输的Socket包结构和数据定义
#define   LISTENPORT1          3210   //Thales发送使用的Socket端口
#define   LISTENPORT2          3215   //和大调度通信的默认端口


typedef struct CPackHead {
 WORD    m_PackFlag;
 UINT    m_PackSize;
} CPackHead;

class CAdmitSocket : public CSocket
{
 DECLARE_DYNCREATE(CAdmitSocket)
public:
 CAdmitSocket();
 virtual ~CAdmitSocket();

 virtual void OnAcceptSocket( CAdmitSocket* pAcceptSock );
 virtual void OnSocketClose();
 virtual void OnRecResponse( LPBYTE, int );

 static CTypedPtrArray<CObArray,CAdmitSocket*>  s_SockArray;

 BOOL   Listen( int nPort = 0 );
 virtual void OnAccept(int nErrorCode);

 virtual void Close();

 BOOL    m_bListen;

 BOOL Connect( LPCTSTR lpszHostAddress, UINT nHostPort = 0 );
 BOOL Send( WORD nPackFlag, const void* lpBuf, int nBufLen );


public:
 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CAdmitSocket)
 public: 
 virtual void OnReceive(int nErrorCode);
 virtual void OnClose(int nErrorCode);
 //}}AFX_VIRTUAL

 // Generated message map functions
 //{{AFX_MSG(CAdmitSocket)
  // NOTE - the ClassWizard will add and remove member functions here.
 //}}AFX_MSG

// Implementation
protected:
 UINT    m_nBufSize;
 LPBYTE  m_pRecDataBuf;
 UINT    m_nPrevDataSize;
// CPackHead  m_PackHead;
};

inline void CAdmitSocket::OnRecResponse( LPBYTE, int )
{
}

inline void CAdmitSocket::OnAcceptSocket( CAdmitSocket* pAcceptSock )
{
}

inline void CAdmitSocket::OnSocketClose()
{
}


#ifdef _AFX_PACKING
#pragma pack(pop)
#endif

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_ADMITSOCKET_H__68E0BD89_4E1E_48CB_B3E3_4235CED0C14B__INCLUDED_)

执行文件:AdmitSocket.cpp

#include "stdafx.h"
#include "AdmitSocket.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//#define  RECBUF_SIZE   10000

IMPLEMENT_DYNCREATE(CAdmitSocket, CSocket)

CTypedPtrArray<CObArray,CAdmitSocket*>  CAdmitSocket::s_SockArray;

CAdmitSocket::CAdmitSocket():m_pRecDataBuf(NULL),m_nPrevDataSize(0)
{
       m_bListen       = FALSE;
       m_nBufSize      = 0;
}

CAdmitSocket::~CAdmitSocket()
{
      if( m_hSocket != INVALID_SOCKET )
           CSocket::Close();
      if( m_pRecDataBuf != NULL )
     {
          delete []m_pRecDataBuf;
          m_pRecDataBuf = NULL;
     }
}


// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CAdmitSocket, CSocket)
 //{{AFX_MSG_MAP(CAdmitSocket)
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0

void CAdmitSocket::OnReceive(int nErrorCode)   //服务器方的接收
{
      if( nErrorCode != 0 )
     {
          ASSERT( FALSE );
          return;
     }

     int         nRec;
     DWORD       dwBytes = 0;
     CPackHead*  pPackHead;
     UINT        nOffset = 0, nHeadSize = sizeof( CPackHead );
     if( !IOCtl( FIONREAD, &dwBytes ) )
    {
         dwBytes = GetLastError();
         if( dwBytes == WSAENETDOWN || dwBytes == WSAENOTSOCK )
              Close();
         return;
    }
    if( dwBytes == 0 )
    {
        ASSERT( FALSE );
        return;
    }
    if( dwBytes+m_nPrevDataSize > m_nBufSize )
   {
        LPBYTE pTmpBuf = new BYTE[dwBytes+m_nPrevDataSize];
        if( m_pRecDataBuf != NULL )
       {
             memcpy( pTmpBuf, m_pRecDataBuf, m_nPrevDataSize );
             delete []m_pRecDataBuf;
        }
        m_pRecDataBuf = pTmpBuf;
        m_nBufSize    = dwBytes+m_nPrevDataSize;
   }
 nRec = Receive( m_pRecDataBuf + m_nPrevDataSize, dwBytes );
 if( nRec <= 0 )
 {
  Close();
  return;
 }
 m_nPrevDataSize += nRec;
 while( m_nPrevDataSize > nHeadSize )
 {
  pPackHead = (CPackHead*)(m_pRecDataBuf+nOffset);
  if( m_nPrevDataSize < pPackHead->m_PackSize )
   break;
  OnRecResponse( (LPBYTE)pPackHead, pPackHead->m_PackSize );
  m_nPrevDataSize -= pPackHead->m_PackSize;
  nOffset += pPackHead->m_PackSize;
 }
 if( nOffset > 0 && m_nPrevDataSize > 0 )
  memcpy( m_pRecDataBuf, m_pRecDataBuf+nOffset, m_nPrevDataSize );

 

 

/* if( nErrorCode != 0 )
 {
  ASSERT( FALSE );
  return;
 }

 TRACE( _T("OnReceive(),准备接收:/n") );
 int nPackSize, nHeadSize = sizeof( m_PackHead );
 int nRecCount = Receive( &m_PackHead, nHeadSize );
 if( nRecCount == 0 || nRecCount == SOCKET_ERROR )
 {
  Close();
  ASSERT( FALSE );
  return;
 }
 TRACE( _T("开始收到%d字节/n"), nRecCount );
 nPackSize = nRecCount;
 while( nPackSize < nHeadSize )
 {
  nRecCount = Receive( ((char*)(&m_PackHead))+nPackSize, nHeadSize-nPackSize );
  if( nRecCount == 0 || nRecCount == SOCKET_ERROR )
  {
   Close();
   ASSERT( FALSE );
   return;
  }
  nPackSize += nRecCount;
 }

 TRACE( _T("收到包头%d字节,总包长=%d/n"), nPackSize, m_PackHead.m_PackSize );

 LPBYTE pDataBuf = new BYTE[m_PackHead.m_PackSize];
 memcpy( pDataBuf, (char*)&m_PackHead, nHeadSize );
 while( (UINT)nPackSize < m_PackHead.m_PackSize )
 {
  nRecCount = Receive( pDataBuf + nPackSize, m_PackHead.m_PackSize - nPackSize );
  if( nRecCount == 0 || nRecCount == SOCKET_ERROR )
  {
   Close();
   ASSERT( FALSE );
   return;
  }
  nPackSize += nRecCount;
 }
 TRACE( _T("收到整包%d字节,进行处理!/n"), nPackSize );
 ASSERT( nPackSize == m_PackHead.m_PackSize );
 OnRecResponse( pDataBuf, m_PackHead.m_PackSize );
 delete []pDataBuf;
 DWORD dwBytes = 0;
 if( IOCtl( FIONREAD, &dwBytes ) && dwBytes > 0 )
 {
  TRACE( _T("Socket内部还有%d字节没有收,继续.../n"), dwBytes );
  OnReceive( 0 );
 }*/

/* if( nErrorCode != 0 )
  return;
 int     nPackSize, nRecCount;
 DWORD   nBytes = 0;
 if( m_nPrevDataSize == 0 )
 {
  CPackHead   phs;   
  int         nHeadSize = sizeof(phs);
  if( !IOCtl( FIONREAD, &nBytes ) || nBytes < (DWORD)nHeadSize ) //检查到达有多少字节
   return;
  nRecCount = Receive( &phs, nHeadSize, MSG_PEEK );
  if( nRecCount == 0 || nRecCount == SOCKET_ERROR ) //连接已经关闭
  {
   OnClose(0);
   return;
  }
  if( nRecCount != nHeadSize )  //不够包头长度
   return;

  nPackSize = phs.m_PackSize;
  if( m_pRecDataBuf == NULL )
   m_pRecDataBuf = (BYTE*)malloc( nPackSize );
  else
   m_pRecDataBuf = (BYTE*)realloc( m_pRecDataBuf, nPackSize );
 }
 else
 {
  nPackSize = ((CPackHead*)m_pRecDataBuf)->m_PackSize;
  if( m_nPrevDataSize >= nPackSize )  //永远也不应该出现这种情况
  {
   m_nPrevDataSize = 0;
   return;
  }
 }
 nRecCount = Receive( m_pRecDataBuf + m_nPrevDataSize, nPackSize - m_nPrevDataSize );
 if( nRecCount == 0 || nRecCount == SOCKET_ERROR ) //连接已经关闭
 {
  OnClose( 0 );
  return;
 }
 m_nPrevDataSize += nRecCount;
 if( m_nPrevDataSize == nPackSize )
 {
//  if( s_pProcRespSock != NULL )  //如果本Socket处于子线程里,那么下面的响应函数如果位于别的线程,这就需要进行线程同步
//   s_pProcRespSock( this, m_pRecDataBuf, nPackSize );
  OnRecResponse( m_pRecDataBuf, nPackSize );
  m_nPrevDataSize = 0;
  free( m_pRecDataBuf );
  m_pRecDataBuf = NULL;
  if( IOCtl( FIONREAD, &nBytes ) && nBytes > 0 )
   OnReceive( 0 );
 }*/
}

void CAdmitSocket::OnClose(int nErrorCode)
{
 Close();
}

BOOL CAdmitSocket::Listen( int nPort )
{
 if( nPort == 0 )
  nPort = LISTENPORT1;
 if( m_hSocket == INVALID_SOCKET && !Create( nPort ) )
  return FALSE;
 return (m_bListen = CSocket::Listen( 0x7fffffff ));
}

void CAdmitSocket::OnAccept(int nErrorCode)
{
 CAdmitSocket* psockit = (CAdmitSocket*)this->GetRuntimeClass()->CreateObject();
 ASSERT( psockit != NULL );
 if( !Accept( *psockit ) )
  delete psockit;
 s_SockArray.Add( psockit );
 OnAcceptSocket( psockit );
 CSocket::OnAccept( nErrorCode ); 
}

void CAdmitSocket::Close()
{
// DWORD nBytes = 0;
// IOCtl( FIONREAD, &nBytes );

 

 CSocket::Close();
 int i, nCount = s_SockArray.GetSize();
 if( m_bListen )
 {
  for( i=0; i<nCount; i++ )
  {
   delete s_SockArray[i];
  }
  s_SockArray.RemoveAll();
 }
 else
 {
  for( i=0; i<nCount; i++ )
  {
   if( s_SockArray[i] == this )
   {
    OnSocketClose();
    s_SockArray.RemoveAt( i );
    delete this;
    break;
   }
  }
 }
}

BOOL CAdmitSocket::Connect( LPCTSTR lpszHostAddress, UINT nHostPort )
{
 if( m_hSocket == INVALID_SOCKET && !Create() )
  return false;
 if( nHostPort == 0 )
  nHostPort = LISTENPORT1;
 if( !CSocket::Connect( lpszHostAddress, nHostPort ) )
 {
  if( GetLastError() != WSAEWOULDBLOCK )
   return FALSE;
 }
 return TRUE;
}

BOOL CAdmitSocket::Send( WORD nPackFlag, const void* lpBuf, int nBufLen )
{
/* CPackHead  ph;
 ASSERT( nBufLen > 0 );
 ph.m_PackFlag  = nPackFlag;
 ph.m_PackSize  = sizeof(ph) + ((UINT)nBufLen);
 if( CSocket::Send( &ph, sizeof(ph) ) == SOCKET_ERROR )
  return FALSE;
 if( nBufLen > 0 )
  return (CSocket::Send( lpBuf, nBufLen ) != SOCKET_ERROR);
 return TRUE;
*/
 int nHeadSize = sizeof(CPackHead);
 CPackHead* pPackHead;
 BYTE* lpSendBuf = new BYTE[nHeadSize+nBufLen];
 pPackHead = (CPackHead*)lpSendBuf;
 pPackHead->m_PackFlag = nPackFlag;
 pPackHead->m_PackSize = (UINT)(nHeadSize + nBufLen);
 if( nBufLen > 0 )
  CopyMemory( lpSendBuf+nHeadSize, lpBuf, nBufLen );
 if( CSocket::Send( lpSendBuf, pPackHead->m_PackSize ) == SOCKET_ERROR )
 {
  delete [] lpSendBuf;
  return FALSE;
 }
 delete [] lpSendBuf;
 return TRUE;
}

TCP连接时CSocket粘包问题的解决方法

http://www.vckbase.com/document/viewdoc/?id=1203 解决TCP网络传输“粘包”问题作者:杨小平 王胜开原文出处:http://www.ciw.com.cn...
  • soli
  • soli
  • 2006年09月27日 21:39
  • 3623

Qt通过UDP传图片,实现自定义分包和组包

一.包头结构体 //包头 struct PackageHeader { //包头大小(sizeof(PackageHeader)) unsigned int uTransPack...
  • caoshangpa
  • caoshangpa
  • 2016年09月27日 16:35
  • 3260

关于CSocket::Accept()能不能作为接收客户端消息循环的判断条件

这两天小用了一下MFC的CSocket类,写点心得:其实网上好多高人都说这个类其实封装的不怎么样,我对此不发表意见,对我而言能用,会用,就成,还没有到评判微软的水准。所以说就练习着用了一下,本机电脑装...
  • panshiqu
  • panshiqu
  • 2013年08月02日 12:26
  • 1811

CAsyncSocket与CSocket的区别(即同步与异步)

一、CAsyncSocket与CSocket的区别 前者是异步通信,后者是同步通信;前者是非阻塞模式,后者是阻塞 模式。另外,异步非阻塞模式有时也被称为长连接,同步阻塞模式则 被称为短连接。为了...
  • good_jianhong
  • good_jianhong
  • 2011年10月21日 09:36
  • 2444

一个使用CSocket类的网络通信实例

一个使用CSocket类的网络通信实例 本例采用CSocket编程模型建立一个聊天程序的简单实例。建立项目时注意选上“Windows套接字”复选框。 3.8.1 服务器端应用程序设计(Server...
  • cuijinquan
  • cuijinquan
  • 2014年01月16日 15:25
  • 986

vc socket在debug模式下可以正常通讯,在release模式下经常通讯失败

问题描述:前一段时间写了一个基于socket的网络程序(B/S模式,应答通讯),使用的是MFC的异步socket类(CAsyncSocket),debug模式下可以正常通讯,打包发布后发现通讯总是失败...
  • u013030599
  • u013030599
  • 2016年08月16日 10:25
  • 721

CSocket进行UDP通信

客户端: MySocket类 1 class MySocket : public CSocket 2 { 3 public: 4 MySocket(); 5 ...
  • weili82830
  • weili82830
  • 2013年01月24日 10:31
  • 5556

TCP组包问题及处理方法

TCP组包问题及处理方法 问题的表述 原因的剖析 发送端处理方法 接收端处理方法 总结 问题的表述问题的背景是这样的:有一个系统,那有后台服务器,也有移动端的客户端。当客户端上线时,服务器会将指定的数...
  • Snipergzf
  • Snipergzf
  • 2016年03月05日 18:10
  • 3171

WEBRTC音视频接收(从网络接收RTP包到组帧)

 WebRtcSession::SetLocalDescription|WebRtcSession::SetRemoteDescription-> WebRtcSession::Creat...
  • doitsjz
  • doitsjz
  • 2016年09月07日 18:55
  • 2222

蓝牙收发数据过大需要分包-组包处理

{ static unsigned char Bt_RxData_Merge_Status=Bt_RxData_Merge_Defaul_Status; //意思是一个iA...
  • CAO527121128
  • CAO527121128
  • 2015年04月23日 16:16
  • 3581
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一个管理监听、连接、发送、接收、组包的CSocket派生类
举报原因:
原因补充:

(最多只允许输入30个字)