TcpClient.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. /*
  2. * Copyright: JessMA Open Source (ldcsaa@gmail.com)
  3. *
  4. * Author : Bruce Liang
  5. * Website : https://github.com/ldcsaa
  6. * Project : https://github.com/ldcsaa/HP-Socket
  7. * Blog : http://www.cnblogs.com/ldcsaa
  8. * Wiki : http://www.oschina.net/p/hp-socket
  9. * QQ Group : 44636872, 75375912
  10. *
  11. * Licensed under the Apache License, Version 2.0 (the "License");
  12. * you may not use this file except in compliance with the License.
  13. * You may obtain a copy of the License at
  14. *
  15. * http://www.apache.org/licenses/LICENSE-2.0
  16. *
  17. * Unless required by applicable law or agreed to in writing, software
  18. * distributed under the License is distributed on an "AS IS" BASIS,
  19. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  20. * See the License for the specific language governing permissions and
  21. * limitations under the License.
  22. */
  23. #include "TcpClient.h"
  24. #include "./common/FileHelper.h"
  25. BOOL CTcpClient::Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
  26. {
  27. if(!CheckParams() || !CheckStarting())
  28. return FALSE;
  29. PrepareStart();
  30. m_ccContext.Reset();
  31. BOOL isOK = FALSE;
  32. HP_SOCKADDR addrRemote, addrBind;
  33. if(CreateClientSocket(lpszRemoteAddress, addrRemote, usPort, lpszBindAddress, addrBind))
  34. {
  35. if(BindClientSocket(addrBind, addrRemote, usLocalPort))
  36. {
  37. if(TRIGGER(FirePrepareConnect(m_soClient)) != HR_ERROR)
  38. {
  39. if(ConnectToServer(addrRemote, bAsyncConnect))
  40. {
  41. if(CreateWorkerThread())
  42. isOK = TRUE;
  43. else
  44. SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ERROR_CREATE_FAILED);
  45. }
  46. else
  47. SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
  48. }
  49. else
  50. SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
  51. }
  52. else
  53. SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
  54. }
  55. else
  56. SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
  57. if(!isOK)
  58. {
  59. m_ccContext.Reset(FALSE);
  60. EXECUTE_RESTORE_ERROR(Stop());
  61. }
  62. return isOK;
  63. }
  64. BOOL CTcpClient::CheckParams()
  65. {
  66. if (((int)m_dwSyncConnectTimeout > 0) &&
  67. ((int)m_dwSocketBufferSize > 0) &&
  68. ((int)m_dwFreeBufferPoolSize >= 0) &&
  69. ((int)m_dwFreeBufferPoolHold >= 0) &&
  70. ((int)m_dwKeepAliveTime >= 1000 || m_dwKeepAliveTime == 0) &&
  71. ((int)m_dwKeepAliveInterval >= 1000 || m_dwKeepAliveInterval == 0) )
  72. return TRUE;
  73. SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
  74. return FALSE;
  75. }
  76. void CTcpClient::PrepareStart()
  77. {
  78. m_itPool.SetItemCapacity(m_dwSocketBufferSize);
  79. m_itPool.SetPoolSize(m_dwFreeBufferPoolSize);
  80. m_itPool.SetPoolHold(m_dwFreeBufferPoolHold);
  81. m_itPool.Prepare();
  82. }
  83. BOOL CTcpClient::CheckStarting()
  84. {
  85. CSpinLock locallock(m_csState);
  86. if(m_enState == SS_STOPPED)
  87. m_enState = SS_STARTING;
  88. else
  89. {
  90. SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
  91. return FALSE;
  92. }
  93. return TRUE;
  94. }
  95. BOOL CTcpClient::CheckStoping()
  96. {
  97. if(m_enState != SS_STOPPED)
  98. {
  99. CSpinLock locallock(m_csState);
  100. if(HasStarted())
  101. {
  102. m_enState = SS_STOPPING;
  103. return TRUE;
  104. }
  105. }
  106. SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
  107. return FALSE;
  108. }
  109. BOOL CTcpClient::CreateClientSocket(LPCTSTR lpszRemoteAddress, HP_SOCKADDR& addrRemote, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& addrBind)
  110. {
  111. HP_SCOPE_HOST host(lpszRemoteAddress);
  112. if(!::GetSockAddrByHostName(host.addr, usPort, addrRemote))
  113. return FALSE;
  114. if(::IsStrNotEmpty(lpszBindAddress))
  115. {
  116. if(!::sockaddr_A_2_IN(lpszBindAddress, 0, addrBind))
  117. return FALSE;
  118. if(addrRemote.family != addrBind.family)
  119. {
  120. ::WSASetLastError(ERROR_AFNOSUPPORT);
  121. return FALSE;
  122. }
  123. }
  124. m_soClient = socket(addrRemote.family, SOCK_STREAM, IPPROTO_TCP);
  125. if(m_soClient == INVALID_SOCKET)
  126. return FALSE;
  127. BOOL bOnOff = (m_dwKeepAliveTime > 0 && m_dwKeepAliveInterval > 0);
  128. VERIFY(::SSO_KeepAliveVals(m_soClient, bOnOff, m_dwKeepAliveTime, m_dwKeepAliveInterval) == NO_ERROR);
  129. VERIFY(::SSO_ReuseAddress(m_soClient, m_enReusePolicy) == NO_ERROR);
  130. VERIFY(::SSO_NoDelay(m_soClient, m_bNoDelay) == NO_ERROR);
  131. SetRemoteHost(host.name, usPort);
  132. return TRUE;
  133. }
  134. BOOL CTcpClient::BindClientSocket(const HP_SOCKADDR& addrBind, const HP_SOCKADDR& addrRemote, USHORT usLocalPort)
  135. {
  136. if(addrBind.IsSpecified() && usLocalPort == 0)
  137. {
  138. if(::bind(m_soClient, addrBind.Addr(), addrBind.AddrSize()) == SOCKET_ERROR)
  139. return FALSE;
  140. }
  141. else if(usLocalPort != 0)
  142. {
  143. HP_SOCKADDR realBindAddr = addrBind.IsSpecified() ? addrBind : HP_SOCKADDR::AnyAddr(addrRemote.family);
  144. realBindAddr.SetPort(usLocalPort);
  145. if(::bind(m_soClient, realBindAddr.Addr(), realBindAddr.AddrSize()) == SOCKET_ERROR)
  146. return FALSE;
  147. }
  148. m_dwConnID = ::GenerateConnectionID();
  149. return TRUE;
  150. }
  151. BOOL CTcpClient::ConnectToServer(const HP_SOCKADDR& addrRemote, BOOL bAsyncConnect)
  152. {
  153. BOOL isOK = FALSE;
  154. VERIFY(::fcntl_SETFL(m_soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
  155. int rc = ::connect(m_soClient, addrRemote.Addr(), addrRemote.AddrSize());
  156. if(IS_NO_ERROR(rc) || IS_IO_PENDING_ERROR())
  157. {
  158. if(bAsyncConnect)
  159. {
  160. m_nEvents = POLLOUT;
  161. isOK = TRUE;
  162. }
  163. else
  164. {
  165. if(IS_HAS_ERROR(rc))
  166. rc = ::WaitForSocketWrite(m_soClient, m_dwSyncConnectTimeout);
  167. if(!IS_NO_ERROR(rc))
  168. ::WSASetLastError(rc);
  169. else
  170. {
  171. SetConnected();
  172. if(TRIGGER(FireConnect()) == HR_ERROR)
  173. ::WSASetLastError(ENSURE_ERROR_CANCELLED);
  174. else
  175. {
  176. m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
  177. isOK = TRUE;
  178. }
  179. }
  180. }
  181. }
  182. return isOK;
  183. }
  184. BOOL CTcpClient::Stop()
  185. {
  186. if(!CheckStoping())
  187. return FALSE;
  188. WaitForWorkerThreadEnd();
  189. SetConnected(FALSE);
  190. if(m_ccContext.bFireOnClose)
  191. FireClose(m_ccContext.enOperation, m_ccContext.iErrorCode);
  192. if(m_soClient != INVALID_SOCKET)
  193. {
  194. shutdown(m_soClient, SHUT_WR);
  195. closesocket(m_soClient);
  196. m_soClient = INVALID_SOCKET;
  197. }
  198. Reset();
  199. return TRUE;
  200. }
  201. void CTcpClient::Reset()
  202. {
  203. CCriSecLock locallock(m_csSend);
  204. m_evSend.Reset();
  205. m_evRecv.Reset();
  206. m_evStop.Reset();
  207. m_lsSend.Clear();
  208. m_itPool.Clear();
  209. m_rcBuffer.Free();
  210. m_strHost.Empty();
  211. m_usPort = 0;
  212. m_nEvents = 0;
  213. m_bPaused = FALSE;
  214. m_enState = SS_STOPPED;
  215. m_evWait.SyncNotifyAll();
  216. }
  217. void CTcpClient::WaitForWorkerThreadEnd()
  218. {
  219. if(!m_thWorker.IsRunning())
  220. return;
  221. if(m_thWorker.IsInMyThread())
  222. m_thWorker.Detach();
  223. else
  224. {
  225. m_evStop.Set();
  226. m_thWorker.Join();
  227. }
  228. }
  229. BOOL CTcpClient::CreateWorkerThread()
  230. {
  231. return m_thWorker.Start(this, &CTcpClient::WorkerThreadProc);
  232. }
  233. UINT WINAPI CTcpClient::WorkerThreadProc(LPVOID pv)
  234. {
  235. ::SetCurrentWorkerThreadName();
  236. TRACE("---------------> Client Worker Thread 0x%08X started <---------------", SELF_THREAD_ID);
  237. OnWorkerThreadStart(SELF_THREAD_ID);
  238. BOOL bCallStop = TRUE;
  239. pollfd pfds[] = { {m_soClient, m_nEvents},
  240. {m_evSend.GetFD(), POLLIN},
  241. {m_evRecv.GetFD(), POLLIN},
  242. {m_evStop.GetFD(), POLLIN} };
  243. int size = ARRAY_SIZE(pfds);
  244. m_rcBuffer.Malloc(m_dwSocketBufferSize);
  245. while(HasStarted())
  246. {
  247. int rs = (int)::PollForMultipleObjects(pfds, size);
  248. ASSERT(rs > TIMEOUT);
  249. if(rs <= 0)
  250. {
  251. m_ccContext.Reset(TRUE, SO_UNKNOWN, ::WSAGetLastError());
  252. goto EXIT_WORKER_THREAD;
  253. }
  254. for(int i = 0; i < size; i++)
  255. {
  256. if((1 << i) & rs)
  257. {
  258. SHORT revents = pfds[i].revents;
  259. if(i == 0)
  260. {
  261. if(!ProcessNetworkEvent(revents))
  262. goto EXIT_WORKER_THREAD;
  263. }
  264. else if(i == 1)
  265. {
  266. m_evSend.Reset();
  267. if(!SendData())
  268. goto EXIT_WORKER_THREAD;
  269. }
  270. else if(i == 2)
  271. {
  272. m_evRecv.Reset();
  273. if(!BeforeUnpause())
  274. goto EXIT_WORKER_THREAD;
  275. if(!ReadData())
  276. goto EXIT_WORKER_THREAD;
  277. }
  278. else if(i == 3)
  279. {
  280. m_evStop.Reset();
  281. bCallStop = FALSE;
  282. goto EXIT_WORKER_THREAD;
  283. }
  284. else
  285. VERIFY(FALSE);
  286. }
  287. }
  288. m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
  289. pfds[0].events = m_nEvents;
  290. }
  291. EXIT_WORKER_THREAD:
  292. OnWorkerThreadEnd(SELF_THREAD_ID);
  293. if(bCallStop && HasStarted())
  294. Stop();
  295. TRACE("---------------> Client Worker Thread 0x%08X stoped <---------------", SELF_THREAD_ID);
  296. return 0;
  297. }
  298. BOOL CTcpClient::ProcessNetworkEvent(SHORT events)
  299. {
  300. BOOL bContinue = TRUE;
  301. if(bContinue && events & POLLERR)
  302. bContinue = HandleClose(events);
  303. if(bContinue && !IsConnected())
  304. bContinue = HandleConnect(events);
  305. if(bContinue && events & POLLIN)
  306. bContinue = HandleRead(events);
  307. if(bContinue && events & POLLOUT)
  308. bContinue = HandleWrite(events);
  309. if(bContinue && events & _POLL_HUNGUP_EVENTS)
  310. bContinue = HandleClose(events);
  311. return bContinue;
  312. }
  313. BOOL CTcpClient::HandleConnect(SHORT events)
  314. {
  315. ASSERT(events & POLLOUT);
  316. int code = ::SSO_GetError(m_soClient);
  317. if(!IS_NO_ERROR(code) || (events & _POLL_ERROR_EVENTS))
  318. {
  319. m_ccContext.Reset(TRUE, SO_CONNECT, code);
  320. return FALSE;
  321. }
  322. if(events & _POLL_HUNGUP_EVENTS)
  323. {
  324. m_ccContext.Reset(TRUE, SO_CONNECT, NO_ERROR);
  325. return FALSE;
  326. }
  327. SetConnected();
  328. if(TRIGGER(FireConnect()) == HR_ERROR)
  329. {
  330. m_ccContext.Reset(FALSE);
  331. return FALSE;
  332. }
  333. return TRUE;
  334. }
  335. BOOL CTcpClient::HandleClose(SHORT events)
  336. {
  337. EnSocketOperation enOperation = SO_CLOSE;
  338. if(events & _POLL_HUNGUP_EVENTS)
  339. enOperation = SO_CLOSE;
  340. else if(events & POLLIN)
  341. enOperation = SO_RECEIVE;
  342. else if(events & POLLOUT)
  343. enOperation = SO_SEND;
  344. m_ccContext.Reset(TRUE, enOperation, ::SSO_GetError(m_soClient));
  345. return FALSE;
  346. }
  347. BOOL CTcpClient::HandleRead(SHORT events)
  348. {
  349. return ReadData();
  350. }
  351. BOOL CTcpClient::HandleWrite(SHORT events)
  352. {
  353. return SendData();
  354. }
  355. BOOL CTcpClient::ReadData()
  356. {
  357. while(TRUE)
  358. {
  359. if(m_bPaused)
  360. break;
  361. int rc = (int)read(m_soClient, (char*)(BYTE*)m_rcBuffer, m_dwSocketBufferSize);
  362. if(rc > 0)
  363. {
  364. if(TRIGGER(FireReceive(m_rcBuffer, rc)) == HR_ERROR)
  365. {
  366. TRACE("<C-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", m_dwConnID);
  367. m_ccContext.Reset(TRUE, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
  368. return FALSE;
  369. }
  370. }
  371. else if(rc == SOCKET_ERROR)
  372. {
  373. int code = ::WSAGetLastError();
  374. if(code == ERROR_WOULDBLOCK)
  375. break;
  376. else
  377. {
  378. m_ccContext.Reset(TRUE, SO_RECEIVE, code);
  379. return FALSE;
  380. }
  381. }
  382. else if(rc == 0)
  383. {
  384. m_ccContext.Reset(TRUE, SO_CLOSE, SE_OK);
  385. return FALSE;
  386. }
  387. else
  388. ASSERT(FALSE);
  389. }
  390. return TRUE;
  391. }
  392. BOOL CTcpClient::PauseReceive(BOOL bPause)
  393. {
  394. if(!IsConnected())
  395. {
  396. ::SetLastError(ERROR_INVALID_STATE);
  397. return FALSE;
  398. }
  399. if(m_bPaused == bPause)
  400. return TRUE;
  401. m_bPaused = bPause;
  402. if(!bPause)
  403. return m_evRecv.Set();
  404. return TRUE;
  405. }
  406. BOOL CTcpClient::SendData()
  407. {
  408. BOOL bBlocked = FALSE;
  409. while(m_lsSend.Length() > 0)
  410. {
  411. TItemPtr itPtr(m_itPool);
  412. {
  413. CCriSecLock locallock(m_csSend);
  414. itPtr = m_lsSend.PopFront();
  415. }
  416. if(!itPtr.IsValid())
  417. break;
  418. ASSERT(!itPtr->IsEmpty());
  419. if(!DoSendData(itPtr, bBlocked))
  420. return FALSE;
  421. if(bBlocked)
  422. {
  423. ASSERT(!itPtr->IsEmpty());
  424. CCriSecLock locallock(m_csSend);
  425. m_lsSend.PushFront(itPtr.Detach());
  426. break;
  427. }
  428. }
  429. return TRUE;
  430. }
  431. BOOL CTcpClient::DoSendData(TItem* pItem, BOOL& bBlocked)
  432. {
  433. while(!pItem->IsEmpty())
  434. {
  435. int rc = (int)write(m_soClient, (char*)pItem->Ptr(), pItem->Size());
  436. if(rc > 0)
  437. {
  438. if(TRIGGER(FireSend(pItem->Ptr(), rc)) == HR_ERROR)
  439. {
  440. TRACE("<C-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", m_dwConnID);
  441. ASSERT(FALSE);
  442. }
  443. pItem->Reduce(rc);
  444. }
  445. else if(rc == SOCKET_ERROR)
  446. {
  447. int code = ::WSAGetLastError();
  448. if(code == ERROR_WOULDBLOCK)
  449. {
  450. bBlocked = TRUE;
  451. break;
  452. }
  453. else
  454. {
  455. m_ccContext.Reset(TRUE, SO_SEND, code);
  456. return FALSE;
  457. }
  458. }
  459. else
  460. ASSERT(FALSE);
  461. }
  462. return TRUE;
  463. }
  464. BOOL CTcpClient::Send(const BYTE* pBuffer, int iLength, int iOffset)
  465. {
  466. ASSERT(pBuffer && iLength > 0);
  467. if(iOffset != 0) pBuffer += iOffset;
  468. WSABUF buffer;
  469. buffer.len = iLength;
  470. buffer.buf = (BYTE*)pBuffer;
  471. return SendPackets(&buffer, 1);
  472. }
  473. BOOL CTcpClient::DoSendPackets(const WSABUF pBuffers[], int iCount)
  474. {
  475. ASSERT(pBuffers && iCount > 0);
  476. int result = NO_ERROR;
  477. if(pBuffers && iCount > 0)
  478. {
  479. if(IsConnected())
  480. {
  481. CCriSecLock locallock(m_csSend);
  482. if(IsConnected())
  483. result = SendInternal(pBuffers, iCount);
  484. else
  485. result = ERROR_INVALID_STATE;
  486. }
  487. else
  488. result = ERROR_INVALID_STATE;
  489. }
  490. else
  491. result = ERROR_INVALID_PARAMETER;
  492. if(result != NO_ERROR)
  493. ::SetLastError(result);
  494. return (result == NO_ERROR);
  495. }
  496. int CTcpClient::SendInternal(const WSABUF pBuffers[], int iCount)
  497. {
  498. ASSERT(m_lsSend.Length() >= 0);
  499. int iPending = m_lsSend.Length();
  500. for(int i = 0; i < iCount; i++)
  501. {
  502. int iBufLen = pBuffers[i].len;
  503. if(iBufLen > 0)
  504. {
  505. BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
  506. ASSERT(pBuffer);
  507. m_lsSend.Cat(pBuffer, iBufLen);
  508. ASSERT(m_lsSend.Length() > 0);
  509. }
  510. }
  511. if(iPending == 0 && m_lsSend.Length() > 0) m_evSend.Set();
  512. return NO_ERROR;
  513. }
  514. BOOL CTcpClient::SendSmallFile(LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail)
  515. {
  516. CFile file;
  517. CFileMapping fmap;
  518. WSABUF szBuf[3];
  519. HRESULT hr = ::MakeSmallFilePackage(lpszFileName, file, fmap, szBuf, pHead, pTail);
  520. if(FAILED(hr))
  521. {
  522. ::SetLastError(hr);
  523. return FALSE;
  524. }
  525. return SendPackets(szBuf, 3);
  526. }
  527. void CTcpClient::SetLastError(EnSocketError code, LPCSTR func, int ec)
  528. {
  529. TRACE("%s --> Error: %d, EC: %d", func, code, ec);
  530. m_enLastError = code;
  531. ::SetLastError(ec);
  532. }
  533. BOOL CTcpClient::GetLocalAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
  534. {
  535. ASSERT(lpszAddress != nullptr && iAddressLen > 0);
  536. return ::GetSocketLocalAddress(m_soClient, lpszAddress, iAddressLen, usPort);
  537. }
  538. void CTcpClient::SetRemoteHost(LPCTSTR lpszHost, USHORT usPort)
  539. {
  540. m_strHost = lpszHost;
  541. m_usPort = usPort;
  542. }
  543. BOOL CTcpClient::GetRemoteHost(TCHAR lpszHost[], int& iHostLen, USHORT& usPort)
  544. {
  545. BOOL isOK = FALSE;
  546. if(m_strHost.IsEmpty())
  547. return isOK;
  548. int iLen = m_strHost.GetLength() + 1;
  549. if(iHostLen >= iLen)
  550. {
  551. memcpy(lpszHost, CA2CT(m_strHost), iLen * sizeof(TCHAR));
  552. usPort = m_usPort;
  553. isOK = TRUE;
  554. }
  555. iHostLen = iLen;
  556. return isOK;
  557. }
  558. BOOL CTcpClient::GetRemoteHost(LPCSTR* lpszHost, USHORT* pusPort)
  559. {
  560. *lpszHost = m_strHost;
  561. if(pusPort != nullptr)
  562. *pusPort = m_usPort;
  563. return !m_strHost.IsEmpty();
  564. }