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
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);
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));
122 [[maybe_unused]] CriticalSection& readLock,
123 [[maybe_unused]]
bool isListener,
124 [[maybe_unused]]
int portNumber,
127 const auto h = (SocketHandle) handle.load();
131 if (h != invalidSocket || connected)
145 StreamingSocket temp;
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;
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,
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)
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)
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)
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));
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)
739 return SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer, maxBytesToRead,
740 connected, shouldBlock, readLock);
745 if (handle < 0 || ! isBound)
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()));
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");
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
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.
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.
DatagramSocket()
Creates a datagram socket.
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)
Options used for the configuration of the underlying system socket in the StreamingSocket and Datagra...
A wrapper for a streaming (TCP) socket.
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.
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.
This is a base class for classes that perform a unit test.
void zerostruct(Type &structure) noexcept
Overwrites a structure or object with zeros.
unsigned short uint16
A platform-independent 16-bit unsigned integer type.
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
std::u16string toString(NumberT value)
convert an number to an UTF-16 string