27 #pragma warning (push)
28 #pragma warning (disable : 4127 4389 4018)
32 #define AI_NUMERICSERV 0x1000
36 typedef int juce_socklen_t;
37 typedef int juce_recvsend_size_t;
38 typedef SOCKET SocketHandle;
39 static const SocketHandle invalidSocket = INVALID_SOCKET;
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;
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;
53namespace 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
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 != (
unsigned) SOCKET_ERROR || connected)
120 StreamingSocket temp;
121 temp.connect (IPAddress::local().toString(), portNumber, 1000);
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 <= 0 || ! 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
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);
186 return String (
"0.0.0.0");
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
199 while (bytesRead < maxBytesToRead)
201 long bytesThisTime = -1;
202 auto buffer =
static_cast<char*
> (destBuffer) + bytesRead;
203 auto numToRead = (juce_recvsend_size_t) (maxBytesToRead - bytesRead);
211 if (senderIP ==
nullptr || senderPort ==
nullptr)
213 bytesThisTime = ::recv (handle, buffer, numToRead, 0);
218 socklen_t clientLen =
sizeof (sockaddr);
220 bytesThisTime = ::recvfrom (handle, buffer, numToRead, 0, (sockaddr*) &client, &clientLen);
223 *senderPort = ntohs (client.sin_port);
228 if (bytesThisTime <= 0 || ! connected)
230 if (bytesRead == 0 && blockUntilSpecifiedAmountHasArrived)
236 bytesRead += bytesThisTime;
238 if (! blockUntilSpecifiedAmountHasArrived)
242 return (
int) bytesRead;
245 static int waitForReadiness (std::atomic<int>& handle, CriticalSection& readLock,
246 bool forReading,
int timeoutMsecs)
noexcept
251 if (! lock.isLocked())
254 int h = handle.load();
256 struct timeval timeout;
257 struct timeval* timeoutp;
259 if (timeoutMsecs >= 0)
261 timeout.tv_sec = timeoutMsecs / 1000;
262 timeout.tv_usec = (timeoutMsecs % 1000) * 1000;
276 fd_set*
const prset = forReading ? &rset :
nullptr;
277 fd_set*
const pwset = forReading ? nullptr : &wset;
280 if (select ((
int) h + 1, prset, pwset, 0, timeoutp) < 0)
286 while ((result = select (h + 1, prset, pwset,
nullptr, timeoutp)) < 0
297 if (handle.load() < 0)
302 juce_socklen_t len =
sizeof (opt);
304 if (getsockopt (h, SOL_SOCKET, SO_ERROR, (
char*) &opt, &len) < 0
309 return FD_ISSET (h, forReading ? &rset : &wset) ? 1 : 0;
312 static bool setSocketBlockingState (SocketHandle handle,
bool shouldBlock)
noexcept
315 u_long nonBlocking = shouldBlock ? 0 : (u_long) 1;
316 return ioctlsocket (handle, FIONBIO, &nonBlocking) == 0;
318 int socketFlags = fcntl (handle, F_GETFL, 0);
320 if (socketFlags == -1)
324 socketFlags &= ~O_NONBLOCK;
326 socketFlags |= O_NONBLOCK;
328 return fcntl (handle, F_SETFL, socketFlags) == 0;
332 static addrinfo* getAddressInfo (
bool isDatagram,
const String& hostName,
int portNumber)
334 struct addrinfo hints;
337 hints.ai_family = AF_UNSPEC;
338 hints.ai_socktype = isDatagram ? SOCK_DGRAM : SOCK_STREAM;
339 hints.ai_flags = AI_NUMERICSERV;
341 struct addrinfo* info =
nullptr;
343 if (getaddrinfo (hostName.toRawUTF8(), String (portNumber).toRawUTF8(), &hints, &info) == 0)
349 static bool connectSocket (std::atomic<int>& handle,
350 CriticalSection& readLock,
351 const String& hostName,
353 int timeOutMillisecs)
noexcept
355 bool success =
false;
357 if (
auto* info = getAddressInfo (
false, hostName, portNumber))
359 for (
auto* i = info; i !=
nullptr; i = i->ai_next)
361 auto newHandle = socket (i->ai_family, i->ai_socktype, 0);
363 if (newHandle != invalidSocket)
365 setSocketBlockingState (newHandle,
false);
366 auto result = ::connect (newHandle, i->ai_addr, (socklen_t) i->ai_addrlen);
367 success = (result >= 0);
372 if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
374 if (errno == EINPROGRESS)
377 std::atomic<int> cvHandle { (int) newHandle };
379 if (waitForReadiness (cvHandle, readLock,
false, timeOutMillisecs) == 1)
386 handle = (int) newHandle;
391 closesocket (newHandle);
402 setSocketBlockingState (handle,
true);
403 resetSocketOptions (handle,
false,
false);
410 static void makeReusable (
int handle)
noexcept
412 setOption (handle, SO_REUSEADDR, (
int) 1);
415 static bool multicast (
int handle,
const String& multicastIPAddress,
416 const String& interfaceIPAddress,
bool join)
noexcept
421 mreq.imr_multiaddr.s_addr = inet_addr (multicastIPAddress.toRawUTF8());
422 mreq.imr_interface.s_addr = INADDR_ANY;
424 if (interfaceIPAddress.isNotEmpty())
425 mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toRawUTF8());
427 return setsockopt (handle, IPPROTO_IP,
428 join ? IP_ADD_MEMBERSHIP
429 : IP_DROP_MEMBERSHIP,
430 (const char*) &mreq, sizeof (mreq)) == 0;
437 SocketHelpers::initSockets();
442 portNumber (portNum),
446 jassert (SocketHelpers::isValidPortNumber (portNum));
448 SocketHelpers::initSockets();
449 SocketHelpers::resetSocketOptions (h,
false,
false);
460 return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
461 connected, shouldBlock, readLock)
467 if (isListener || ! connected)
470 return (
int) ::send (handle, (
const char*) sourceBuffer, (juce_recvsend_size_t) numBytesToWrite, 0);
476 return connected ? SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs)
488 jassert (SocketHelpers::isValidPortNumber (port));
490 return SocketHelpers::bindSocket (handle, port, addr);
495 return SocketHelpers::getBoundPort (handle);
500 jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
511 hostName = remoteHostName;
512 portNumber = remotePortNumber;
515 connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName,
516 remotePortNumber, timeOutMillisecs);
518 if (! (connected && SocketHelpers::resetSocketOptions (handle,
false,
false)))
529 SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected);
540 jassert (SocketHelpers::isValidPortNumber (newPortNumber));
545 hostName =
"listener";
546 portNumber = newPortNumber;
549 handle = (int) socket (AF_INET, SOCK_STREAM, 0);
555 SocketHelpers::makeReusable (handle);
558 if (SocketHelpers::bindSocket (handle, portNumber, localHostName)
559 && listen (handle, SOMAXCONN) >= 0)
573 jassert (isListener || ! connected);
575 if (connected && isListener)
577 struct sockaddr_storage address;
578 juce_socklen_t len =
sizeof (address);
579 auto newSocket = (int) accept (handle, (
struct sockaddr*) &address, &len);
581 if (newSocket >= 0 && connected)
582 return new StreamingSocket (inet_ntoa (((
struct sockaddr_in*) &address)->sin_addr),
583 portNumber, newSocket);
594 IPAddress currentIP (SocketHelpers::getConnectedAddress (handle));
600 return hostName ==
"127.0.0.1";
608 SocketHelpers::initSockets();
610 handle = (int) socket (AF_INET, SOCK_DGRAM, 0);
614 SocketHelpers::resetSocketOptions (handle,
true, canBroadcast);
615 SocketHelpers::makeReusable (handle);
621 if (lastServerAddress !=
nullptr)
622 freeaddrinfo (
static_cast<struct addrinfo*
> (lastServerAddress));
632 std::atomic<int> handleCopy { handle.load() };
634 std::atomic<bool> connected {
false };
635 SocketHelpers::closeSocket (handleCopy, readLock,
false, 0, connected);
645 jassert (SocketHelpers::isValidPortNumber (port));
647 if (SocketHelpers::bindSocket (handle, port, addr))
650 lastBindAddress = addr;
659 return (handle >= 0 && isBound) ? SocketHelpers::getBoundPort (handle) : -1;
668 return SocketHelpers::waitForReadiness (handle, readLock, readyForReading, timeoutMsecs);
673 if (handle < 0 || ! isBound)
676 std::atomic<bool> connected {
true };
678 SocketHelpers::setSocketBlockingState (handle, shouldBlock);
679 return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
680 connected, shouldBlock, readLock);
685 if (handle < 0 || ! isBound)
688 std::atomic<bool> connected {
true };
690 SocketHelpers::setSocketBlockingState (handle, shouldBlock);
691 return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected,
692 shouldBlock, readLock, &senderIPAddress, &senderPort);
696 const void* sourceBuffer,
int numBytesToWrite)
698 jassert (SocketHelpers::isValidPortNumber (remotePortNumber));
703 struct addrinfo*& info =
reinterpret_cast<struct addrinfo*&
> (lastServerAddress);
706 if (info ==
nullptr || remoteHostname != lastServerHost || remotePortNumber != lastServerPort)
711 if ((info = SocketHelpers::getAddressInfo (
true, remoteHostname, remotePortNumber)) ==
nullptr)
714 lastServerHost = remoteHostname;
715 lastServerPort = remotePortNumber;
718 return (
int) ::sendto (handle, (
const char*) sourceBuffer,
719 (juce_recvsend_size_t) numBytesToWrite, 0,
720 info->ai_addr, (socklen_t) info->ai_addrlen);
725 if (! isBound || handle < 0)
728 return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress,
true);
733 if (! isBound || handle < 0)
736 return SocketHelpers::multicast (handle, multicastIPAddress, lastBindAddress,
false);
741 if (! isBound || handle < 0)
744 return SocketHelpers::setOption<bool> (handle, IPPROTO_IP, IP_MULTICAST_LOOP, enable);
750 ignoreUnused (enabled);
753 return SocketHelpers::setOption (handle,
754 #
if JUCE_WINDOWS || JUCE_LINUX
759 (
int) (enabled ? 1 : 0));
766 #pragma warning (pop)
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.
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.