SockClient.cpp
上传用户:chinaliu11
上传日期:2022-03-10
资源大小:11842k
文件大小:10k
源码类别:

P2P编程

开发平台:

Visual C++

  1. // SockClient.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "TcpHoleSrv.h"
  5. #include "SockClient.h"
  6. #include <Afxmt.h>
  7. #ifdef _DEBUG
  8. #define new DEBUG_NEW
  9. #undef THIS_FILE
  10. static char THIS_FILE[] = __FILE__;
  11. #endif
  12. CPtrArray g_PtrAry_SockClient;
  13. CCriticalSection g_CSFor_PtrAry_SockClient;
  14. extern BOOL g_bDeleteNullSockClient;
  15. DWORD g_nSockClientID = 0;
  16. CSockClient* GetNewSockClient ()
  17. {
  18. CSockClient *pSockClient = new CSockClient;
  19. if ( !pSockClient )
  20. {
  21. printf ( "New SockClient object failedn" );
  22. return NULL;
  23. }
  24. g_CSFor_PtrAry_SockClient.Lock();
  25. g_PtrAry_SockClient.Add ( pSockClient );
  26. pSockClient->m_dwID = ++g_nSockClientID;
  27. g_CSFor_PtrAry_SockClient.Unlock();
  28. printf ( "Current SocketClient array count is %dn", g_PtrAry_SockClient.GetSize() );
  29. return pSockClient;
  30. }
  31. //
  32. // 将新客户端登录信息发送给所有已登录的客户端,但不发送给自己
  33. //
  34. BOOL SendNewUserLoginNotifyToAll ( LPCTSTR lpszClientIP, UINT nClientPort, DWORD dwID )
  35. {
  36. ASSERT ( lpszClientIP && nClientPort > 0 );
  37. g_CSFor_PtrAry_SockClient.Lock();
  38. for ( int i=0; i<g_PtrAry_SockClient.GetSize(); i++ )
  39. {
  40. CSockClient *pSockClient = (CSockClient*)g_PtrAry_SockClient.GetAt(i);
  41. if ( pSockClient && pSockClient->m_bMainConn && pSockClient->m_dwID > 0 && pSockClient->m_dwID != dwID )
  42. {
  43. if ( !pSockClient->SendNewUserLoginNotify ( lpszClientIP, nClientPort, dwID ) )
  44. {
  45. g_CSFor_PtrAry_SockClient.Unlock();
  46. return FALSE;
  47. }
  48. }
  49. }
  50. g_CSFor_PtrAry_SockClient.Unlock ();
  51. return TRUE;
  52. }
  53. CSockClient* FindSocketClient ( DWORD dwID )
  54. {
  55. g_CSFor_PtrAry_SockClient.Lock ();
  56. for ( int i=0; i<g_PtrAry_SockClient.GetSize(); i++ )
  57. {
  58. CSockClient *pSockClient = (CSockClient*)g_PtrAry_SockClient.GetAt(i);
  59. if ( pSockClient && pSockClient->m_dwID == dwID )
  60. {
  61. g_CSFor_PtrAry_SockClient.Unlock ();
  62. return pSockClient;
  63. }
  64. }
  65. printf ( "Can't find ID:%un", dwID );
  66. g_CSFor_PtrAry_SockClient.Unlock ();
  67. return NULL;
  68. }
  69. void DeleteNullSocketClient ()
  70. {
  71. g_bDeleteNullSockClient = FALSE;
  72. g_CSFor_PtrAry_SockClient.Lock ();
  73. for ( int i=0; i<g_PtrAry_SockClient.GetSize(); i++ )
  74. {
  75. CSockClient *pSockClient = (CSockClient*)g_PtrAry_SockClient.GetAt(i);
  76. if ( pSockClient && pSockClient->m_dwID == 0 )
  77. {
  78. delete pSockClient;
  79. g_PtrAry_SockClient.RemoveAt(i);
  80. i --;
  81. }
  82. }
  83. printf ( "Current SocketClient array count is %dn", g_PtrAry_SockClient.GetSize() );
  84. g_CSFor_PtrAry_SockClient.Unlock ();
  85. }
  86. DWORD WINAPI ThreadProc_SockClient(
  87.   LPVOID lpParameter   // thread data
  88. )
  89. {
  90. CSockClient *pSockClient = reinterpret_cast<CSockClient*>(lpParameter);
  91. if ( pSockClient )
  92. return pSockClient->ThreadProc_SockClient ();
  93. return TRUE;
  94. }
  95. /////////////////////////////////////////////////////////////////////////////
  96. // CSockClient
  97. CSockClient::CSockClient()
  98. : m_dwID ( 0 )
  99. , m_nPeerPort ( 0 )
  100. , m_hThread ( NULL )
  101. , m_hEvtEndModule ( NULL )
  102. , m_hEvtWaitClientBHole ( NULL )
  103. , m_dwThreadID ( 0 )
  104. , m_bMainConn ( FALSE )
  105. {
  106. }
  107. CSockClient::~CSockClient()
  108. {
  109. this->CancelBlockingCall ();
  110. if ( HANDLE_IS_VALID(m_hEvtEndModule) )
  111. ::SetEvent ( m_hEvtEndModule );
  112. WaitForThreadEnd ( &m_hThread );
  113. }
  114. // Do not edit the following lines, which are needed by ClassWizard.
  115. #if 0
  116. BEGIN_MESSAGE_MAP(CSockClient, CSocket)
  117. //{{AFX_MSG_MAP(CSockClient)
  118. //}}AFX_MSG_MAP
  119. END_MESSAGE_MAP()
  120. #endif // 0
  121. /////////////////////////////////////////////////////////////////////////////
  122. // CSockClient member functions
  123. //
  124. // 和客户端建立连接
  125. //
  126. BOOL CSockClient::EstablishConnect(SOCKET hSock, SOCKADDR_IN &sockAddr, BOOL bMainConn)
  127. {
  128. ASSERT ( HANDLE_IS_VALID(hSock) );
  129. m_csPeerAddress = inet_ntoa(sockAddr.sin_addr);
  130. m_nPeerPort = ntohs(sockAddr.sin_port);
  131. m_hSocket = hSock;
  132. m_bMainConn = bMainConn;
  133. if ( m_bMainConn )
  134. {
  135. if ( !SendNewUserLoginNotifyToAll ( m_csPeerAddress, m_nPeerPort, m_dwID ) )
  136. return FALSE;
  137. printf ( "[MAIN SOCKET] New connection ( %s:%d:%u ) come inn", m_csPeerAddress, m_nPeerPort, m_dwID );
  138. }
  139. else
  140. {
  141. printf ( "<HOLE SOCKET> New connection ( %s:%d:%u ) come inn", m_csPeerAddress, m_nPeerPort, m_dwID );
  142. }
  143. // 创建一个线程来处理该客户端的所有数据通信
  144. ASSERT ( m_hEvtEndModule == NULL && m_hEvtWaitClientBHole == NULL && m_hThread == NULL );
  145. m_hEvtEndModule = ::CreateEvent ( NULL, TRUE, FALSE, NULL );
  146. m_hEvtWaitClientBHole = ::CreateEvent ( NULL, FALSE, FALSE, NULL );
  147. m_hThread = ::CreateThread ( NULL, 0, ::ThreadProc_SockClient, this, 0, &m_dwThreadID );
  148. if ( !HANDLE_IS_VALID(m_hThread) || !HANDLE_IS_VALID(m_hEvtEndModule) || !HANDLE_IS_VALID(m_hEvtWaitClientBHole) )
  149. return FALSE;
  150. return TRUE;
  151. }
  152. //
  153. // 将其他新登录的客户端信息发送给该客户端
  154. //
  155. BOOL CSockClient::SendNewUserLoginNotify(LPCTSTR lpszClientIP, UINT nClientPort, DWORD dwID)
  156. {
  157. ASSERT ( lpszClientIP && nClientPort > 0 );
  158. if ( !m_bMainConn ) return TRUE;
  159. t_NewUserLoginPkt NewUserLoginPkt;
  160. STRNCPY_SZ ( NewUserLoginPkt.szClientIP, lpszClientIP );
  161. NewUserLoginPkt.nClientPort = nClientPort;
  162. NewUserLoginPkt.dwID = dwID;
  163. printf ( "Send new user login notify to (%s:%u:%u)n", m_csPeerAddress, m_nPeerPort, m_dwID );
  164. return ( SendChunk ( &NewUserLoginPkt, sizeof(t_NewUserLoginPkt), 0 ) == sizeof(t_NewUserLoginPkt) );
  165. }
  166. BOOL CSockClient::ThreadProc_SockClient()
  167. {
  168. printf ( "Client.%u thread start.n", m_dwID );
  169. BOOL bRet = FALSE;
  170. char szRecvBuffer[NET_BUFFER_SIZE] = {0};
  171. int nRecvBytes = 0;
  172. // WinSocket 的句柄是不能跨线程访问的,所以这里重新附加进来
  173. if ( !Attach ( m_hSocket ) )
  174. goto finished;
  175. // 发送欢迎信息,通知客户端连接成功了。
  176. SendWelcomeInfo ();
  177. // 循环接收网络数据
  178. while ( TRUE )
  179. {
  180. nRecvBytes = Receive ( szRecvBuffer, sizeof(szRecvBuffer) );
  181. if ( nRecvBytes > 0 )
  182. {
  183. if ( !HandleDataReceived ( szRecvBuffer, nRecvBytes ) )
  184. goto finished;
  185. }
  186. else if ( (nRecvBytes == 0 && GetLastError() != NO_ERROR) || (SOCKET_ERROR == nRecvBytes && GetLastError() == WSAEWOULDBLOCK) )
  187. {
  188. SLEEP_BREAK ( 10 );
  189. }
  190. // 对方断开连接了
  191. else
  192. {
  193. goto finished;
  194. }
  195. SLEEP_BREAK ( 1 );
  196. }
  197. bRet = TRUE;
  198. finished:
  199. Close ();
  200. printf ( "Client.%u thread end. result : %sn", m_dwID, bRet?"SCUESS":"FAILED" );
  201. if ( HANDLE_IS_VALID(m_hEvtEndModule) )
  202. ::SetEvent ( m_hEvtEndModule );
  203. m_dwID = 0;
  204. printf ( "ThreadProc_SockClient endn" );
  205. g_bDeleteNullSockClient = TRUE;
  206. return bRet;
  207. }
  208. BOOL CSockClient::HandleDataReceived(char *data, int size)
  209. {
  210. if ( !data || size < 4 ) return FALSE;
  211. PACKET_TYPE *pePacketType = (PACKET_TYPE*)data;
  212. ASSERT ( pePacketType );
  213. switch ( *pePacketType )
  214. {
  215. // 要求与另一个客户端建立直接的TCP连接
  216. case PACKET_TYPE_REQUEST_CONN_CLIENT:
  217. {
  218. ASSERT ( !m_bMainConn );
  219. ASSERT ( size == sizeof(t_ReqConnClientPkt) );
  220. t_ReqConnClientPkt *pReqConnClientPkt = (t_ReqConnClientPkt*)data;
  221. if ( !Handle_ReqConnClientPkt ( pReqConnClientPkt ) )
  222. return FALSE;
  223. break;
  224. }
  225. // 被动端(客户端B)请求服务器断开连接,这个时候应该将客户端B的外部IP和端口号告诉客户端A,并让客户端A主动
  226. // 连接客户端B的外部IP和端口号
  227. case PACKET_TYPE_REQUEST_DISCONNECT:
  228. {
  229. ASSERT ( !m_bMainConn );
  230. ASSERT ( size == sizeof(t_ReqSrvDisconnectPkt) );
  231. t_ReqSrvDisconnectPkt *pReqSrvDisconnectPkt = (t_ReqSrvDisconnectPkt*)data;
  232. ASSERT ( pReqSrvDisconnectPkt );
  233. printf ( "Clinet.%u request disconnectn", m_dwID );
  234. CSockClient *pSockClientHole_A = FindSocketClient ( pReqSrvDisconnectPkt->dwInviterHoleID );
  235. if ( !pSockClientHole_A ) return FALSE;
  236. pSockClientHole_A->m_SrvReqDirectConnectPkt.dwInvitedID = pReqSrvDisconnectPkt->dwInvitedID;
  237. STRNCPY_CS ( pSockClientHole_A->m_SrvReqDirectConnectPkt.szInvitedIP, m_csPeerAddress );
  238. pSockClientHole_A->m_SrvReqDirectConnectPkt.nInvitedPort = m_nPeerPort;
  239. Close ();
  240. break;
  241. }
  242. // 被动端(客户端B)打洞和侦听均已准备就绪
  243. case PACKET_TYPE_HOLE_LISTEN_READY:
  244. {
  245. ASSERT ( m_bMainConn );
  246. ASSERT ( size == sizeof(t_HoleListenReadyPkt) );
  247. t_HoleListenReadyPkt *pHoleListenReadyPkt = (t_HoleListenReadyPkt*)data;
  248. ASSERT ( pHoleListenReadyPkt );
  249. printf ( "Client.%u hole and listen readyn", pHoleListenReadyPkt->dwInvitedID );
  250. // 通知正在与客户端A通信的服务器线程,以将客户端B的外部IP地址和端口号告诉客户端A
  251. CSockClient *pSockClientHole_A = FindSocketClient ( pHoleListenReadyPkt->dwInviterHoleID );
  252. if ( !pSockClientHole_A ) return FALSE;
  253. if ( HANDLE_IS_VALID(pSockClientHole_A->m_hEvtWaitClientBHole) )
  254. SetEvent ( pSockClientHole_A->m_hEvtWaitClientBHole );
  255. break;
  256. }
  257. }
  258. return TRUE;
  259. }
  260. //
  261. // 客户端A请求我(服务器)协助连接客户端B,这个包应该在打洞Socket中收到
  262. //
  263. BOOL CSockClient::Handle_ReqConnClientPkt(t_ReqConnClientPkt *pReqConnClientPkt)
  264. {
  265. ASSERT ( !m_bMainConn );
  266. CSockClient *pSockClient_B = FindSocketClient ( pReqConnClientPkt->dwInvitedID );
  267. if ( !pSockClient_B ) return FALSE;
  268. printf ( "%s:%u:%u invite %s:%u:%u connectionn", m_csPeerAddress, m_nPeerPort, m_dwID,
  269. pSockClient_B->m_csPeerAddress, pSockClient_B->m_nPeerPort, pSockClient_B->m_dwID );
  270. // 客户端A想要和客户端B建立直接的TCP连接,服务器负责将A的外部IP和端口号告诉给B
  271. t_SrvReqMakeHolePkt SrvReqMakeHolePkt;
  272. SrvReqMakeHolePkt.dwInviterID = pReqConnClientPkt->dwInviterID;
  273. SrvReqMakeHolePkt.dwInviterHoleID = m_dwID;
  274. SrvReqMakeHolePkt.dwInvitedID = pReqConnClientPkt->dwInvitedID;
  275. STRNCPY_CS ( SrvReqMakeHolePkt.szClientHoleIP, m_csPeerAddress );
  276. SrvReqMakeHolePkt.nClientHolePort = m_nPeerPort;
  277. if ( pSockClient_B->SendChunk ( &SrvReqMakeHolePkt, sizeof(t_SrvReqMakeHolePkt), 0 ) != sizeof(t_SrvReqMakeHolePkt) )
  278. return FALSE;
  279. // 等待客户端B打洞完成,完成以后通知客户端A直接连接客户端外部IP和端口号
  280. if ( !HANDLE_IS_VALID(m_hEvtWaitClientBHole) )
  281. return FALSE;
  282. if ( WaitForSingleObject ( m_hEvtWaitClientBHole, 6000*1000 ) == WAIT_OBJECT_0 ) //d
  283. {
  284. if ( SendChunk ( &m_SrvReqDirectConnectPkt, sizeof(t_SrvReqDirectConnectPkt), 0 ) == sizeof(t_SrvReqDirectConnectPkt) )
  285. return TRUE;
  286. }
  287. return FALSE;
  288. }
  289. BOOL CSockClient::SendWelcomeInfo()
  290. {
  291. if ( !m_bMainConn ) return TRUE;
  292. t_WelcomePkt WelcomePkt;
  293. STRNCPY_CS ( WelcomePkt.szClientIP, m_csPeerAddress );
  294. WelcomePkt.nClientPort = m_nPeerPort;
  295. WelcomePkt.dwID = m_dwID;
  296. _snprintf ( WelcomePkt.szWelcomeInfo, sizeof(WelcomePkt.szWelcomeInfo),
  297. "Hello, ID.%u, Welcome to login", m_dwID );
  298. return ( SendChunk ( &WelcomePkt, sizeof(t_WelcomePkt), 0 ) == sizeof(t_WelcomePkt) );
  299. }