OpenShot Library | libopenshot-audio 0.2.0
juce_Socket.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
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_MSVC
27 #pragma warning (push)
28 #pragma warning (disable : 4127 4389 4018)
29#endif
30
31#ifndef AI_NUMERICSERV // (missing in older Mac SDKs)
32 #define AI_NUMERICSERV 0x1000
33#endif
34
35#if JUCE_WINDOWS
36 typedef int juce_socklen_t;
37 typedef int juce_recvsend_size_t;
38 typedef SOCKET SocketHandle;
39 static const SocketHandle invalidSocket = INVALID_SOCKET;
40#elif JUCE_ANDROID
41 typedef socklen_t juce_socklen_t;
42 typedef size_t juce_recvsend_size_t;
43 typedef int SocketHandle;
44 static const SocketHandle invalidSocket = -1;
45#else
46 typedef socklen_t juce_socklen_t;
47 typedef socklen_t juce_recvsend_size_t;
48 typedef int SocketHandle;
49 static const SocketHandle invalidSocket = -1;
50#endif
51
52//==============================================================================
53namespace SocketHelpers
54{
55 static void initSockets()
56 {
57 #if JUCE_WINDOWS
58 static bool socketsStarted = false;
59
60 if (! socketsStarted)
61 {
62 socketsStarted = true;
63
64 WSADATA wsaData;
65 const WORD wVersionRequested = MAKEWORD (1, 1);
66 WSAStartup (wVersionRequested, &wsaData);
67 }
68 #endif
69 }
70
71 inline bool isValidPortNumber (int port) noexcept
72 {
73 return isPositiveAndBelow (port, 65536);
74 }
75
76 template <typename Type>
77 static bool setOption (SocketHandle handle, int mode, int property, Type value) noexcept
78 {
79 return setsockopt (handle, mode, property, reinterpret_cast<const char*> (&value), sizeof (value)) == 0;
80 }
81
82 template <typename Type>
83 static bool setOption (SocketHandle handle, int property, Type value) noexcept
84 {
85 return setOption (handle, SOL_SOCKET, property, value);
86 }
87
88 static bool resetSocketOptions (SocketHandle handle, bool isDatagram, bool allowBroadcast) noexcept
89 {
90 return handle > 0
91 && setOption (handle, SO_RCVBUF, (int) 65536)
92 && setOption (handle, SO_SNDBUF, (int) 65536)
93 && (isDatagram ? ((! allowBroadcast) || setOption (handle, SO_BROADCAST, (int) 1))
94 : setOption (handle, IPPROTO_TCP, TCP_NODELAY, (int) 1));
95 }
96
97 static void closeSocket (std::atomic<int>& handle, CriticalSection& readLock,
98 bool isListener, int portNumber, std::atomic<bool>& connected) noexcept
99 {
100 const SocketHandle h = handle.load();
101 handle = -1;
102
103 #if JUCE_WINDOWS
104 ignoreUnused (portNumber, isListener, readLock);
105
106 if (h != (unsigned) SOCKET_ERROR || connected)
107 closesocket (h);
108
109 // make sure any read process finishes before we delete the socket
110 CriticalSection::ScopedLockType lock (readLock);
111 connected = false;
112 #else
113 if (connected)
114 {
115 connected = false;
116
117 if (isListener)
118 {
119 // need to do this to interrupt the accept() function..
120 StreamingSocket temp;
121 temp.connect (IPAddress::local().toString(), portNumber, 1000);
122 }
123 }
124
125 if (h != -1)
126 {
127 // unblock any pending read requests
128 ::shutdown (h, SHUT_RDWR);
129
130 {
131 // see man-page of recv on linux about a race condition where the
132 // shutdown command is lost if the receiving thread does not have
133 // a chance to process before close is called. On Mac OS X shutdown
134 // does not unblock a select call, so using a lock here will dead-lock
135 // both threads.
136 #if JUCE_LINUX || JUCE_ANDROID
137 CriticalSection::ScopedLockType lock (readLock);
138 ::close (h);
139 #else
140 ::close (h);
141 CriticalSection::ScopedLockType lock (readLock);
142 #endif
143 }
144 }
145 #endif
146 }
147
148 static bool bindSocket (SocketHandle handle, int port, const String& address) noexcept
149 {
150 if (handle <= 0 || ! isValidPortNumber (port))
151 return false;
152
153 struct sockaddr_in addr;
154 zerostruct (addr); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
155
156 addr.sin_family = PF_INET;
157 addr.sin_port = htons ((uint16) port);
158 addr.sin_addr.s_addr = address.isNotEmpty() ? ::inet_addr (address.toRawUTF8())
159 : htonl (INADDR_ANY);
160
161 return ::bind (handle, (struct sockaddr*) &addr, sizeof (addr)) >= 0;
162 }
163
164 static int getBoundPort (SocketHandle handle) noexcept
165 {
166 if (handle > 0)
167 {
168 struct sockaddr_in addr;
169 socklen_t len = sizeof (addr);
170
171 if (getsockname (handle, (struct sockaddr*) &addr, &len) == 0)
172 return ntohs (addr.sin_port);
173 }
174
175 return -1;
176 }
177
178 static String getConnectedAddress (SocketHandle handle) noexcept
179 {
180 struct sockaddr_in addr;
181 socklen_t len = sizeof (addr);
182
183 if (getpeername (handle, (struct sockaddr*) &addr, &len) >= 0)
184 return inet_ntoa (addr.sin_addr);
185
186 return String ("0.0.0.0");
187 }
188
189 static int readSocket (SocketHandle handle,
190 void* destBuffer, int maxBytesToRead,
191 std::atomic<bool>& connected,
192 bool blockUntilSpecifiedAmountHasArrived,
193 CriticalSection& readLock,
194 String* senderIP = nullptr,
195 int* senderPort = nullptr) noexcept
196 {
197 int bytesRead = 0;
198
199 while (bytesRead < maxBytesToRead)
200 {
201 long bytesThisTime = -1;
202 auto buffer = static_cast<char*> (destBuffer) + bytesRead;
203 auto numToRead = (juce_recvsend_size_t) (maxBytesToRead - bytesRead);
204
205 {
206 // avoid race-condition
208
209 if (lock.isLocked())
210 {
211 if (senderIP == nullptr || senderPort == nullptr)
212 {
213 bytesThisTime = ::recv (handle, buffer, numToRead, 0);
214 }
215 else
216 {
217 sockaddr_in client;
218 socklen_t clientLen = sizeof (sockaddr);
219
220 bytesThisTime = ::recvfrom (handle, buffer, numToRead, 0, (sockaddr*) &client, &clientLen);
221
222 *senderIP = String::fromUTF8 (inet_ntoa (client.sin_addr), 16);
223 *senderPort = ntohs (client.sin_port);
224 }
225 }
226 }
227
228 if (bytesThisTime <= 0 || ! connected)
229 {
230 if (bytesRead == 0 && blockUntilSpecifiedAmountHasArrived)
231 bytesRead = -1;
232
233 break;
234 }
235
236 bytesRead += bytesThisTime;
237
238 if (! blockUntilSpecifiedAmountHasArrived)
239 break;
240 }
241
242 return (int) bytesRead;
243 }
244
245 static int waitForReadiness (std::atomic<int>& handle, CriticalSection& readLock,
246 bool forReading, int timeoutMsecs) noexcept
247 {
248 // avoid race-condition
250
251 if (! lock.isLocked())
252 return -1;
253
254 int h = handle.load();
255
256 struct timeval timeout;
257 struct timeval* timeoutp;
258
259 if (timeoutMsecs >= 0)
260 {
261 timeout.tv_sec = timeoutMsecs / 1000;
262 timeout.tv_usec = (timeoutMsecs % 1000) * 1000;
263 timeoutp = &timeout;
264 }
265 else
266 {
267 timeoutp = nullptr;
268 }
269
270 fd_set rset, wset;
271 FD_ZERO (&rset);
272 FD_SET (h, &rset);
273 FD_ZERO (&wset);
274 FD_SET (h, &wset);
275
276 fd_set* const prset = forReading ? &rset : nullptr;
277 fd_set* const pwset = forReading ? nullptr : &wset;
278
279 #if JUCE_WINDOWS
280 if (select ((int) h + 1, prset, pwset, 0, timeoutp) < 0)
281 return -1;
282 #else
283 {
284 int result;
285
286 while ((result = select (h + 1, prset, pwset, nullptr, timeoutp)) < 0
287 && errno == EINTR)
288 {
289 }
290
291 if (result < 0)
292 return -1;
293 }
294 #endif
295
296 // we are closing
297 if (handle.load() < 0)
298 return -1;
299
300 {
301 int opt;
302 juce_socklen_t len = sizeof (opt);
303
304 if (getsockopt (h, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0
305 || opt != 0)
306 return -1;
307 }
308
309 return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0;
310 }
311
312 static bool setSocketBlockingState (SocketHandle handle, bool shouldBlock) noexcept
313 {
314 #if JUCE_WINDOWS
315 u_long nonBlocking = shouldBlock ? 0 : (u_long) 1;
316 return ioctlsocket (handle, FIONBIO, &nonBlocking) == 0;
317 #else
318 int socketFlags = fcntl (handle, F_GETFL, 0);
319
320 if (socketFlags == -1)
321 return false;
322
323 if (shouldBlock)
324 socketFlags &= ~O_NONBLOCK;
325 else
326 socketFlags |= O_NONBLOCK;
327
328 return fcntl (handle, F_SETFL, socketFlags) == 0;
329 #endif
330 }
331
332 static addrinfo* getAddressInfo (bool isDatagram, const String& hostName, int portNumber)
333 {
334 struct addrinfo hints;
335 zerostruct (hints);
336
337 hints.ai_family = AF_UNSPEC;
338 hints.ai_socktype = isDatagram ? SOCK_DGRAM : SOCK_STREAM;
339 hints.ai_flags = AI_NUMERICSERV;
340
341 struct addrinfo* info = nullptr;
342
343 if (getaddrinfo (hostName.toRawUTF8(), String (portNumber).toRawUTF8(), &hints, &info) == 0)
344 return info;
345
346 return nullptr;
347 }
348
349 static bool connectSocket (std::atomic<int>& handle,
350 CriticalSection& readLock,
351 const String& hostName,
352 int portNumber,
353 int timeOutMillisecs) noexcept
354 {
355 bool success = false;
356
357 if (auto* info = getAddressInfo (false, hostName, portNumber))
358 {
359 for (auto* i = info; i != nullptr; i = i->ai_next)
360 {
361 auto newHandle = socket (i->ai_family, i->ai_socktype, 0);
362
363 if (newHandle != invalidSocket)
364 {
365 setSocketBlockingState (newHandle, false);
366 auto result = ::connect (newHandle, i->ai_addr, (socklen_t) i->ai_addrlen);
367 success = (result >= 0);
368
369 if (! success)
370 {
371 #if JUCE_WINDOWS
372 if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
373 #else
374 if (errno == EINPROGRESS)
375 #endif
376 {
377 std::atomic<int> cvHandle { (int) newHandle };
378
379 if (waitForReadiness (cvHandle, readLock, false, timeOutMillisecs) == 1)
380 success = true;
381 }
382 }
383
384 if (success)
385 {
386 handle = (int) newHandle;
387 break;
388 }
389
390 #if JUCE_WINDOWS
391 closesocket (newHandle);
392 #else
393 ::close (newHandle);
394 #endif
395 }
396 }
397
398 freeaddrinfo (info);
399
400 if (success)
401 {
402 setSocketBlockingState (handle, true);
403 resetSocketOptions (handle, false, false);
404 }
405 }
406
407 return success;
408 }
409
410 static void makeReusable (int handle) noexcept
411 {
412 setOption (handle, SO_REUSEADDR, (int) 1);
413 }
414
415 static bool multicast (int handle, const String& multicastIPAddress,
416 const String& interfaceIPAddress, bool join) noexcept
417 {
418 struct ip_mreq mreq;
419
420 zerostruct (mreq);
421 mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toRawUTF8());
422 mreq.imr_interface.s_addr = INADDR_ANY;
423
424 if (interfaceIPAddress.isNotEmpty())
425 mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toRawUTF8());
426
427 return setsockopt (handle, IPPROTO_IP,
428 join ? IP_ADD_MEMBERSHIP
429 : IP_DROP_MEMBERSHIP,
430 (const char*) &mreq, sizeof (mreq)) == 0;
431 }
432}
433
434//==============================================================================
436{
437 SocketHelpers::initSockets();
438}
439
441 : hostName (host),
442 portNumber (portNum),
443 handle (h),
444 connected (true)
445{
446 jassert (SocketHelpers::isValidPortNumber (portNum));
447
448 SocketHelpers::initSockets();
449 SocketHelpers::resetSocketOptions (h, false, false);
450}
451
456
457//==============================================================================
459{
460 return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
461 connected, shouldBlock, readLock)
462 : -1;
463}
464
466{
467 if (isListener || ! connected)
468 return -1;
469
470 return (int) ::send (handle, (const char*) sourceBuffer, (juce_recvsend_size_t) numBytesToWrite, 0);
471}
472
473//==============================================================================
475{
476 return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs)
477 : -1;
478}
479
480//==============================================================================
482{
483 return bindToPort (port, String());
484}
485
487{
488 jassert (SocketHelpers::isValidPortNumber (port));
489
490 return SocketHelpers::bindSocket (handle, port, addr);
491}
492
494{
495 return SocketHelpers::getBoundPort (handle);
496}
497
499{
500 jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
501
502 if (isListener)
503 {
504 jassertfalse; // a listener socket can't connect to another one!
505 return false;
506 }
507
508 if (connected)
509 close();
510
511 hostName = remoteHostName;
512 portNumber = remotePortNumber;
513 isListener = false;
514
515 connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName,
517
518 if (! (connected && SocketHelpers::resetSocketOptions (handle, false, false)))
519 {
520 close();
521 return false;
522 }
523
524 return true;
525}
526
528{
529 SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected);
530
531 hostName.clear();
532 portNumber = 0;
533 handle = -1;
534 isListener = false;
535}
536
537//==============================================================================
539{
540 jassert (SocketHelpers::isValidPortNumber (newPortNumber));
541
542 if (connected)
543 close();
544
545 hostName = "listener";
546 portNumber = newPortNumber;
547 isListener = true;
548
549 handle = (int) socket (AF_INET, SOCK_STREAM, 0);
550
551 if (handle < 0)
552 return false;
553
554 #if ! JUCE_WINDOWS // on windows, adding this option produces behaviour different to posix
555 SocketHelpers::makeReusable (handle);
556 #endif
557
558 if (SocketHelpers::bindSocket (handle, portNumber, localHostName)
559 && listen (handle, SOMAXCONN) >= 0)
560 {
561 connected = true;
562 return true;
563 }
564
565 close();
566 return false;
567}
568
570{
571 // To call this method, you first have to use createListener() to
572 // prepare this socket as a listener.
573 jassert (isListener || ! connected);
574
575 if (connected && isListener)
576 {
577 struct sockaddr_storage address;
578 juce_socklen_t len = sizeof (address);
579 auto newSocket = (int) accept (handle, (struct sockaddr*) &address, &len);
580
581 if (newSocket >= 0 && connected)
582 return new StreamingSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr),
583 portNumber, newSocket);
584 }
585
586 return nullptr;
587}
588
590{
591 if (! isConnected())
592 return false;
593
594 IPAddress currentIP (SocketHelpers::getConnectedAddress (handle));
595
596 for (auto& a : IPAddress::getAllAddresses())
597 if (a == currentIP)
598 return true;
599
600 return hostName == "127.0.0.1";
601}
602
603
604//==============================================================================
605//==============================================================================
607{
608 SocketHelpers::initSockets();
609
610 handle = (int) socket (AF_INET, SOCK_DGRAM, 0);
611
612 if (handle >= 0)
613 {
614 SocketHelpers::resetSocketOptions (handle, true, canBroadcast);
615 SocketHelpers::makeReusable (handle);
616 }
617}
618
620{
621 if (lastServerAddress != nullptr)
622 freeaddrinfo (static_cast<struct addrinfo*> (lastServerAddress));
623
624 shutdown();
625}
626
628{
629 if (handle < 0)
630 return;
631
632 std::atomic<int> handleCopy { handle.load() };
633 handle = -1;
634 std::atomic<bool> connected { false };
635 SocketHelpers::closeSocket (handleCopy, readLock, false, 0, connected);
636}
637
639{
640 return bindToPort (port, String());
641}
642
644{
645 jassert (SocketHelpers::isValidPortNumber (port));
646
647 if (SocketHelpers::bindSocket (handle, port, addr))
648 {
649 isBound = true;
650 lastBindAddress = addr;
651 return true;
652 }
653
654 return false;
655}
656
658{
659 return (handle >= 0 && isBound) ? SocketHelpers::getBoundPort (handle) : -1;
660}
661
662//==============================================================================
664{
665 if (handle < 0)
666 return -1;
667
668 return SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs);
669}
670
672{
673 if (handle < 0 || ! isBound)
674 return -1;
675
676 std::atomic<bool> connected { true };
677
678 SocketHelpers::setSocketBlockingState (handle, shouldBlock);
679 return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
680 connected, shouldBlock, readLock);
681}
682
684{
685 if (handle < 0 || ! isBound)
686 return -1;
687
688 std::atomic<bool> connected { true };
689
690 SocketHelpers::setSocketBlockingState (handle, shouldBlock);
691 return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected,
693}
694
696 const void* sourceBuffer, int numBytesToWrite)
697{
698 jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
699
700 if (handle < 0)
701 return -1;
702
703 struct addrinfo*& info = reinterpret_cast<struct addrinfo*&> (lastServerAddress);
704
705 // getaddrinfo can be quite slow so cache the result of the address lookup
706 if (info == nullptr || remoteHostname != lastServerHost || remotePortNumber != lastServerPort)
707 {
708 if (info != nullptr)
710
711 if ((info = SocketHelpers::getAddressInfo (true, remoteHostname, remotePortNumber)) == nullptr)
712 return -1;
713
714 lastServerHost = remoteHostname;
715 lastServerPort = remotePortNumber;
716 }
717
718 return (int) ::sendto (handle, (const char*) sourceBuffer,
720 info->ai_addr, (socklen_t) info->ai_addrlen);
721}
722
724{
725 if (! isBound || handle < 0)
726 return false;
727
728 return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, true);
729}
730
732{
733 if (! isBound || handle < 0)
734 return false;
735
736 return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress, false);
737}
738
740{
741 if (! isBound || handle < 0)
742 return false;
743
744 return SocketHelpers::setOption<bool> (handle, IPPROTO_IP, IP_MULTICAST_LOOP, enable);
745}
746
748{
749 #if JUCE_ANDROID
750 ignoreUnused (enabled);
751 #else
752 if (handle >= 0)
753 return SocketHelpers::setOption (handle,
755 SO_REUSEADDR, // port re-use is implied by addr re-use on these platforms
756 #else
758 #endif
759 (int) (enabled ? 1 : 0));
760 #endif
761
762 return false;
763}
764
765#if JUCE_MSVC
766 #pragma warning (pop)
767#endif
768
769} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:60
GenericScopedLock< CriticalSection > ScopedLockType
Provides the type of scoped lock to use with a CriticalSection.
GenericScopedTryLock< CriticalSection > ScopedTryLockType
Provides the type of scoped try-locker to use with a CriticalSection.
void shutdown()
Closes the underlying socket object.
DatagramSocket(bool enableBroadcasting=false)
Creates a datagram socket.
int write(const String &remoteHostname, int remotePortNumber, const void *sourceBuffer, int numBytesToWrite)
Writes bytes to the socket from a buffer.
int read(void *destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived)
Reads bytes from the socket.
bool setMulticastLoopbackEnabled(bool enableLoopback)
Enables or disables multicast loopback.
bool bindToPort(int localPortNumber)
Binds the socket to the specified local port.
bool leaveMulticast(const String &multicastIPAddress)
Leave a multicast group.
~DatagramSocket()
Destructor.
bool setEnablePortReuse(bool enabled)
Allow other applications to re-use the port.
int waitUntilReady(bool readyForReading, int timeoutMsecs)
Waits until the socket is ready for reading or writing.
bool joinMulticast(const String &multicastIPAddress)
Join a multicast group.
int getBoundPort() const noexcept
Returns the local port number to which this socket is currently bound.
Represents an IP address.
static Array< IPAddress > getAllAddresses(bool includeIPv6=false)
Populates a list of all the IP addresses that this machine is using.
static IPAddress local(bool IPv6=false) noexcept
Returns an IPv4 or IPv6 address meaning "localhost", equivalent to 127.0.0.1 (IPv4) or ::1 (IPv6)
A wrapper for a streaming (TCP) socket.
Definition juce_Socket.h:42
int read(void *destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived)
Reads bytes from the socket.
StreamingSocket * waitForNextConnection() const
When in "listener" mode, this waits for a connection and spawns it as a new socket.
int write(const void *sourceBuffer, int numBytesToWrite)
Writes bytes to the socket from a buffer.
bool isLocal() const noexcept
True if the socket is connected to this machine rather than over the network.
~StreamingSocket()
Destructor.
int waitUntilReady(bool readyForReading, int timeoutMsecs)
Waits until the socket is ready for reading or writing.
bool createListener(int portNumber, const String &localHostName=String())
Puts this socket into "listener" mode.
int getBoundPort() const noexcept
Returns the local port number to which this socket is currently bound.
StreamingSocket()
Creates an uninitialised socket.
bool bindToPort(int localPortNumber)
Binds the socket to the specified local port.
void close()
Closes the connection.
bool connect(const String &remoteHostname, int remotePortNumber, int timeOutMillisecs=3000)
Tries to connect the socket to hostname:port.
bool isConnected() const noexcept
True if the socket is currently connected.
The JUCE String class!
Definition juce_String.h:43
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.