OpenShot Audio Library | OpenShotAudio 0.4.0
 
Loading...
Searching...
No Matches
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
23namespace juce
24{
25
26#if ! JUCE_WASM
27
28JUCE_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//==============================================================================
52namespace 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
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
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
489StreamingSocket::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
506
507//==============================================================================
508int 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
515int 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//==============================================================================
524int 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
536bool 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
544{
545 return SocketHelpers::getBoundPort ((SocketHandle) handle.load());
546}
547
548bool 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//==============================================================================
593bool 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
644bool 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//==============================================================================
661DatagramSocket::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
702bool 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
720{
721 return (handle >= 0 && isBound) ? SocketHelpers::getBoundPort ((SocketHandle) handle.load()) : -1;
722}
723
724//==============================================================================
725int 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
733int 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
743int 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
753int 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
781bool 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
789bool 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
805bool 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
821JUCE_END_IGNORE_WARNINGS_MSVC
822
823//==============================================================================
824//==============================================================================
825#if JUCE_UNIT_TESTS
826
827struct 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
887static 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)
static IPAddress local(bool IPv6=false) noexcept
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
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)