简介:
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;
}