UdpCast.cpp 14 KB

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