⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sockclient.cpp

📁 一些初级的网络编程
💻 CPP
字号:
// SockClient.cpp : implementation file
//

#include "stdafx.h"
#include "TcpHoleSrv.h"
#include "SockClient.h"
#include <Afxmt.h>

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

CPtrArray g_PtrAry_SockClient;
CCriticalSection g_CSFor_PtrAry_SockClient;
extern BOOL g_bDeleteNullSockClient;
DWORD g_nSockClientID = 0;

CSockClient* GetNewSockClient ()
{
	CSockClient *pSockClient = new CSockClient;
	if ( !pSockClient )
	{
		printf ( "New SockClient object failed\n" );
		return NULL;
	}
	g_CSFor_PtrAry_SockClient.Lock();
	g_PtrAry_SockClient.Add ( pSockClient );
	pSockClient->m_dwID = ++g_nSockClientID;
	g_CSFor_PtrAry_SockClient.Unlock();
	printf ( "Current SocketClient array count is %d\n", g_PtrAry_SockClient.GetSize() );

	return pSockClient;
}

//
// 将新客户端登录信息发送给所有已登录的客户端,但不发送给自己
//
BOOL SendNewUserLoginNotifyToAll ( LPCTSTR lpszClientIP, UINT nClientPort, DWORD dwID )
{
	ASSERT ( lpszClientIP && nClientPort > 0 );
	g_CSFor_PtrAry_SockClient.Lock();
	for ( int i=0; i<g_PtrAry_SockClient.GetSize(); i++ )
	{
		CSockClient *pSockClient = (CSockClient*)g_PtrAry_SockClient.GetAt(i);
		if ( pSockClient && pSockClient->m_bMainConn && pSockClient->m_dwID > 0 && pSockClient->m_dwID != dwID )
		{
			if ( !pSockClient->SendNewUserLoginNotify ( lpszClientIP, nClientPort, dwID ) )
			{
				g_CSFor_PtrAry_SockClient.Unlock();
				return FALSE;
			}
		}
	}

	g_CSFor_PtrAry_SockClient.Unlock ();
	return TRUE;
}

CSockClient* FindSocketClient ( DWORD dwID )
{
	g_CSFor_PtrAry_SockClient.Lock ();
	for ( int i=0; i<g_PtrAry_SockClient.GetSize(); i++ )
	{
		CSockClient *pSockClient = (CSockClient*)g_PtrAry_SockClient.GetAt(i);
		if ( pSockClient && pSockClient->m_dwID == dwID )
		{
			g_CSFor_PtrAry_SockClient.Unlock ();
			return pSockClient;
		}
	}
	printf ( "Can't find ID:%u\n", dwID );
	g_CSFor_PtrAry_SockClient.Unlock ();
	return NULL;
}

void DeleteNullSocketClient ()
{
	g_bDeleteNullSockClient = FALSE;
	g_CSFor_PtrAry_SockClient.Lock ();
	for ( int i=0; i<g_PtrAry_SockClient.GetSize(); i++ )
	{
		CSockClient *pSockClient = (CSockClient*)g_PtrAry_SockClient.GetAt(i);
		if ( pSockClient && pSockClient->m_dwID == 0 )
		{
			delete pSockClient;
			g_PtrAry_SockClient.RemoveAt(i);
			i --;
		}
	}
	printf ( "Current SocketClient array count is %d\n", g_PtrAry_SockClient.GetSize() );
	g_CSFor_PtrAry_SockClient.Unlock ();
}

DWORD WINAPI ThreadProc_SockClient(
  LPVOID lpParameter   // thread data
)
{
	CSockClient *pSockClient = reinterpret_cast<CSockClient*>(lpParameter);
	if ( pSockClient )
		return pSockClient->ThreadProc_SockClient ();
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CSockClient

CSockClient::CSockClient()
	: m_dwID ( 0 )
	, m_nPeerPort ( 0 )
	, m_hThread ( NULL )
	, m_hEvtEndModule ( NULL )
	, m_hEvtWaitClientBHole ( NULL )
	, m_dwThreadID ( 0 )
	, m_bMainConn ( FALSE )
{
}

CSockClient::~CSockClient()
{
	this->CancelBlockingCall ();
	if ( HANDLE_IS_VALID(m_hEvtEndModule) )
		::SetEvent ( m_hEvtEndModule );
	WaitForThreadEnd ( &m_hThread );
}


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

/////////////////////////////////////////////////////////////////////////////
// CSockClient member functions

//
// 和客户端建立连接
//
BOOL CSockClient::EstablishConnect(SOCKET hSock, SOCKADDR_IN &sockAddr, BOOL bMainConn)
{
	ASSERT ( HANDLE_IS_VALID(hSock) );
	m_csPeerAddress = inet_ntoa(sockAddr.sin_addr);
	m_nPeerPort = ntohs(sockAddr.sin_port);
	m_hSocket = hSock;
	m_bMainConn = bMainConn;

	if ( m_bMainConn )
	{
		if ( !SendNewUserLoginNotifyToAll ( m_csPeerAddress, m_nPeerPort, m_dwID ) )
			return FALSE;
		printf ( "[MAIN SOCKET] New connection ( %s:%d:%u ) come in\n", m_csPeerAddress, m_nPeerPort, m_dwID );
	}
	else
	{
		printf ( "<HOLE SOCKET> New connection ( %s:%d:%u ) come in\n", m_csPeerAddress, m_nPeerPort, m_dwID );
	}

	// 创建一个线程来处理该客户端的所有数据通信
	ASSERT ( m_hEvtEndModule == NULL && m_hEvtWaitClientBHole == NULL && m_hThread == NULL );
	m_hEvtEndModule = ::CreateEvent ( NULL, TRUE, FALSE, NULL );
	m_hEvtWaitClientBHole = ::CreateEvent ( NULL, FALSE, FALSE, NULL );
	m_hThread = ::CreateThread ( NULL, 0, ::ThreadProc_SockClient, this, 0, &m_dwThreadID );
	if ( !HANDLE_IS_VALID(m_hThread) || !HANDLE_IS_VALID(m_hEvtEndModule) || !HANDLE_IS_VALID(m_hEvtWaitClientBHole) )
		return FALSE;

	return TRUE;
}

//
// 将其他新登录的客户端信息发送给该客户端
//
BOOL CSockClient::SendNewUserLoginNotify(LPCTSTR lpszClientIP, UINT nClientPort, DWORD dwID)
{
	ASSERT ( lpszClientIP && nClientPort > 0 );
	if ( !m_bMainConn ) return TRUE;
	t_NewUserLoginPkt NewUserLoginPkt;
	STRNCPY_SZ ( NewUserLoginPkt.szClientIP, lpszClientIP );
	NewUserLoginPkt.nClientPort = nClientPort;
	NewUserLoginPkt.dwID = dwID;
	printf ( "Send new user login notify to (%s:%u:%u)\n", m_csPeerAddress, m_nPeerPort, m_dwID );
	return ( SendChunk ( &NewUserLoginPkt, sizeof(t_NewUserLoginPkt), 0 ) == sizeof(t_NewUserLoginPkt) );
}

BOOL CSockClient::ThreadProc_SockClient()
{
	printf ( "Client.%u thread start.\n", m_dwID );
	BOOL bRet = FALSE;
	char szRecvBuffer[NET_BUFFER_SIZE] = {0};
	int nRecvBytes = 0;

	// WinSocket 的句柄是不能跨线程访问的,所以这里重新附加进来
	if ( !Attach ( m_hSocket ) )
		goto finished;

	// 发送欢迎信息,通知客户端连接成功了。
	SendWelcomeInfo ();
	// 循环接收网络数据
	while ( TRUE )
	{
		nRecvBytes = Receive ( szRecvBuffer, sizeof(szRecvBuffer) );
		if ( nRecvBytes > 0 )
		{
			if ( !HandleDataReceived ( szRecvBuffer, nRecvBytes ) )
				goto finished;
		}
		else if ( (nRecvBytes == 0 && GetLastError() != NO_ERROR) || (SOCKET_ERROR == nRecvBytes && GetLastError() == WSAEWOULDBLOCK) )
		{
			SLEEP_BREAK ( 10 );
		}
		// 对方断开连接了
		else
		{
			goto finished;
		}
		SLEEP_BREAK ( 1 );
	}

	bRet = TRUE;
finished:
	Close ();
	printf ( "Client.%u thread end. result : %s\n", m_dwID, bRet?"SCUESS":"FAILED" );
	if ( HANDLE_IS_VALID(m_hEvtEndModule) )
		::SetEvent ( m_hEvtEndModule );
	m_dwID = 0;
	printf ( "ThreadProc_SockClient end\n" );
	g_bDeleteNullSockClient = TRUE;
	return bRet;
}

BOOL CSockClient::HandleDataReceived(char *data, int size)
{
	if ( !data || size < 4 ) return FALSE;

	PACKET_TYPE *pePacketType = (PACKET_TYPE*)data;
	ASSERT ( pePacketType );
	switch ( *pePacketType )
	{
		// 要求与另一个客户端建立直接的TCP连接
	case PACKET_TYPE_REQUEST_CONN_CLIENT:
		{
			ASSERT ( !m_bMainConn );
			ASSERT ( size == sizeof(t_ReqConnClientPkt) );
			t_ReqConnClientPkt *pReqConnClientPkt = (t_ReqConnClientPkt*)data;
			if ( !Handle_ReqConnClientPkt ( pReqConnClientPkt ) )
				return FALSE;
			break;
		}
		// 被动端(客户端B)请求服务器断开连接,这个时候应该将客户端B的外部IP和端口号告诉客户端A,并让客户端A主动
		// 连接客户端B的外部IP和端口号
	case PACKET_TYPE_REQUEST_DISCONNECT:
		{
			ASSERT ( !m_bMainConn );
			ASSERT ( size == sizeof(t_ReqSrvDisconnectPkt) );
			t_ReqSrvDisconnectPkt *pReqSrvDisconnectPkt = (t_ReqSrvDisconnectPkt*)data;
			ASSERT ( pReqSrvDisconnectPkt );
			printf ( "Clinet.%u request disconnect\n", m_dwID );

			CSockClient *pSockClientHole_A = FindSocketClient ( pReqSrvDisconnectPkt->dwInviterHoleID );
			if ( !pSockClientHole_A ) return FALSE;
			pSockClientHole_A->m_SrvReqDirectConnectPkt.dwInvitedID = pReqSrvDisconnectPkt->dwInvitedID;
			STRNCPY_CS ( pSockClientHole_A->m_SrvReqDirectConnectPkt.szInvitedIP, m_csPeerAddress );
			pSockClientHole_A->m_SrvReqDirectConnectPkt.nInvitedPort = m_nPeerPort;

			Close ();

			break;
		}
		// 被动端(客户端B)打洞和侦听均已准备就绪
	case PACKET_TYPE_HOLE_LISTEN_READY:
		{
			ASSERT ( m_bMainConn );
			ASSERT ( size == sizeof(t_HoleListenReadyPkt) );
			t_HoleListenReadyPkt *pHoleListenReadyPkt = (t_HoleListenReadyPkt*)data;
			ASSERT ( pHoleListenReadyPkt );
			printf ( "Client.%u hole and listen ready\n", pHoleListenReadyPkt->dwInvitedID );
			// 通知正在与客户端A通信的服务器线程,以将客户端B的外部IP地址和端口号告诉客户端A
			CSockClient *pSockClientHole_A = FindSocketClient ( pHoleListenReadyPkt->dwInviterHoleID );
			if ( !pSockClientHole_A ) return FALSE;
			if ( HANDLE_IS_VALID(pSockClientHole_A->m_hEvtWaitClientBHole) )
				SetEvent ( pSockClientHole_A->m_hEvtWaitClientBHole );
			break;
		}
	}

	return TRUE;
}

//
// 客户端A请求我(服务器)协助连接客户端B,这个包应该在打洞Socket中收到
//
BOOL CSockClient::Handle_ReqConnClientPkt(t_ReqConnClientPkt *pReqConnClientPkt)
{
	ASSERT ( !m_bMainConn );
	CSockClient *pSockClient_B = FindSocketClient ( pReqConnClientPkt->dwInvitedID );
	if ( !pSockClient_B ) return FALSE;
	printf ( "%s:%u:%u invite %s:%u:%u connection\n", m_csPeerAddress, m_nPeerPort, m_dwID,
		pSockClient_B->m_csPeerAddress, pSockClient_B->m_nPeerPort, pSockClient_B->m_dwID );

	// 客户端A想要和客户端B建立直接的TCP连接,服务器负责将A的外部IP和端口号告诉给B
	t_SrvReqMakeHolePkt SrvReqMakeHolePkt;
	SrvReqMakeHolePkt.dwInviterID = pReqConnClientPkt->dwInviterID;
	SrvReqMakeHolePkt.dwInviterHoleID = m_dwID;
	SrvReqMakeHolePkt.dwInvitedID = pReqConnClientPkt->dwInvitedID;
	STRNCPY_CS ( SrvReqMakeHolePkt.szClientHoleIP, m_csPeerAddress );
	SrvReqMakeHolePkt.nClientHolePort = m_nPeerPort;
	if ( pSockClient_B->SendChunk ( &SrvReqMakeHolePkt, sizeof(t_SrvReqMakeHolePkt), 0 ) != sizeof(t_SrvReqMakeHolePkt) )
		return FALSE;

	// 等待客户端B打洞完成,完成以后通知客户端A直接连接客户端外部IP和端口号
	if ( !HANDLE_IS_VALID(m_hEvtWaitClientBHole) )
		return FALSE;
	if ( WaitForSingleObject ( m_hEvtWaitClientBHole, 6000*1000 ) == WAIT_OBJECT_0 )	//d
	{
		if ( SendChunk ( &m_SrvReqDirectConnectPkt, sizeof(t_SrvReqDirectConnectPkt), 0 ) == sizeof(t_SrvReqDirectConnectPkt) )
			return TRUE;
	}

	return FALSE;
}

BOOL CSockClient::SendWelcomeInfo()
{
	if ( !m_bMainConn ) return TRUE;
	t_WelcomePkt WelcomePkt;
	STRNCPY_CS ( WelcomePkt.szClientIP, m_csPeerAddress );
	WelcomePkt.nClientPort = m_nPeerPort;
	WelcomePkt.dwID = m_dwID;
	_snprintf ( WelcomePkt.szWelcomeInfo, sizeof(WelcomePkt.szWelcomeInfo),
		"Hello, ID.%u, Welcome to login", m_dwID );

	return ( SendChunk ( &WelcomePkt, sizeof(t_WelcomePkt), 0 ) == sizeof(t_WelcomePkt) );
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -