28JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4127 4389 4018)
31 #define AI_NUMERICSERV 0x1000
35 using juce_socklen_t = int;
36 using juce_recvsend_size_t = int;
37 using SocketHandle = SOCKET;
38 static const SocketHandle invalidSocket = INVALID_SOCKET;
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;
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;
52namespace SocketHelpers
54 static void initSockets()
57 static bool socketsStarted =
false;
62 const WORD wVersionRequested = MAKEWORD (1, 1);
63 socketsStarted = WSAStartup (wVersionRequested, &wsaData) == 0;
68 inline bool isValidPortNumber (
int port)
noexcept
70 return isPositiveAndBelow (port, 65536);
73 template <
typename Type>
74 static bool setOption (SocketHandle handle,
int mode,
int property, Type value)
noexcept
76 return setsockopt (handle, mode, property,
reinterpret_cast<const char*
> (&value),
sizeof (value)) == 0;
79 template <
typename Type>
80 static bool setOption (SocketHandle handle,
int property, Type value)
noexcept
82 return setOption (handle, SOL_SOCKET, property, value);
85 static std::optional<int> getBufferSize (SocketHandle handle,
int property)
88 auto outParamSize = (socklen_t)
sizeof (result);
90 if (getsockopt (handle, SOL_SOCKET, property,
reinterpret_cast<char*
> (&result), &outParamSize) != 0
91 || outParamSize != (socklen_t)
sizeof (result))
99 static bool resetSocketOptions (SocketHandle handle,
bool isDatagram,
bool allowBroadcast,
const SocketOptions& options)
noexcept
101 auto getCurrentBufferSizeWithMinimum = [handle] (
int property)
103 constexpr auto minBufferSize = 65536;
105 if (
auto currentBufferSize = getBufferSize (handle, property))
106 return std::max (*currentBufferSize, minBufferSize);
108 return minBufferSize;
111 const auto receiveBufferSize = options.getReceiveBufferSize().value_or (getCurrentBufferSizeWithMinimum (SO_RCVBUF));
112 const auto sendBufferSize = options.getSendBufferSize() .value_or (getCurrentBufferSizeWithMinimum (SO_SNDBUF));
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));
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
127 const auto h = (SocketHandle) handle.load();
131 if (h != invalidSocket || connected)
145 StreamingSocket temp;
146 temp.connect (IPAddress::local().toString(), portNumber, 1000);
153 ::shutdown (h, SHUT_RDWR);
161 #if JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
173 static bool bindSocket (SocketHandle handle,
int port,
const String& address)
noexcept
175 if (handle == invalidSocket || ! isValidPortNumber (port))
178 struct sockaddr_in addr;
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);
186 return ::bind (handle, (
struct sockaddr*) &addr,
sizeof (addr)) >= 0;
189 static int getBoundPort (SocketHandle handle)
noexcept
191 if (handle != invalidSocket)
193 struct sockaddr_in addr;
194 socklen_t len =
sizeof (addr);
196 if (getsockname (handle, (
struct sockaddr*) &addr, &len) == 0)
197 return ntohs (addr.sin_port);
203 static String getConnectedAddress (SocketHandle handle)
noexcept
205 struct sockaddr_in addr;
206 socklen_t len =
sizeof (addr);
208 if (getpeername (handle, (
struct sockaddr*) &addr, &len) >= 0)
209 return inet_ntoa (addr.sin_addr);
214 static bool setSocketBlockingState (SocketHandle handle,
bool shouldBlock)
noexcept
217 u_long nonBlocking = shouldBlock ? 0 : (u_long) 1;
218 return ioctlsocket (handle, (
long) FIONBIO, &nonBlocking) == 0;
220 int socketFlags = fcntl (handle, F_GETFL, 0);
222 if (socketFlags == -1)
226 socketFlags &= ~O_NONBLOCK;
228 socketFlags |= O_NONBLOCK;
230 return fcntl (handle, F_SETFL, socketFlags) == 0;
235 static bool getSocketBlockingState (SocketHandle handle)
237 return (fcntl (handle, F_GETFL, 0) & O_NONBLOCK) == 0;
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
250 if (blockUntilSpecifiedAmountHasArrived != getSocketBlockingState (handle))
252 setSocketBlockingState (handle, blockUntilSpecifiedAmountHasArrived);
256 while (bytesRead < maxBytesToRead)
258 long bytesThisTime = -1;
259 auto buffer =
static_cast<char*
> (destBuffer) + bytesRead;
260 auto numToRead = (juce_recvsend_size_t) (maxBytesToRead - bytesRead);
268 if (senderIP ==
nullptr || senderPort ==
nullptr)
270 bytesThisTime = ::recv (handle, buffer, numToRead, 0);
275 socklen_t clientLen =
sizeof (sockaddr);
277 bytesThisTime = ::recvfrom (handle, buffer, numToRead, 0, (sockaddr*) &client, &clientLen);
280 *senderPort = ntohs (client.sin_port);
285 if (bytesThisTime <= 0 || ! connected)
287 if (bytesRead == 0 && blockUntilSpecifiedAmountHasArrived)
293 bytesRead =
static_cast<int> (bytesRead + bytesThisTime);
295 if (! blockUntilSpecifiedAmountHasArrived)
299 return (
int) bytesRead;
302 static int waitForReadiness (std::atomic<int>& handle, CriticalSection& readLock,
303 bool forReading,
int timeoutMsecs)
noexcept
308 if (! lock.isLocked())
311 auto hasErrorOccurred = [&handle]() ->
bool
313 auto h = (SocketHandle) handle.load();
315 if (h == invalidSocket)
319 juce_socklen_t len =
sizeof (opt);
321 if (getsockopt (h, SOL_SOCKET, SO_ERROR, (
char*) &opt, &len) < 0 || opt != 0)
327 auto h = handle.load();
329 #if JUCE_WINDOWS || JUCE_MINGW
330 struct timeval timeout;
331 struct timeval* timeoutp;
333 if (timeoutMsecs >= 0)
335 timeout.tv_sec = timeoutMsecs / 1000;
336 timeout.tv_usec = (timeoutMsecs % 1000) * 1000;
346 FD_SET ((SOCKET) h, &rset);
348 FD_SET ((SOCKET) h, &wset);
350 fd_set* prset = forReading ? &rset :
nullptr;
351 fd_set* pwset = forReading ? nullptr : &wset;
354 if (select ((
int) h + 1, prset, pwset,
nullptr, timeoutp) < 0 || hasErrorOccurred())
357 return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0;
359 short eventsFlag = (forReading ? POLLIN : POLLOUT);
360 pollfd pfd { (SocketHandle) h, eventsFlag, 0 };
366 result = poll (&pfd, 1, timeoutMsecs);
368 if (result >= 0 || errno != EINTR)
372 if (result < 0 || hasErrorOccurred())
375 return (pfd.revents & eventsFlag) != 0;
379 static addrinfo* getAddressInfo (
bool isDatagram,
const String& hostName,
int portNumber)
381 struct addrinfo hints;
384 hints.ai_family = AF_UNSPEC;
385 hints.ai_socktype = isDatagram ? SOCK_DGRAM : SOCK_STREAM;
386 hints.ai_flags = AI_NUMERICSERV;
388 struct addrinfo* info =
nullptr;
390 if (getaddrinfo (hostName.toRawUTF8(), String (portNumber).toRawUTF8(), &hints, &info) == 0)
396 static bool connectSocket (std::atomic<int>& handle,
397 CriticalSection& readLock,
398 const String& hostName,
400 int timeOutMillisecs,
401 const SocketOptions& options)
noexcept
403 bool success =
false;
405 if (
auto* info = getAddressInfo (
false, hostName, portNumber))
407 for (
auto* i = info; i !=
nullptr; i = i->ai_next)
409 auto newHandle = socket (i->ai_family, i->ai_socktype, 0);
411 if (newHandle != invalidSocket)
413 setSocketBlockingState (newHandle,
false);
414 auto result = ::connect (newHandle, i->ai_addr, (socklen_t) i->ai_addrlen);
415 success = (result >= 0);
420 if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
422 if (errno == EINPROGRESS)
425 std::atomic<int> cvHandle { (int) newHandle };
427 if (waitForReadiness (cvHandle, readLock,
false, timeOutMillisecs) == 1)
434 handle = (int) newHandle;
439 closesocket (newHandle);
450 auto h = (SocketHandle) handle.load();
451 setSocketBlockingState (h,
true);
452 resetSocketOptions (h,
false,
false, options);
459 static void makeReusable (
int handle)
noexcept
461 setOption ((SocketHandle) handle, SO_REUSEADDR, (
int) 1);
464 static bool multicast (
int handle,
const String& multicastIPAddress,
465 const String& interfaceIPAddress,
bool join)
noexcept
470 mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toRawUTF8());
471 mreq.imr_interface.s_addr = INADDR_ANY;
473 if (interfaceIPAddress.isNotEmpty())
474 mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toRawUTF8());
476 return setsockopt ((SocketHandle) handle, IPPROTO_IP,
477 join ? IP_ADD_MEMBERSHIP
478 : IP_DROP_MEMBERSHIP,
479 (
const char*) &mreq,
sizeof (mreq)) == 0;
486 SocketHelpers::initSockets();
490 : options (optionsIn),
492 portNumber (portNum),
496 jassert (SocketHelpers::isValidPortNumber (portNum));
498 SocketHelpers::initSockets();
499 SocketHelpers::resetSocketOptions ((SocketHandle) h,
false,
false, options);
510 return (connected && ! isListener) ? SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer,maxBytesToRead,
511 connected, shouldBlock, readLock)
517 if (isListener || ! connected)
520 return (
int) ::send ((SocketHandle) handle.load(), (
const char*) sourceBuffer, (juce_recvsend_size_t) numBytesToWrite, 0);
526 return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs)
538 jassert (SocketHelpers::isValidPortNumber (port));
540 return SocketHelpers::bindSocket ((SocketHandle) handle.load(), port, addr);
545 return SocketHelpers::getBoundPort ((SocketHandle) handle.load());
550 jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
562 hostName = remoteHostName;
563 portNumber = remotePortNumber;
566 connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName,
567 remotePortNumber, timeOutMillisecs, options);
572 if (! SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(),
false,
false, options))
584 SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected);
595 jassert (SocketHelpers::isValidPortNumber (newPortNumber));
600 hostName =
"listener";
601 portNumber = newPortNumber;
604 handle = (int) socket (AF_INET, SOCK_STREAM, 0);
610 SocketHelpers::makeReusable (handle);
613 if (SocketHelpers::bindSocket ((SocketHandle) handle.load(), portNumber, localHostName)
614 && listen ((SocketHandle) handle.load(), SOMAXCONN) >= 0)
628 jassert (isListener || ! connected);
630 if (connected && isListener)
632 struct sockaddr_storage address;
633 juce_socklen_t len =
sizeof (address);
634 auto newSocket = (int) accept ((SocketHandle) handle.load(), (
struct sockaddr*) &address, &len);
636 if (newSocket >= 0 && connected)
637 return new StreamingSocket (inet_ntoa (((
struct sockaddr_in*) &address)->sin_addr),
638 portNumber, newSocket, options);
649 IPAddress currentIP (SocketHelpers::getConnectedAddress ((SocketHandle) handle.load()));
655 return hostName ==
"127.0.0.1";
662 : options { optionsIn }
664 SocketHelpers::initSockets();
666 handle = (int) socket (AF_INET, SOCK_DGRAM, 0);
670 SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(),
true, canBroadcast, options);
671 SocketHelpers::makeReusable (handle);
677 if (lastServerAddress !=
nullptr)
678 freeaddrinfo (
static_cast<struct addrinfo*
> (lastServerAddress));
688 std::atomic<int> handleCopy { handle.load() };
691 std::atomic<bool> connected {
false };
692 SocketHelpers::closeSocket (handleCopy, readLock,
false, 0, connected);
704 jassert (SocketHelpers::isValidPortNumber (port));
709 if (SocketHelpers::bindSocket ((SocketHandle) handle.load(), port, addr))
712 lastBindAddress = addr;
721 return (handle >= 0 && isBound) ? SocketHelpers::getBoundPort ((SocketHandle) handle.load()) : -1;
730 return SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs);
735 if (handle < 0 || ! isBound)
738 std::atomic<bool> connected {
true };
739 return SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer, maxBytesToRead,
740 connected, shouldBlock, readLock);
745 if (handle < 0 || ! isBound)
748 std::atomic<bool> connected {
true };
749 return SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer, maxBytesToRead, connected,
750 shouldBlock, readLock, &senderIPAddress, &senderPort);
754 const void* sourceBuffer,
int numBytesToWrite)
756 jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
761 struct addrinfo*& info =
reinterpret_cast<struct addrinfo*&
> (lastServerAddress);
764 if (info ==
nullptr || remoteHostname != lastServerHost || remotePortNumber != lastServerPort)
769 if ((info = SocketHelpers::getAddressInfo (
true, remoteHostname, remotePortNumber)) ==
nullptr)
772 lastServerHost = remoteHostname;
773 lastServerPort = remotePortNumber;
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);
783 if (handle < 0 || ! isBound)
786 return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress,
true);
791 if (handle < 0 || ! isBound)
794 return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress,
false);
799 if (handle < 0 || ! isBound)
802 return SocketHelpers::setOption<bool> ((SocketHandle) handle.load(), IPPROTO_IP, IP_MULTICAST_LOOP, enable);
809 return SocketHelpers::setOption ((SocketHandle) handle.load(),
810 #
if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
815 (
int) (enabled ? 1 : 0));
821JUCE_END_IGNORE_WARNINGS_MSVC
827struct SocketTests final :
public UnitTest
830 :
UnitTest (
"Sockets", UnitTestCategories::networking)
834 void runTest()
override
836 auto localHost = IPAddress::local();
839 beginTest (
"StreamingSocket");
841 StreamingSocket socketServer;
843 expect (socketServer.isConnected() ==
false);
844 expect (socketServer.getHostName().isEmpty());
845 expect (socketServer.getBoundPort() == -1);
846 expect (
static_cast<SocketHandle
> (socketServer.getRawSocketHandle()) == invalidSocket);
848 expect (socketServer.createListener (portNum, localHost.toString()));
850 StreamingSocket socket;
852 expect (socket.connect (localHost.toString(), portNum));
854 expect (socket.isConnected() ==
true);
855 expect (socket.getHostName() == localHost.toString());
856 expect (socket.getBoundPort() != -1);
857 expect (
static_cast<SocketHandle
> (socket.getRawSocketHandle()) != invalidSocket);
861 expect (socket.isConnected() ==
false);
862 expect (socket.getHostName().isEmpty());
863 expect (socket.getBoundPort() == -1);
864 expect (
static_cast<SocketHandle
> (socket.getRawSocketHandle()) == invalidSocket);
867 beginTest (
"DatagramSocket");
869 DatagramSocket socket;
871 expect (socket.getBoundPort() == -1);
872 expect (
static_cast<SocketHandle
> (socket.getRawSocketHandle()) != invalidSocket);
874 expect (socket.bindToPort (portNum, localHost.toString()));
876 expect (socket.getBoundPort() == portNum);
877 expect (
static_cast<SocketHandle
> (socket.getRawSocketHandle()) != invalidSocket);
881 expect (socket.getBoundPort() == -1);
882 expect (
static_cast<SocketHandle
> (socket.getRawSocketHandle()) == invalidSocket);
887static SocketTests socketTests;
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
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)