OpenShot Audio Library | OpenShotAudio  0.6.0
juce_Socket.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 #if ! JUCE_WASM
27 
28 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4127 4389 4018)
29 
30 #ifndef AI_NUMERICSERV // (missing in older Mac SDKs)
31  #define AI_NUMERICSERV 0x1000
32 #endif
33 
34 #if JUCE_WINDOWS
35  using juce_socklen_t = int;
36  using juce_recvsend_size_t = int;
37  using SocketHandle = SOCKET;
38  static const SocketHandle invalidSocket = INVALID_SOCKET;
39 #elif JUCE_ANDROID
40  using juce_socklen_t = socklen_t;
41  using juce_recvsend_size_t = size_t;
42  using SocketHandle = int;
43  static const SocketHandle invalidSocket = -1;
44 #else
45  using juce_socklen_t = socklen_t;
46  using juce_recvsend_size_t = socklen_t;
47  using SocketHandle = int;
48  static const SocketHandle invalidSocket = -1;
49 #endif
50 
51 //==============================================================================
52 namespace SocketHelpers
53 {
54  static void initSockets()
55  {
56  #if JUCE_WINDOWS
57  static bool socketsStarted = false;
58 
59  if (! socketsStarted)
60  {
61  WSADATA wsaData;
62  const WORD wVersionRequested = MAKEWORD (1, 1);
63  socketsStarted = WSAStartup (wVersionRequested, &wsaData) == 0;
64  }
65  #endif
66  }
67 
68  inline bool isValidPortNumber (int port) noexcept
69  {
70  return isPositiveAndBelow (port, 65536);
71  }
72 
73  template <typename Type>
74  static bool setOption (SocketHandle handle, int mode, int property, Type value) noexcept
75  {
76  return setsockopt (handle, mode, property, reinterpret_cast<const char*> (&value), sizeof (value)) == 0;
77  }
78 
79  template <typename Type>
80  static bool setOption (SocketHandle handle, int property, Type value) noexcept
81  {
82  return setOption (handle, SOL_SOCKET, property, value);
83  }
84 
85  static std::optional<int> getBufferSize (SocketHandle handle, int property)
86  {
87  int result;
88  auto outParamSize = (socklen_t) sizeof (result);
89 
90  if (getsockopt (handle, SOL_SOCKET, property, reinterpret_cast<char*> (&result), &outParamSize) != 0
91  || outParamSize != (socklen_t) sizeof (result))
92  {
93  return std::nullopt;
94  }
95 
96  return result;
97  }
98 
99  static bool resetSocketOptions (SocketHandle handle, bool isDatagram, bool allowBroadcast, const SocketOptions& options) noexcept
100  {
101  auto getCurrentBufferSizeWithMinimum = [handle] (int property)
102  {
103  constexpr auto minBufferSize = 65536;
104 
105  if (auto currentBufferSize = getBufferSize (handle, property))
106  return std::max (*currentBufferSize, minBufferSize);
107 
108  return minBufferSize;
109  };
110 
111  const auto receiveBufferSize = options.getReceiveBufferSize().value_or (getCurrentBufferSizeWithMinimum (SO_RCVBUF));
112  const auto sendBufferSize = options.getSendBufferSize() .value_or (getCurrentBufferSizeWithMinimum (SO_SNDBUF));
113 
114  return handle != invalidSocket
115  && setOption (handle, SO_RCVBUF, receiveBufferSize)
116  && setOption (handle, SO_SNDBUF, sendBufferSize)
117  && (isDatagram ? ((! allowBroadcast) || setOption (handle, SO_BROADCAST, (int) 1))
118  : setOption (handle, IPPROTO_TCP, TCP_NODELAY, (int) 1));
119  }
120 
121  static void closeSocket (std::atomic<int>& handle,
122  [[maybe_unused]] CriticalSection& readLock,
123  [[maybe_unused]] bool isListener,
124  [[maybe_unused]] int portNumber,
125  std::atomic<bool>& connected) noexcept
126  {
127  const auto h = (SocketHandle) handle.load();
128  handle = -1;
129 
130  #if JUCE_WINDOWS
131  if (h != invalidSocket || connected)
132  closesocket (h);
133 
134  // make sure any read process finishes before we delete the socket
135  CriticalSection::ScopedLockType lock (readLock);
136  connected = false;
137  #else
138  if (connected)
139  {
140  connected = false;
141 
142  if (isListener)
143  {
144  // need to do this to interrupt the accept() function..
145  StreamingSocket temp;
146  temp.connect (IPAddress::local().toString(), portNumber, 1000);
147  }
148  }
149 
150  if (h >= 0)
151  {
152  // unblock any pending read requests
153  ::shutdown (h, SHUT_RDWR);
154 
155  {
156  // see man-page of recv on linux about a race condition where the
157  // shutdown command is lost if the receiving thread does not have
158  // a chance to process before close is called. On Mac OS X shutdown
159  // does not unblock a select call, so using a lock here will dead-lock
160  // both threads.
161  #if JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
162  CriticalSection::ScopedLockType lock (readLock);
163  ::close (h);
164  #else
165  ::close (h);
166  CriticalSection::ScopedLockType lock (readLock);
167  #endif
168  }
169  }
170  #endif
171  }
172 
173  static bool bindSocket (SocketHandle handle, int port, const String& address) noexcept
174  {
175  if (handle == invalidSocket || ! isValidPortNumber (port))
176  return false;
177 
178  struct sockaddr_in addr;
179  zerostruct (addr); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
180 
181  addr.sin_family = PF_INET;
182  addr.sin_port = htons ((uint16) port);
183  addr.sin_addr.s_addr = address.isNotEmpty() ? ::inet_addr (address.toRawUTF8())
184  : htonl (INADDR_ANY);
185 
186  return ::bind (handle, (struct sockaddr*) &addr, sizeof (addr)) >= 0;
187  }
188 
189  static int getBoundPort (SocketHandle handle) noexcept
190  {
191  if (handle != invalidSocket)
192  {
193  struct sockaddr_in addr;
194  socklen_t len = sizeof (addr);
195 
196  if (getsockname (handle, (struct sockaddr*) &addr, &len) == 0)
197  return ntohs (addr.sin_port);
198  }
199 
200  return -1;
201  }
202 
203  static String getConnectedAddress (SocketHandle handle) noexcept
204  {
205  struct sockaddr_in addr;
206  socklen_t len = sizeof (addr);
207 
208  if (getpeername (handle, (struct sockaddr*) &addr, &len) >= 0)
209  return inet_ntoa (addr.sin_addr);
210 
211  return "0.0.0.0";
212  }
213 
214  static bool setSocketBlockingState (SocketHandle handle, bool shouldBlock) noexcept
215  {
216  #if JUCE_WINDOWS
217  u_long nonBlocking = shouldBlock ? 0 : (u_long) 1;
218  return ioctlsocket (handle, (long) FIONBIO, &nonBlocking) == 0;
219  #else
220  int socketFlags = fcntl (handle, F_GETFL, 0);
221 
222  if (socketFlags == -1)
223  return false;
224 
225  if (shouldBlock)
226  socketFlags &= ~O_NONBLOCK;
227  else
228  socketFlags |= O_NONBLOCK;
229 
230  return fcntl (handle, F_SETFL, socketFlags) == 0;
231  #endif
232  }
233 
234  #if ! JUCE_WINDOWS
235  static bool getSocketBlockingState (SocketHandle handle)
236  {
237  return (fcntl (handle, F_GETFL, 0) & O_NONBLOCK) == 0;
238  }
239  #endif
240 
241  static int readSocket (SocketHandle handle,
242  void* destBuffer, int maxBytesToRead,
243  std::atomic<bool>& connected,
244  bool blockUntilSpecifiedAmountHasArrived,
245  CriticalSection& readLock,
246  String* senderIP = nullptr,
247  int* senderPort = nullptr) noexcept
248  {
249  #if ! JUCE_WINDOWS
250  if (blockUntilSpecifiedAmountHasArrived != getSocketBlockingState (handle))
251  #endif
252  setSocketBlockingState (handle, blockUntilSpecifiedAmountHasArrived);
253 
254  int bytesRead = 0;
255 
256  while (bytesRead < maxBytesToRead)
257  {
258  long bytesThisTime = -1;
259  auto buffer = static_cast<char*> (destBuffer) + bytesRead;
260  auto numToRead = (juce_recvsend_size_t) (maxBytesToRead - bytesRead);
261 
262  {
263  // avoid race-condition
264  CriticalSection::ScopedTryLockType lock (readLock);
265 
266  if (lock.isLocked())
267  {
268  if (senderIP == nullptr || senderPort == nullptr)
269  {
270  bytesThisTime = ::recv (handle, buffer, numToRead, 0);
271  }
272  else
273  {
274  sockaddr_in client;
275  socklen_t clientLen = sizeof (sockaddr);
276 
277  bytesThisTime = ::recvfrom (handle, buffer, numToRead, 0, (sockaddr*) &client, &clientLen);
278 
279  *senderIP = String::fromUTF8 (inet_ntoa (client.sin_addr), 16);
280  *senderPort = ntohs (client.sin_port);
281  }
282  }
283  }
284 
285  if (bytesThisTime <= 0 || ! connected)
286  {
287  if (bytesRead == 0 && blockUntilSpecifiedAmountHasArrived)
288  bytesRead = -1;
289 
290  break;
291  }
292 
293  bytesRead = static_cast<int> (bytesRead + bytesThisTime);
294 
295  if (! blockUntilSpecifiedAmountHasArrived)
296  break;
297  }
298 
299  return (int) bytesRead;
300  }
301 
302  static int waitForReadiness (std::atomic<int>& handle, CriticalSection& readLock,
303  bool forReading, int timeoutMsecs) noexcept
304  {
305  // avoid race-condition
306  CriticalSection::ScopedTryLockType lock (readLock);
307 
308  if (! lock.isLocked())
309  return -1;
310 
311  auto hasErrorOccurred = [&handle]() -> bool
312  {
313  auto h = (SocketHandle) handle.load();
314 
315  if (h == invalidSocket)
316  return true;
317 
318  int opt;
319  juce_socklen_t len = sizeof (opt);
320 
321  if (getsockopt (h, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 || opt != 0)
322  return true;
323 
324  return false;
325  };
326 
327  auto h = handle.load();
328 
329  #if JUCE_WINDOWS || JUCE_MINGW
330  struct timeval timeout;
331  struct timeval* timeoutp;
332 
333  if (timeoutMsecs >= 0)
334  {
335  timeout.tv_sec = timeoutMsecs / 1000;
336  timeout.tv_usec = (timeoutMsecs % 1000) * 1000;
337  timeoutp = &timeout;
338  }
339  else
340  {
341  timeoutp = nullptr;
342  }
343 
344  fd_set rset, wset;
345  FD_ZERO (&rset);
346  FD_SET ((SOCKET) h, &rset);
347  FD_ZERO (&wset);
348  FD_SET ((SOCKET) h, &wset);
349 
350  fd_set* prset = forReading ? &rset : nullptr;
351  fd_set* pwset = forReading ? nullptr : &wset;
352 
353  // NB - need to use select() here as WSAPoll is broken on Windows
354  if (select ((int) h + 1, prset, pwset, nullptr, timeoutp) < 0 || hasErrorOccurred())
355  return -1;
356 
357  return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0;
358  #else
359  short eventsFlag = (forReading ? POLLIN : POLLOUT);
360  pollfd pfd { (SocketHandle) h, eventsFlag, 0 };
361 
362  int result = 0;
363 
364  for (;;)
365  {
366  result = poll (&pfd, 1, timeoutMsecs);
367 
368  if (result >= 0 || errno != EINTR)
369  break;
370  }
371 
372  if (result < 0 || hasErrorOccurred())
373  return -1;
374 
375  return (pfd.revents & eventsFlag) != 0;
376  #endif
377  }
378 
379  static addrinfo* getAddressInfo (bool isDatagram, const String& hostName, int portNumber)
380  {
381  struct addrinfo hints;
382  zerostruct (hints);
383 
384  hints.ai_family = AF_UNSPEC;
385  hints.ai_socktype = isDatagram ? SOCK_DGRAM : SOCK_STREAM;
386  hints.ai_flags = AI_NUMERICSERV;
387 
388  struct addrinfo* info = nullptr;
389 
390  if (getaddrinfo (hostName.toRawUTF8(), String (portNumber).toRawUTF8(), &hints, &info) == 0)
391  return info;
392 
393  return nullptr;
394  }
395 
396  static bool connectSocket (std::atomic<int>& handle,
397  CriticalSection& readLock,
398  const String& hostName,
399  int portNumber,
400  int timeOutMillisecs,
401  const SocketOptions& options) noexcept
402  {
403  bool success = false;
404 
405  if (auto* info = getAddressInfo (false, hostName, portNumber))
406  {
407  for (auto* i = info; i != nullptr; i = i->ai_next)
408  {
409  auto newHandle = socket (i->ai_family, i->ai_socktype, 0);
410 
411  if (newHandle != invalidSocket)
412  {
413  setSocketBlockingState (newHandle, false);
414  auto result = ::connect (newHandle, i->ai_addr, (socklen_t) i->ai_addrlen);
415  success = (result >= 0);
416 
417  if (! success)
418  {
419  #if JUCE_WINDOWS
420  if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
421  #else
422  if (errno == EINPROGRESS)
423  #endif
424  {
425  std::atomic<int> cvHandle { (int) newHandle };
426 
427  if (waitForReadiness (cvHandle, readLock, false, timeOutMillisecs) == 1)
428  success = true;
429  }
430  }
431 
432  if (success)
433  {
434  handle = (int) newHandle;
435  break;
436  }
437 
438  #if JUCE_WINDOWS
439  closesocket (newHandle);
440  #else
441  ::close (newHandle);
442  #endif
443  }
444  }
445 
446  freeaddrinfo (info);
447 
448  if (success)
449  {
450  auto h = (SocketHandle) handle.load();
451  setSocketBlockingState (h, true);
452  resetSocketOptions (h, false, false, options);
453  }
454  }
455 
456  return success;
457  }
458 
459  static void makeReusable (int handle) noexcept
460  {
461  setOption ((SocketHandle) handle, SO_REUSEADDR, (int) 1);
462  }
463 
464  static bool multicast (int handle, const String& multicastIPAddress,
465  const String& interfaceIPAddress, bool join) noexcept
466  {
467  struct ip_mreq mreq;
468 
469  zerostruct (mreq);
470  mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toRawUTF8());
471  mreq.imr_interface.s_addr = INADDR_ANY;
472 
473  if (interfaceIPAddress.isNotEmpty())
474  mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toRawUTF8());
475 
476  return setsockopt ((SocketHandle) handle, IPPROTO_IP,
477  join ? IP_ADD_MEMBERSHIP
478  : IP_DROP_MEMBERSHIP,
479  (const char*) &mreq, sizeof (mreq)) == 0;
480  }
481 }
482 
483 //==============================================================================
485 {
486  SocketHelpers::initSockets();
487 }
488 
489 StreamingSocket::StreamingSocket (const String& host, int portNum, int h, const SocketOptions& optionsIn)
490  : options (optionsIn),
491  hostName (host),
492  portNumber (portNum),
493  handle (h),
494  connected (true)
495 {
496  jassert (SocketHelpers::isValidPortNumber (portNum));
497 
498  SocketHelpers::initSockets();
499  SocketHelpers::resetSocketOptions ((SocketHandle) h, false, false, options);
500 }
501 
503 {
504  close();
505 }
506 
507 //==============================================================================
508 int StreamingSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock)
509 {
510  return (connected && ! isListener) ? SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer,maxBytesToRead,
511  connected, shouldBlock, readLock)
512  : -1;
513 }
514 
515 int StreamingSocket::write (const void* sourceBuffer, int numBytesToWrite)
516 {
517  if (isListener || ! connected)
518  return -1;
519 
520  return (int) ::send ((SocketHandle) handle.load(), (const char*) sourceBuffer, (juce_recvsend_size_t) numBytesToWrite, 0);
521 }
522 
523 //==============================================================================
524 int StreamingSocket::waitUntilReady (bool readyForReading, int timeoutMsecs)
525 {
526  return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs)
527  : -1;
528 }
529 
530 //==============================================================================
532 {
533  return bindToPort (port, String());
534 }
535 
536 bool StreamingSocket::bindToPort (int port, const String& addr)
537 {
538  jassert (SocketHelpers::isValidPortNumber (port));
539 
540  return SocketHelpers::bindSocket ((SocketHandle) handle.load(), port, addr);
541 }
542 
543 int StreamingSocket::getBoundPort() const noexcept
544 {
545  return SocketHelpers::getBoundPort ((SocketHandle) handle.load());
546 }
547 
548 bool StreamingSocket::connect (const String& remoteHostName, int remotePortNumber, int timeOutMillisecs)
549 {
550  jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
551 
552  if (isListener)
553  {
554  // a listener socket can't connect to another one!
555  jassertfalse;
556  return false;
557  }
558 
559  if (connected)
560  close();
561 
562  hostName = remoteHostName;
563  portNumber = remotePortNumber;
564  isListener = false;
565 
566  connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName,
567  remotePortNumber, timeOutMillisecs, options);
568 
569  if (! connected)
570  return false;
571 
572  if (! SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(), false, false, options))
573  {
574  close();
575  return false;
576  }
577 
578  return true;
579 }
580 
582 {
583  if (handle >= 0)
584  SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected);
585 
586  hostName.clear();
587  portNumber = 0;
588  handle = -1;
589  isListener = false;
590 }
591 
592 //==============================================================================
593 bool StreamingSocket::createListener (int newPortNumber, const String& localHostName)
594 {
595  jassert (SocketHelpers::isValidPortNumber (newPortNumber));
596 
597  if (connected)
598  close();
599 
600  hostName = "listener";
601  portNumber = newPortNumber;
602  isListener = true;
603 
604  handle = (int) socket (AF_INET, SOCK_STREAM, 0);
605 
606  if (handle < 0)
607  return false;
608 
609  #if ! JUCE_WINDOWS // on windows, adding this option produces behaviour different to posix
610  SocketHelpers::makeReusable (handle);
611  #endif
612 
613  if (SocketHelpers::bindSocket ((SocketHandle) handle.load(), portNumber, localHostName)
614  && listen ((SocketHandle) handle.load(), SOMAXCONN) >= 0)
615  {
616  connected = true;
617  return true;
618  }
619 
620  close();
621  return false;
622 }
623 
625 {
626  // To call this method, you first have to use createListener() to
627  // prepare this socket as a listener.
628  jassert (isListener || ! connected);
629 
630  if (connected && isListener)
631  {
632  struct sockaddr_storage address;
633  juce_socklen_t len = sizeof (address);
634  auto newSocket = (int) accept ((SocketHandle) handle.load(), (struct sockaddr*) &address, &len);
635 
636  if (newSocket >= 0 && connected)
637  return new StreamingSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr),
638  portNumber, newSocket, options);
639  }
640 
641  return nullptr;
642 }
643 
644 bool StreamingSocket::isLocal() const noexcept
645 {
646  if (! isConnected())
647  return false;
648 
649  IPAddress currentIP (SocketHelpers::getConnectedAddress ((SocketHandle) handle.load()));
650 
651  for (auto& a : IPAddress::getAllAddresses())
652  if (a == currentIP)
653  return true;
654 
655  return hostName == "127.0.0.1";
656 }
657 
658 
659 //==============================================================================
660 //==============================================================================
661 DatagramSocket::DatagramSocket (bool canBroadcast, const SocketOptions& optionsIn)
662  : options { optionsIn }
663 {
664  SocketHelpers::initSockets();
665 
666  handle = (int) socket (AF_INET, SOCK_DGRAM, 0);
667 
668  if (handle >= 0)
669  {
670  SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(), true, canBroadcast, options);
671  SocketHelpers::makeReusable (handle);
672  }
673 }
674 
676 {
677  if (lastServerAddress != nullptr)
678  freeaddrinfo (static_cast<struct addrinfo*> (lastServerAddress));
679 
680  shutdown();
681 }
682 
684 {
685  if (handle < 0)
686  return;
687 
688  std::atomic<int> handleCopy { handle.load() };
689  handle = -1;
690 
691  std::atomic<bool> connected { false };
692  SocketHelpers::closeSocket (handleCopy, readLock, false, 0, connected);
693 
694  isBound = false;
695 }
696 
698 {
699  return bindToPort (port, String());
700 }
701 
702 bool DatagramSocket::bindToPort (int port, const String& addr)
703 {
704  jassert (SocketHelpers::isValidPortNumber (port));
705 
706  if (handle < 0)
707  return false;
708 
709  if (SocketHelpers::bindSocket ((SocketHandle) handle.load(), port, addr))
710  {
711  isBound = true;
712  lastBindAddress = addr;
713  return true;
714  }
715 
716  return false;
717 }
718 
719 int DatagramSocket::getBoundPort() const noexcept
720 {
721  return (handle >= 0 && isBound) ? SocketHelpers::getBoundPort ((SocketHandle) handle.load()) : -1;
722 }
723 
724 //==============================================================================
725 int DatagramSocket::waitUntilReady (bool readyForReading, int timeoutMsecs)
726 {
727  if (handle < 0)
728  return -1;
729 
730  return SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs);
731 }
732 
733 int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock)
734 {
735  if (handle < 0 || ! isBound)
736  return -1;
737 
738  std::atomic<bool> connected { true };
739  return SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer, maxBytesToRead,
740  connected, shouldBlock, readLock);
741 }
742 
743 int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock, String& senderIPAddress, int& senderPort)
744 {
745  if (handle < 0 || ! isBound)
746  return -1;
747 
748  std::atomic<bool> connected { true };
749  return SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer, maxBytesToRead, connected,
750  shouldBlock, readLock, &senderIPAddress, &senderPort);
751 }
752 
753 int DatagramSocket::write (const String& remoteHostname, int remotePortNumber,
754  const void* sourceBuffer, int numBytesToWrite)
755 {
756  jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
757 
758  if (handle < 0)
759  return -1;
760 
761  struct addrinfo*& info = reinterpret_cast<struct addrinfo*&> (lastServerAddress);
762 
763  // getaddrinfo can be quite slow so cache the result of the address lookup
764  if (info == nullptr || remoteHostname != lastServerHost || remotePortNumber != lastServerPort)
765  {
766  if (info != nullptr)
767  freeaddrinfo (info);
768 
769  if ((info = SocketHelpers::getAddressInfo (true, remoteHostname, remotePortNumber)) == nullptr)
770  return -1;
771 
772  lastServerHost = remoteHostname;
773  lastServerPort = remotePortNumber;
774  }
775 
776  return (int) ::sendto ((SocketHandle) handle.load(), (const char*) sourceBuffer,
777  (juce_recvsend_size_t) numBytesToWrite, 0,
778  info->ai_addr, (socklen_t) info->ai_addrlen);
779 }
780 
781 bool DatagramSocket::joinMulticast (const String& multicastIPAddress)
782 {
783  if (handle < 0 || ! isBound)
784  return false;
785 
786  return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, true);
787 }
788 
789 bool DatagramSocket::leaveMulticast (const String& multicastIPAddress)
790 {
791  if (handle < 0 || ! isBound)
792  return false;
793 
794  return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, false);
795 }
796 
798 {
799  if (handle < 0 || ! isBound)
800  return false;
801 
802  return SocketHelpers::setOption<bool> ((SocketHandle) handle.load(), IPPROTO_IP, IP_MULTICAST_LOOP, enable);
803 }
804 
805 bool DatagramSocket::setEnablePortReuse ([[maybe_unused]] bool enabled)
806 {
807  #if ! JUCE_ANDROID
808  if (handle >= 0)
809  return SocketHelpers::setOption ((SocketHandle) handle.load(),
810  #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
811  SO_REUSEADDR, // port re-use is implied by addr re-use on these platforms
812  #else
813  SO_REUSEPORT,
814  #endif
815  (int) (enabled ? 1 : 0));
816  #endif
817 
818  return false;
819 }
820 
821 JUCE_END_IGNORE_WARNINGS_MSVC
822 
823 //==============================================================================
824 //==============================================================================
825 #if JUCE_UNIT_TESTS
826 
827 struct SocketTests final : public UnitTest
828 {
829  SocketTests()
830  : UnitTest ("Sockets", UnitTestCategories::networking)
831  {
832  }
833 
834  void runTest() override
835  {
836  auto localHost = IPAddress::local();
837  int portNum = 12345;
838 
839  beginTest ("StreamingSocket");
840  {
841  StreamingSocket socketServer;
842 
843  expect (socketServer.isConnected() == false);
844  expect (socketServer.getHostName().isEmpty());
845  expect (socketServer.getBoundPort() == -1);
846  expect (static_cast<SocketHandle> (socketServer.getRawSocketHandle()) == invalidSocket);
847 
848  expect (socketServer.createListener (portNum, localHost.toString()));
849 
850  StreamingSocket socket;
851 
852  expect (socket.connect (localHost.toString(), portNum));
853 
854  expect (socket.isConnected() == true);
855  expect (socket.getHostName() == localHost.toString());
856  expect (socket.getBoundPort() != -1);
857  expect (static_cast<SocketHandle> (socket.getRawSocketHandle()) != invalidSocket);
858 
859  socket.close();
860 
861  expect (socket.isConnected() == false);
862  expect (socket.getHostName().isEmpty());
863  expect (socket.getBoundPort() == -1);
864  expect (static_cast<SocketHandle> (socket.getRawSocketHandle()) == invalidSocket);
865  }
866 
867  beginTest ("DatagramSocket");
868  {
869  DatagramSocket socket;
870 
871  expect (socket.getBoundPort() == -1);
872  expect (static_cast<SocketHandle> (socket.getRawSocketHandle()) != invalidSocket);
873 
874  expect (socket.bindToPort (portNum, localHost.toString()));
875 
876  expect (socket.getBoundPort() == portNum);
877  expect (static_cast<SocketHandle> (socket.getRawSocketHandle()) != invalidSocket);
878 
879  socket.shutdown();
880 
881  expect (socket.getBoundPort() == -1);
882  expect (static_cast<SocketHandle> (socket.getRawSocketHandle()) == invalidSocket);
883  }
884  }
885 };
886 
887 static SocketTests socketTests;
888 
889 #endif
890 #endif
891 
892 } // namespace juce
GenericScopedLock< CriticalSection > ScopedLockType
GenericScopedTryLock< CriticalSection > ScopedTryLockType
int write(const String &remoteHostname, int remotePortNumber, const void *sourceBuffer, int numBytesToWrite)
int read(void *destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived)
bool setMulticastLoopbackEnabled(bool enableLoopback)
bool bindToPort(int localPortNumber)
bool leaveMulticast(const String &multicastIPAddress)
bool setEnablePortReuse(bool enabled)
int waitUntilReady(bool readyForReading, int timeoutMsecs)
bool joinMulticast(const String &multicastIPAddress)
int getBoundPort() const noexcept
static Array< IPAddress > getAllAddresses(bool includeIPv6=false)
int read(void *destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived)
StreamingSocket * waitForNextConnection() const
int write(const void *sourceBuffer, int numBytesToWrite)
bool isLocal() const noexcept
int waitUntilReady(bool readyForReading, int timeoutMsecs)
bool createListener(int portNumber, const String &localHostName=String())
int getBoundPort() const noexcept
bool bindToPort(int localPortNumber)
bool connect(const String &remoteHostname, int remotePortNumber, int timeOutMillisecs=3000)
bool isConnected() const noexcept
Definition: juce_Socket.h:159
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)