27 #pragma warning (push)
28 #pragma warning (disable : 4127 4389 4018)
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;
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
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();
468 jassert (SocketHelpers::isValidPortNumber (
portNum));
470 SocketHelpers::initSockets();
471 SocketHelpers::resetSocketOptions (h,
false,
false);
489 if (isListener || ! connected)
510 jassert (SocketHelpers::isValidPortNumber (port));
512 return SocketHelpers::bindSocket (handle, port,
addr);
517 return SocketHelpers::getBoundPort (handle);
538 connected = SocketHelpers::connectSocket (handle, readLock,
remoteHostName,
544 if (! SocketHelpers::resetSocketOptions (handle,
false,
false))
556 SocketHelpers::closeSocket (handle, readLock, isListener, portNumber, connected);
572 hostName =
"listener";
582 SocketHelpers::makeReusable (handle);
585 if (SocketHelpers::bindSocket (handle, portNumber,
localHostName)
600 jassert (isListener || ! connected);
602 if (connected && isListener)
627 return hostName ==
"127.0.0.1";
635 SocketHelpers::initSockets();
641 SocketHelpers::resetSocketOptions (handle,
true,
canBroadcast);
642 SocketHelpers::makeReusable (handle);
648 if (lastServerAddress !=
nullptr)
659 std::atomic<int>
handleCopy { handle.load() };
662 std::atomic<bool> connected {
false };
663 SocketHelpers::closeSocket (
handleCopy, readLock,
false, 0, connected);
675 jassert (SocketHelpers::isValidPortNumber (port));
680 if (SocketHelpers::bindSocket (handle, port,
addr))
683 lastBindAddress =
addr;
692 return (handle >= 0 && isBound) ? SocketHelpers::getBoundPort (handle) : -1;
706 if (handle < 0 || ! isBound)
709 std::atomic<bool> connected {
true };
716 if (handle < 0 || ! isBound)
719 std::atomic<bool> connected {
true };
747 return (
int) ::sendto (handle, (
const char*)
sourceBuffer,
754 if (handle < 0 || ! isBound)
762 if (handle < 0 || ! isBound)
770 if (handle < 0 || ! isBound)
779 ignoreUnused (enabled);
782 return SocketHelpers::setOption (handle,
788 (
int) (enabled ? 1 : 0));
795 #pragma warning (pop)
806 :
UnitTest (
"Sockets", UnitTestCategories::networking)
810 void runTest()
override
815 beginTest (
"StreamingSocket");
822 expect (
socketServer.getRawSocketHandle() == invalidSocket);
826 StreamingSocket socket;
830 expect (socket.isConnected() ==
true);
831 expect (socket.getHostName() ==
localHost.toString());
832 expect (socket.getBoundPort() != -1);
833 expect (socket.getRawSocketHandle() != invalidSocket);
837 expect (socket.isConnected() ==
false);
838 expect (socket.getHostName().isEmpty());
839 expect (socket.getBoundPort() == -1);
840 expect (socket.getRawSocketHandle() == invalidSocket);
843 beginTest (
"DatagramSocket");
845 DatagramSocket socket;
847 expect (socket.getBoundPort() == -1);
848 expect (socket.getRawSocketHandle() != invalidSocket);
852 expect (socket.getBoundPort() ==
portNum);
853 expect (socket.getRawSocketHandle() != invalidSocket);
857 expect (socket.getBoundPort() == -1);
858 expect (socket.getRawSocketHandle() == invalidSocket);
863static SocketTests socketTests;
GenericScopedLock< CriticalSection > ScopedLockType
GenericScopedTryLock< CriticalSection > ScopedTryLockType
DatagramSocket(bool enableBroadcasting=false)
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)
static IPAddress local(bool IPv6=false) noexcept
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)