27 #pragma warning (push) 28 #pragma warning (disable : 4127 4389 4018) 31 #ifndef AI_NUMERICSERV // (missing in older Mac SDKs) 32 #define AI_NUMERICSERV 0x1000 36 using juce_socklen_t = int;
37 using juce_recvsend_size_t = int;
38 using SocketHandle = SOCKET;
39 static const SocketHandle invalidSocket = INVALID_SOCKET;
41 using juce_socklen_t = socklen_t;
42 using juce_recvsend_size_t = size_t;
43 using SocketHandle = int;
44 static const SocketHandle invalidSocket = -1;
46 using juce_socklen_t = socklen_t;
47 using juce_recvsend_size_t = socklen_t;
48 using SocketHandle = int;
49 static const SocketHandle invalidSocket = -1;
53 namespace SocketHelpers
55 static void initSockets()
58 static bool socketsStarted =
false;
62 socketsStarted =
true;
65 const WORD wVersionRequested = MAKEWORD (1, 1);
66 WSAStartup (wVersionRequested, &wsaData);
71 inline bool isValidPortNumber (
int port) noexcept
73 return isPositiveAndBelow (port, 65536);
76 template <
typename Type>
77 static bool setOption (SocketHandle handle,
int mode,
int property, Type value) noexcept
79 return setsockopt (handle, mode, property, reinterpret_cast<const char*> (&value),
sizeof (value)) == 0;
82 template <
typename Type>
83 static bool setOption (SocketHandle handle,
int property, Type value) noexcept
85 return setOption (handle, SOL_SOCKET, property, value);
88 static bool resetSocketOptions (SocketHandle handle,
bool isDatagram,
bool allowBroadcast) noexcept
90 return handle != invalidSocket
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));
97 static void closeSocket (std::atomic<int>& handle, CriticalSection& readLock,
98 bool isListener,
int portNumber, std::atomic<bool>& connected) noexcept
100 const SocketHandle h = handle.load();
104 ignoreUnused (portNumber, isListener, readLock);
106 if (h != invalidSocket || connected)
120 StreamingSocket temp;
128 ::shutdown (h, SHUT_RDWR);
136 #if JUCE_LINUX || JUCE_ANDROID 148 static bool bindSocket (SocketHandle handle,
int port,
const String& address) noexcept
150 if (handle == invalidSocket || ! isValidPortNumber (port))
153 struct sockaddr_in addr;
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);
161 return ::bind (handle, (
struct sockaddr*) &addr,
sizeof (addr)) >= 0;
164 static int getBoundPort (SocketHandle handle) noexcept
166 if (handle != invalidSocket)
168 struct sockaddr_in addr;
169 socklen_t len =
sizeof (addr);
171 if (getsockname (handle, (
struct sockaddr*) &addr, &len) == 0)
172 return ntohs (addr.sin_port);
178 static String getConnectedAddress (SocketHandle handle) noexcept
180 struct sockaddr_in addr;
181 socklen_t len =
sizeof (addr);
183 if (getpeername (handle, (
struct sockaddr*) &addr, &len) >= 0)
184 return inet_ntoa (addr.sin_addr);
189 static bool setSocketBlockingState (SocketHandle handle,
bool shouldBlock) noexcept
192 u_long nonBlocking = shouldBlock ? 0 : (u_long) 1;
193 return ioctlsocket (handle, FIONBIO, &nonBlocking) == 0;
195 int socketFlags = fcntl (handle, F_GETFL, 0);
197 if (socketFlags == -1)
201 socketFlags &= ~O_NONBLOCK;
203 socketFlags |= O_NONBLOCK;
205 return fcntl (handle, F_SETFL, socketFlags) == 0;
210 static bool getSocketBlockingState (SocketHandle handle)
212 return (fcntl (handle, F_GETFL, 0) & O_NONBLOCK) == 0;
216 static int readSocket (SocketHandle handle,
217 void* destBuffer,
int maxBytesToRead,
218 std::atomic<bool>& connected,
219 bool blockUntilSpecifiedAmountHasArrived,
220 CriticalSection& readLock,
221 String* senderIP =
nullptr,
222 int* senderPort =
nullptr) noexcept
225 if (blockUntilSpecifiedAmountHasArrived != getSocketBlockingState (handle))
227 setSocketBlockingState (handle, blockUntilSpecifiedAmountHasArrived);
231 while (bytesRead < maxBytesToRead)
233 long bytesThisTime = -1;
234 auto buffer =
static_cast<char*
> (destBuffer) + bytesRead;
235 auto numToRead = (juce_recvsend_size_t) (maxBytesToRead - bytesRead);
243 if (senderIP ==
nullptr || senderPort ==
nullptr)
245 bytesThisTime = ::recv (handle, buffer, numToRead, 0);
250 socklen_t clientLen =
sizeof (sockaddr);
252 bytesThisTime = ::recvfrom (handle, buffer, numToRead, 0, (sockaddr*) &client, &clientLen);
255 *senderPort = ntohs (client.sin_port);
260 if (bytesThisTime <= 0 || ! connected)
262 if (bytesRead == 0 && blockUntilSpecifiedAmountHasArrived)
268 bytesRead += bytesThisTime;
270 if (! blockUntilSpecifiedAmountHasArrived)
274 return (
int) bytesRead;
277 static int waitForReadiness (std::atomic<int>& handle, CriticalSection& readLock,
278 bool forReading,
int timeoutMsecs) noexcept
283 if (! lock.isLocked())
286 auto hasErrorOccurred = [&handle] () ->
bool 288 auto h = handle.load();
290 if (h == invalidSocket)
294 juce_socklen_t len =
sizeof (opt);
296 if (getsockopt (h, SOL_SOCKET, SO_ERROR, (
char*) &opt, &len) < 0 || opt != 0)
302 auto h = handle.load();
304 #if JUCE_WINDOWS || JUCE_MINGW 305 struct timeval timeout;
306 struct timeval* timeoutp;
308 if (timeoutMsecs >= 0)
310 timeout.tv_sec = timeoutMsecs / 1000;
311 timeout.tv_usec = (timeoutMsecs % 1000) * 1000;
325 fd_set* prset = forReading ? &rset :
nullptr;
326 fd_set* pwset = forReading ? nullptr : &wset;
329 if (select ((
int) h + 1, prset, pwset,
nullptr, timeoutp) < 0 || hasErrorOccurred())
332 return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0;
334 short eventsFlag = (forReading ? POLLIN : POLLOUT);
335 pollfd pfd { (SocketHandle) h, eventsFlag, 0 };
341 result = poll (&pfd, 1, timeoutMsecs);
343 if (result >= 0 || errno != EINTR)
347 if (result < 0 || hasErrorOccurred())
350 return (pfd.revents & eventsFlag) != 0;
354 static addrinfo* getAddressInfo (
bool isDatagram,
const String& hostName,
int portNumber)
356 struct addrinfo hints;
359 hints.ai_family = AF_UNSPEC;
360 hints.ai_socktype = isDatagram ? SOCK_DGRAM : SOCK_STREAM;
361 hints.ai_flags = AI_NUMERICSERV;
363 struct addrinfo* info =
nullptr;
365 if (getaddrinfo (hostName.toRawUTF8(), String (portNumber).toRawUTF8(), &hints, &info) == 0)
371 static bool connectSocket (std::atomic<int>& handle,
372 CriticalSection& readLock,
373 const String& hostName,
375 int timeOutMillisecs) noexcept
377 bool success =
false;
379 if (
auto* info = getAddressInfo (
false, hostName, portNumber))
381 for (
auto* i = info; i !=
nullptr; i = i->ai_next)
383 auto newHandle = socket (i->ai_family, i->ai_socktype, 0);
385 if (newHandle != invalidSocket)
387 setSocketBlockingState (newHandle,
false);
388 auto result = ::connect (newHandle, i->ai_addr, (socklen_t) i->ai_addrlen);
389 success = (result >= 0);
394 if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
396 if (errno == EINPROGRESS)
399 std::atomic<int> cvHandle { (int) newHandle };
401 if (waitForReadiness (cvHandle, readLock,
false, timeOutMillisecs) == 1)
408 handle = (int) newHandle;
413 closesocket (newHandle);
424 setSocketBlockingState (handle,
true);
425 resetSocketOptions (handle,
false,
false);
432 static void makeReusable (
int handle) noexcept
434 setOption (handle, SO_REUSEADDR, (
int) 1);
437 static bool multicast (
int handle,
const String& multicastIPAddress,
438 const String& interfaceIPAddress,
bool join) noexcept
443 mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toRawUTF8());
444 mreq.imr_interface.s_addr = INADDR_ANY;
446 if (interfaceIPAddress.isNotEmpty())
447 mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toRawUTF8());
449 return setsockopt (handle, IPPROTO_IP,
450 join ? IP_ADD_MEMBERSHIP
451 : IP_DROP_MEMBERSHIP,
452 (
const char*) &mreq,
sizeof (mreq)) == 0;
459 SocketHelpers::initSockets();
464 portNumber (portNum),
468 jassert (SocketHelpers::isValidPortNumber (portNum));
470 SocketHelpers::initSockets();
471 SocketHelpers::resetSocketOptions (h,
false,
false);
482 return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
483 connected, shouldBlock, readLock)
489 if (isListener || ! connected)
492 return (
int) ::send (handle, (
const char*) sourceBuffer, (juce_recvsend_size_t) numBytesToWrite, 0);
498 return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs)
505 return bindToPort (port,
String());
510 jassert (SocketHelpers::isValidPortNumber (port));
512 return SocketHelpers::bindSocket (handle, port, addr);
517 return SocketHelpers::getBoundPort (handle);
522 jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
534 hostName = remoteHostName;
535 portNumber = remotePortNumber;
538 connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName,
539 remotePortNumber, timeOutMillisecs);
544 if (! SocketHelpers::resetSocketOptions (handle,
false,
false))
556 SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected);
567 jassert (SocketHelpers::isValidPortNumber (newPortNumber));
572 hostName =
"listener";
573 portNumber = newPortNumber;
576 handle = (int) socket (AF_INET, SOCK_STREAM, 0);
581 #if ! JUCE_WINDOWS // on windows, adding this option produces behaviour different to posix 582 SocketHelpers::makeReusable (handle);
585 if (SocketHelpers::bindSocket (handle, portNumber, localHostName)
586 && listen (handle, SOMAXCONN) >= 0)
600 jassert (isListener || ! connected);
602 if (connected && isListener)
604 struct sockaddr_storage address;
605 juce_socklen_t len =
sizeof (address);
606 auto newSocket = (int) accept (handle, (
struct sockaddr*) &address, &len);
608 if (newSocket >= 0 && connected)
609 return new StreamingSocket (inet_ntoa (((
struct sockaddr_in*) &address)->sin_addr),
610 portNumber, newSocket);
621 IPAddress currentIP (SocketHelpers::getConnectedAddress (handle));
627 return hostName ==
"127.0.0.1";
635 SocketHelpers::initSockets();
637 handle = (int) socket (AF_INET, SOCK_DGRAM, 0);
641 SocketHelpers::resetSocketOptions (handle,
true, canBroadcast);
642 SocketHelpers::makeReusable (handle);
648 if (lastServerAddress !=
nullptr)
649 freeaddrinfo (static_cast<struct addrinfo*> (lastServerAddress));
659 std::atomic<int> handleCopy { handle.load() };
662 std::atomic<bool> connected {
false };
663 SocketHelpers::closeSocket (handleCopy, readLock,
false, 0, connected);
670 return bindToPort (port,
String());
675 jassert (SocketHelpers::isValidPortNumber (port));
680 if (SocketHelpers::bindSocket (handle, port, addr))
683 lastBindAddress = addr;
692 return (handle >= 0 && isBound) ? SocketHelpers::getBoundPort (handle) : -1;
701 return SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs);
706 if (handle < 0 || ! isBound)
709 std::atomic<bool> connected {
true };
710 return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
711 connected, shouldBlock, readLock);
716 if (handle < 0 || ! isBound)
719 std::atomic<bool> connected {
true };
720 return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected,
721 shouldBlock, readLock, &senderIPAddress, &senderPort);
725 const void* sourceBuffer,
int numBytesToWrite)
727 jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
732 struct addrinfo*& info =
reinterpret_cast<struct addrinfo*&
> (lastServerAddress);
735 if (info ==
nullptr || remoteHostname != lastServerHost || remotePortNumber != lastServerPort)
740 if ((info = SocketHelpers::getAddressInfo (
true, remoteHostname, remotePortNumber)) ==
nullptr)
743 lastServerHost = remoteHostname;
744 lastServerPort = remotePortNumber;
747 return (
int) ::sendto (handle, (
const char*) sourceBuffer,
748 (juce_recvsend_size_t) numBytesToWrite, 0,
749 info->ai_addr, (socklen_t) info->ai_addrlen);
754 if (handle < 0 || ! isBound)
757 return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress,
true);
762 if (handle < 0 || ! isBound)
765 return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress,
false);
770 if (handle < 0 || ! isBound)
773 return SocketHelpers::setOption<bool> (handle, IPPROTO_IP, IP_MULTICAST_LOOP, enable);
779 ignoreUnused (enabled);
782 return SocketHelpers::setOption (handle,
783 #
if JUCE_WINDOWS || JUCE_LINUX
788 (
int) (enabled ? 1 : 0));
795 #pragma warning (pop) 803 struct SocketTests :
public UnitTest 806 :
UnitTest (
"Sockets", UnitTestCategories::networking)
810 void runTest()
override 815 beginTest (
"StreamingSocket");
824 expect (socketServer.
createListener (portNum, localHost.toString()));
828 expect (socket.
connect (localHost.toString(), portNum));
831 expect (socket.
getHostName() == localHost.toString());
843 beginTest (
"DatagramSocket");
850 expect (socket.
bindToPort (portNum, localHost.toString()));
863 static SocketTests socketTests;
int getRawSocketHandle() const noexcept
Returns the OS's socket handle that's currently open.
bool isConnected() const noexcept
True if the socket is currently connected.
A wrapper for a datagram (UDP) socket.
int read(void *destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived)
Reads bytes from the socket.
bool leaveMulticast(const String &multicastIPAddress)
Leave a multicast group.
bool bindToPort(int localPortNumber)
Binds the socket to the specified local port.
Represents an IP address.
const String & getHostName() const noexcept
Returns the name of the currently connected host.
GenericScopedTryLock< CriticalSection > ScopedTryLockType
Provides the type of scoped try-locker to use with a CriticalSection.
bool setMulticastLoopbackEnabled(bool enableLoopback)
Enables or disables multicast loopback.
bool createListener(int portNumber, const String &localHostName=String())
Puts this socket into "listener" mode.
bool bindToPort(int localPortNumber)
Binds the socket to the specified local port.
void shutdown()
Closes the underlying socket object.
DatagramSocket(bool enableBroadcasting=false)
Creates a datagram socket.
~DatagramSocket()
Destructor.
This is a base class for classes that perform a unit test.
A wrapper for a streaming (TCP) socket.
int waitUntilReady(bool readyForReading, int timeoutMsecs)
Waits until the socket is ready for reading or writing.
~StreamingSocket()
Destructor.
StreamingSocket * waitForNextConnection() const
When in "listener" mode, this waits for a connection and spawns it as a new socket.
int read(void *destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived)
Reads bytes from the socket.
int write(const String &remoteHostname, int remotePortNumber, const void *sourceBuffer, int numBytesToWrite)
Writes bytes to the socket from a buffer.
static Array< IPAddress > getAllAddresses(bool includeIPv6=false)
Populates a list of all the IP addresses that this machine is using.
int getBoundPort() const noexcept
Returns the local port number to which this socket is currently bound.
int getBoundPort() const noexcept
Returns the local port number to which this socket is currently bound.
bool isLocal() const noexcept
True if the socket is connected to this machine rather than over the network.
bool isEmpty() const noexcept
Returns true if the string contains no characters.
bool joinMulticast(const String &multicastIPAddress)
Join a multicast group.
StreamingSocket()
Creates an uninitialised socket.
int waitUntilReady(bool readyForReading, int timeoutMsecs)
Waits until the socket is ready for reading or writing.
int write(const void *sourceBuffer, int numBytesToWrite)
Writes bytes to the socket from a buffer.
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) ...
void close()
Closes the connection.
GenericScopedLock< CriticalSection > ScopedLockType
Provides the type of scoped lock to use with a CriticalSection.
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.
bool setEnablePortReuse(bool enabled)
Allow other applications to re-use the port.
int getRawSocketHandle() const noexcept
Returns the OS's socket handle that's currently open.
bool connect(const String &remoteHostname, int remotePortNumber, int timeOutMillisecs=3000)
Tries to connect the socket to hostname:port.