TcpClient.cpp 14 KB

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