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
bool isConnected() const noexcept
int read(void *destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived)
bool leaveMulticast(const String &multicastIPAddress)
bool bindToPort(int localPortNumber)
const String & getHostName() const noexcept
GenericScopedTryLock< CriticalSection > ScopedTryLockType
bool setMulticastLoopbackEnabled(bool enableLoopback)
bool createListener(int portNumber, const String &localHostName=String())
bool bindToPort(int localPortNumber)
DatagramSocket(bool enableBroadcasting=false)
int waitUntilReady(bool readyForReading, int timeoutMsecs)
StreamingSocket * waitForNextConnection() const
int read(void *destBuffer, int maxBytesToRead, bool blockUntilSpecifiedAmountHasArrived)
int write(const String &remoteHostname, int remotePortNumber, const void *sourceBuffer, int numBytesToWrite)
static Array< IPAddress > getAllAddresses(bool includeIPv6=false)
int getBoundPort() const noexcept
int getBoundPort() const noexcept
bool isLocal() const noexcept
bool isEmpty() const noexcept
bool joinMulticast(const String &multicastIPAddress)
int waitUntilReady(bool readyForReading, int timeoutMsecs)
int write(const void *sourceBuffer, int numBytesToWrite)
static IPAddress local(bool IPv6=false) noexcept
GenericScopedLock< CriticalSection > ScopedLockType
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
bool setEnablePortReuse(bool enabled)
int getRawSocketHandle() const noexcept
bool connect(const String &remoteHostname, int remotePortNumber, int timeOutMillisecs=3000)