diff --git a/docs/JUCE changelist.txt b/docs/JUCE changelist.txt index e5a9d80a68..95bea243e0 100644 --- a/docs/JUCE changelist.txt +++ b/docs/JUCE changelist.txt @@ -13,6 +13,7 @@ Changelist for version 1.45 - audio plugins: new methods AudioFilterBase::beginParameterChangeGesture() and endParameterChangeGesture() let you tell the host when a parameter-change action starts and finishes. - new class: FileSearchPathListComponent, for letting the user edit a FileSearchPath. - added a critical section option to ReferenceCountedArray +- refactored and added features to the Socket class, replacing it with StreamableSocket (basically the same as the original class), and DatagramSocket. ============================================================================== Changelist for version 1.44 diff --git a/src/juce_appframework/events/juce_InterprocessConnection.cpp b/src/juce_appframework/events/juce_InterprocessConnection.cpp index b66cf8303d..354b5414a1 100644 --- a/src/juce_appframework/events/juce_InterprocessConnection.cpp +++ b/src/juce_appframework/events/juce_InterprocessConnection.cpp @@ -64,7 +64,7 @@ bool InterprocessConnection::connectToSocket (const String& hostName, disconnect(); const ScopedLock sl (pipeAndSocketLock); - socket = new Socket(); + socket = new StreamingSocket(); if (socket->connect (hostName, portNumber, timeOutMillisecs)) { @@ -186,7 +186,7 @@ bool InterprocessConnection::sendMessage (const MemoryBlock& message) } //============================================================================== -void InterprocessConnection::initialiseWithSocket (Socket* const socket_) +void InterprocessConnection::initialiseWithSocket (StreamingSocket* const socket_) { jassert (socket == 0); socket = socket_; @@ -324,7 +324,7 @@ void InterprocessConnection::run() { if (socket != 0) { - const int ready = socket->isReady (0); + const int ready = socket->waitUntilReady (true, 0); if (ready < 0) { diff --git a/src/juce_appframework/events/juce_InterprocessConnection.h b/src/juce_appframework/events/juce_InterprocessConnection.h index ad669f371b..b346345d9c 100644 --- a/src/juce_appframework/events/juce_InterprocessConnection.h +++ b/src/juce_appframework/events/juce_InterprocessConnection.h @@ -180,7 +180,7 @@ public: private: CriticalSection pipeAndSocketLock; - Socket* socket; + StreamingSocket* socket; NamedPipe* pipe; bool callbackConnectionState; const bool useMessageThread; @@ -190,7 +190,7 @@ private: //============================================================================== friend class InterprocessConnectionServer; - void initialiseWithSocket (Socket* const socket_); + void initialiseWithSocket (StreamingSocket* const socket_); void initialiseWithPipe (NamedPipe* const pipe_); void handleMessage (const Message& message); diff --git a/src/juce_appframework/events/juce_InterprocessConnectionServer.cpp b/src/juce_appframework/events/juce_InterprocessConnectionServer.cpp index ec79f1dde2..a204472e4c 100644 --- a/src/juce_appframework/events/juce_InterprocessConnectionServer.cpp +++ b/src/juce_appframework/events/juce_InterprocessConnectionServer.cpp @@ -53,7 +53,7 @@ bool InterprocessConnectionServer::beginWaitingForSocket (const int portNumber) { stop(); - socket = new Socket(); + socket = new StreamingSocket(); if (socket->createListener (portNumber)) { @@ -81,7 +81,7 @@ void InterprocessConnectionServer::run() { while ((! threadShouldExit()) && socket != 0) { - Socket* const clientSocket = socket->waitForNextConnection(); + StreamingSocket* const clientSocket = socket->waitForNextConnection(); if (clientSocket != 0) { diff --git a/src/juce_appframework/events/juce_InterprocessConnectionServer.h b/src/juce_appframework/events/juce_InterprocessConnectionServer.h index dfc64a8b5d..cf24a10127 100644 --- a/src/juce_appframework/events/juce_InterprocessConnectionServer.h +++ b/src/juce_appframework/events/juce_InterprocessConnectionServer.h @@ -94,7 +94,7 @@ public: juce_UseDebuggingNewOperator private: - Socket* volatile socket; + StreamingSocket* volatile socket; void run(); diff --git a/src/juce_core/basics/juce_SystemStats.cpp b/src/juce_core/basics/juce_SystemStats.cpp index 0167bab144..bf31a1b4fd 100644 --- a/src/juce_core/basics/juce_SystemStats.cpp +++ b/src/juce_core/basics/juce_SystemStats.cpp @@ -83,10 +83,22 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() } } +#if JUCE_WIN32 + // This is imported from the sockets code.. + typedef int (__stdcall juce_CloseWin32SocketLibCall) (void); + extern juce_CloseWin32SocketLibCall* juce_CloseWin32SocketLib; +#endif + void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI() { if (juceInitialisedNonGUI) { +#if JUCE_WIN32 + // need to shut down sockets if they were used.. + if (juce_CloseWin32SocketLib != 0) + (*juce_CloseWin32SocketLib)(); +#endif + LocalisedStrings::setCurrentMappings (0); Thread::stopAllThreads (3000); diff --git a/src/juce_core/io/network/juce_Socket.cpp b/src/juce_core/io/network/juce_Socket.cpp index f55d10c13d..02e5668f0c 100644 --- a/src/juce_core/io/network/juce_Socket.cpp +++ b/src/juce_core/io/network/juce_Socket.cpp @@ -62,76 +62,62 @@ BEGIN_JUCE_NAMESPACE #include "../../threads/juce_Thread.h" +//============================================================================== #if JUCE_WIN32 - static CriticalSection socketInitLock; - static int numActiveSockets = 0; -#endif -//============================================================================== -Socket::Socket() - : portNumber (0), - handle (-1), - connected (false), - isListener (false) +typedef int (__stdcall juce_CloseWin32SocketLibCall) (void); +juce_CloseWin32SocketLibCall* juce_CloseWin32SocketLib = 0; + +static void initWin32Sockets() { -#if JUCE_WIN32 - const ScopedLock sl (socketInitLock); + static CriticalSection lock; + const ScopedLock sl (lock); - if (numActiveSockets++ == 0) + if (juce_CloseWin32SocketLib == 0) { WSADATA wsaData; - WORD wVersionRequested = MAKEWORD (1, 1); + const WORD wVersionRequested = MAKEWORD (1, 1); WSAStartup (wVersionRequested, &wsaData); - } -#endif -} - -Socket::Socket (const String& hostName_, const int portNumber_, const int handle_) - : hostName (hostName_), - portNumber (portNumber_), - handle (handle_), - connected (true), - isListener (false) -{ -#if JUCE_WIN32 - socketInitLock.enter(); - ++numActiveSockets; - socketInitLock.exit(); -#endif - resetSocketOptions(); + juce_CloseWin32SocketLib = &WSACleanup; + } } -Socket::~Socket() -{ - close(); - -#if JUCE_WIN32 - const ScopedLock sl (socketInitLock); - - if (--numActiveSockets == 0) - WSACleanup(); #endif -} //============================================================================== -bool Socket::resetSocketOptions() +static bool resetSocketOptions (const int handle, const bool isDatagram) throw() { + if (handle <= 0) + return false; + const int sndBufSize = 65536; const int rcvBufSize = 65536; const int one = 1; return setsockopt (handle, SOL_SOCKET, SO_RCVBUF, (const char*) &rcvBufSize, sizeof (int)) == 0 && setsockopt (handle, SOL_SOCKET, SO_SNDBUF, (const char*) &sndBufSize, sizeof (int)) == 0 - && setsockopt (handle, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof (int)) == 0; + && (isDatagram || (setsockopt (handle, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof (int)) == 0)); } -//============================================================================== -int Socket::read (void* destBuffer, const int maxBytesToRead) +static bool bindSocketToPort (const int handle, const int port) throw() { - if (isListener || ! connected) - return -1; + if (handle == 0 || port <= 0) + return false; + + struct sockaddr_in servTmpAddr; + zerostruct (servTmpAddr); + servTmpAddr.sin_family = PF_INET; + servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); + servTmpAddr.sin_port = htons ((uint16) port); + + return bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 0; +} +static int readSocket (const int handle, + void* const destBuffer, const int maxBytesToRead, + bool volatile& connected) throw() +{ int bytesRead = 0; while (bytesRead < maxBytesToRead) @@ -162,31 +148,9 @@ int Socket::read (void* destBuffer, const int maxBytesToRead) return bytesRead; } -int Socket::write (const void* sourceBuffer, int numBytesToWrite) -{ - if (isListener || ! connected) - return -1; - -#if JUCE_WIN32 - return send (handle, (const char*) sourceBuffer, numBytesToWrite, 0); -#else - int result; - - while ((result = ::write (handle, sourceBuffer, numBytesToWrite)) < 0 - && errno == EINTR) - { - } - - return result; -#endif -} - -//============================================================================== -int Socket::isReady (int timeoutMsecs) +static int waitForReadiness (const int handle, const bool forReading, + const int timeoutMsecs) throw() { - if (! connected) - return -1; - struct timeval timeout; struct timeval* timeoutp; @@ -201,16 +165,21 @@ int Socket::isReady (int timeoutMsecs) timeoutp = 0; } - fd_set readbits; - FD_ZERO (&readbits); - FD_SET (handle, &readbits); + fd_set rset, wset; + FD_ZERO (&rset); + FD_SET (handle, &rset); + FD_ZERO (&wset); + FD_SET (handle, &wset); + + fd_set* const prset = forReading ? &rset : 0; + fd_set* const pwset = forReading ? 0 : &wset; #if JUCE_WIN32 - if (select (handle + 1, &readbits, 0, 0, timeoutp) < 0) + if (select (handle + 1, prset, pwset, 0, timeoutp) < 0) return -1; #else int result; - while ((result = select (handle + 1, &readbits, 0, 0, timeoutp)) < 0 + while ((result = select (handle + 1, prset, pwset, 0, timeoutp)) < 0 && errno == EINTR) { } @@ -219,33 +188,48 @@ int Socket::isReady (int timeoutMsecs) return -1; #endif - if (FD_ISSET (handle, &readbits)) + if ((forReading && FD_ISSET (handle, &rset)) + || ((! forReading) && FD_ISSET (handle, &wset))) return 1; return 0; } -//============================================================================== -bool Socket::connect (const String& newHostName, - int newPortNumber, - int timeOutMillisecs) +static bool setSocketBlockingState (const int handle, const bool shouldBlock) throw() { - if (isListener) - { - jassertfalse // a listener socket can't connect to another one! +#if JUCE_WIN32 + u_long nonBlocking = shouldBlock ? 0 : 1; + + if (ioctlsocket (handle, FIONBIO, &nonBlocking) != 0) return false; - } +#else + int socketFlags = fcntl (handle, F_GETFL, 0); - if (connected) - close(); + if (socketFlags == -1) + return false; - hostName = newHostName; - portNumber = newPortNumber; - isListener = false; + if (shouldBlock) + socketFlags &= ~O_NONBLOCK; + else + socketFlags |= O_NONBLOCK; + + if (fcntl (handle, F_SETFL, socketFlags) != 0) + return false; +#endif + + return true; +} - struct hostent* hostEnt = gethostbyname (hostName); +static bool connectSocket (int volatile& handle, + const bool isDatagram, + void** serverAddress, + const String& hostName, + const int portNumber, + const int timeOutMillisecs) throw() +{ + struct hostent* const hostEnt = gethostbyname (hostName); - if (! hostEnt) + if (hostEnt == 0) return false; struct in_addr targetAddress; @@ -259,31 +243,136 @@ bool Socket::connect (const String& newHostName, servTmpAddr.sin_addr = targetAddress; servTmpAddr.sin_port = htons ((uint16) portNumber); - handle = (int) socket (AF_INET, SOCK_STREAM, 0); + handle = (int) socket (AF_INET, isDatagram ? SOCK_DGRAM : SOCK_STREAM, 0); if (handle < 0) return false; - while (timeOutMillisecs > 0 || timeOutMillisecs < 0) + if (isDatagram) { - if (handle < 0) - return false; + *serverAddress = new struct sockaddr_in(); + *((struct sockaddr_in*) *serverAddress) = servTmpAddr; - if (::connect (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 0) - { - connected = true; - break; - } + return true; + } + + setSocketBlockingState (handle, false); + + const int result = ::connect (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)); - if (timeOutMillisecs > 0) + if (result < 0) + { +#if JUCE_WIN32 + if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK) +#else + if (result == EINPROGRESS) +#endif { - const int timeToSleep = jmin (timeOutMillisecs, 1000); - timeOutMillisecs -= timeToSleep; - Thread::sleep (timeToSleep); + if (waitForReadiness (handle, false, timeOutMillisecs) != 1) + return false; } } + + setSocketBlockingState (handle, true); + resetSocketOptions (handle, false); + + return true; +} + +//============================================================================== +StreamingSocket::StreamingSocket() + : portNumber (0), + handle (-1), + connected (false), + isListener (false) +{ +#if JUCE_WIN32 + initWin32Sockets(); +#endif +} + +StreamingSocket::StreamingSocket (const String& hostName_, + const int portNumber_, + const int handle_) + : hostName (hostName_), + portNumber (portNumber_), + handle (handle_), + connected (true), + isListener (false) +{ +#if JUCE_WIN32 + initWin32Sockets(); +#endif + + resetSocketOptions (handle_, false); +} + +StreamingSocket::~StreamingSocket() +{ + close(); +} + +//============================================================================== +int StreamingSocket::read (void* destBuffer, const int maxBytesToRead) +{ + return (connected && ! isListener) ? readSocket (handle, destBuffer, maxBytesToRead, connected) + : -1; +} + +int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite) +{ + if (isListener || ! connected) + return -1; + +#if JUCE_WIN32 + return send (handle, (const char*) sourceBuffer, numBytesToWrite, 0); +#else + int result; + + while ((result = ::write (handle, sourceBuffer, numBytesToWrite)) < 0 + && errno == EINTR) + { + } + + return result; +#endif +} + +//============================================================================== +int StreamingSocket::waitUntilReady (const bool readyForReading, + const int timeoutMsecs) const +{ + return connected ? waitForReadiness (handle, readyForReading, timeoutMsecs) + : -1; +} + +//============================================================================== +bool StreamingSocket::bindToPort (const int port) +{ + return bindSocketToPort (handle, port); +} + +bool StreamingSocket::connect (const String& remoteHostName, + const int remotePortNumber, + const int timeOutMillisecs) +{ + if (isListener) + { + jassertfalse // a listener socket can't connect to another one! + return false; + } + + if (connected) + close(); + + hostName = remoteHostName; + portNumber = remotePortNumber; + isListener = false; + + connected = connectSocket (handle, false, 0, remoteHostName, + remotePortNumber, timeOutMillisecs); - if (! (connected && resetSocketOptions())) + if (! (connected && resetSocketOptions (handle, false))) { close(); return false; @@ -292,10 +381,11 @@ bool Socket::connect (const String& newHostName, return true; } -void Socket::close() +void StreamingSocket::close() { #if JUCE_WIN32 closesocket (handle); + connected = false; #else if (connected) { @@ -304,7 +394,7 @@ void Socket::close() if (isListener) { // need to do this to interrupt the accept() function.. - Socket temp; + StreamingSocket temp; temp.connect ("localhost", portNumber, 1000); } } @@ -315,12 +405,11 @@ void Socket::close() hostName = String::empty; portNumber = 0; handle = -1; - connected = false; isListener = false; } //============================================================================== -bool Socket::createListener (int newPortNumber) +bool StreamingSocket::createListener (const int newPortNumber) { if (connected) close(); @@ -354,7 +443,7 @@ bool Socket::createListener (int newPortNumber) return true; } -Socket* Socket::waitForNextConnection() +StreamingSocket* StreamingSocket::waitForNextConnection() const { jassert (isListener || ! connected); // to call this method, you first have to use createListener() to // prepare this socket as a listener. @@ -371,15 +460,153 @@ Socket* Socket::waitForNextConnection() const int newSocket = (int) accept (handle, &address, &len); if (newSocket >= 0 && connected) - return new Socket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr), - portNumber, newSocket); + return new StreamingSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr), + portNumber, newSocket); + } + + return 0; +} + +bool StreamingSocket::isLocal() const throw() +{ + return hostName == T("127.0.0.1"); +} + + +//============================================================================== +//============================================================================== +DatagramSocket::DatagramSocket (const int localPortNumber) + : portNumber (0), + handle (-1), + connected (false), + serverAddress (0) +{ +#if JUCE_WIN32 + initWin32Sockets(); +#endif + + bindToPort (localPortNumber); +} + +DatagramSocket::DatagramSocket (const String& hostName_, const int portNumber_, + const int handle_, const int localPortNumber) + : hostName (hostName_), + portNumber (portNumber_), + handle (handle_), + connected (true), + serverAddress (0) +{ +#if JUCE_WIN32 + initWin32Sockets(); +#endif + + resetSocketOptions (handle_, true); + bindToPort (localPortNumber); +} + +DatagramSocket::~DatagramSocket() +{ + close(); + + delete ((struct sockaddr_in*) serverAddress); + serverAddress = 0; +} + +void DatagramSocket::close() +{ +#if JUCE_WIN32 + closesocket (handle); + connected = false; +#else + connected = false; + ::close (handle); +#endif + + hostName = String::empty; + portNumber = 0; + handle = -1; +} + +bool DatagramSocket::bindToPort (const int port) +{ + return bindSocketToPort (handle, port); +} + +bool DatagramSocket::connect (const String& remoteHostName, + const int remotePortNumber, + const int timeOutMillisecs) +{ + if (connected) + close(); + + hostName = remoteHostName; + portNumber = remotePortNumber; + + connected = connectSocket (handle, true, &serverAddress, + remoteHostName, remotePortNumber, + timeOutMillisecs); + + if (! (connected && resetSocketOptions (handle, true))) + { + close(); + return false; + } + + return true; +} + +DatagramSocket* DatagramSocket::waitForNextConnection() const +{ + struct sockaddr address; + +#if defined (JUCE_LINUX) || (defined (JUCE_MAC) && ! MACOS_10_2_OR_EARLIER) + socklen_t len = sizeof (sockaddr); +#else + int len = sizeof (sockaddr); +#endif + + while (waitUntilReady (true, -1) == 1) + { + char buf[1]; + + if (recvfrom (handle, buf, 0, 0, &address, &len) > 0) + { + return new DatagramSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr), + ntohs (((struct sockaddr_in*) &address)->sin_port), + -1, -1); + } } return 0; } //============================================================================== -bool Socket::isLocal() const throw() +int DatagramSocket::waitUntilReady (const bool readyForReading, + const int timeoutMsecs) const +{ + return connected ? waitForReadiness (handle, readyForReading, timeoutMsecs) + : -1; +} + +int DatagramSocket::read (void* destBuffer, const int maxBytesToRead) +{ + return connected ? readSocket (handle, destBuffer, maxBytesToRead, connected) + : -1; +} + +int DatagramSocket::write (const void* sourceBuffer, const int numBytesToWrite) +{ + // You need to call connect() first to set the server address.. + jassert (serverAddress != 0 && connected); + + return connected ? sendto (handle, (const char*) sourceBuffer, + numBytesToWrite, 0, + (const struct sockaddr*) serverAddress, + sizeof (struct sockaddr_in)) + : -1; +} + +bool DatagramSocket::isLocal() const throw() { return hostName == T("127.0.0.1"); } diff --git a/src/juce_core/io/network/juce_Socket.h b/src/juce_core/io/network/juce_Socket.h index 06d474abe6..91a1fab6a7 100644 --- a/src/juce_core/io/network/juce_Socket.h +++ b/src/juce_core/io/network/juce_Socket.h @@ -37,14 +37,14 @@ //============================================================================== /** - A wrapper for a socket. + A wrapper for a streaming (TCP) socket. - Allows low-level use of sockets; for an easier-to-use messaging layer on top of + This allows low-level use of sockets; for an easier-to-use messaging layer on top of sockets, you could also try the InterprocessConnection class. - @see InterprocessConnection, InterprocessConnectionServer + @see DatagramSocket, InterprocessConnection, InterprocessConnectionServer */ -class JUCE_API Socket +class JUCE_API StreamingSocket { public: //============================================================================== @@ -57,49 +57,37 @@ public: enters "listener" mode, and can be used to spawn new sockets for each connection that comes along. */ - Socket(); + StreamingSocket(); /** Destructor. */ - ~Socket(); + ~StreamingSocket(); //============================================================================== - // Tests if the socket is ready - // Returns: 1 == yes, 0 == no, -1 == error - int isReady (int timeoutMsecs = 0); + /** Binds the socket to the specified local port. - //============================================================================== - /** Reads bytes from the socket (blocking). - - Returns the number of bytes read, or -1 if there was an error. - */ - int read (void* destBuffer, const int maxBytesToRead); - - /** Writes bytes to the socket from a buffer. - - This may block on error conditions. - - Returns the number of bytes written, or -1 if there was an error. + @returns true on success; false may indicate that another socket is already bound + on the same port */ - int write (const void* sourceBuffer, int numBytesToWrite); + bool bindToPort (const int localPortNumber); - //============================================================================== /** Tries to connect the socket to hostname:port. - Returns true if it succeeds. + If timeOutMillisecs is 0, then this method will block until the operating system + rejects the connection (which could take a long time). + @returns true if it succeeds. @see isConnected */ - bool connect (const String& hostname, - int portNumber, - int timeOutMillisecs = 3000); + bool connect (const String& remoteHostname, + const int remotePortNumber, + const int timeOutMillisecs = 3000); - /** Closes the connection. */ - void close(); - - //============================================================================== /** True if the socket is currently connected. */ bool isConnected() const throw() { return connected; } + /** Closes the connection. */ + void close(); + /** Returns the name of the currently connected host. */ const String& getHostName() const throw() { return hostName; } @@ -109,6 +97,39 @@ public: /** True if the socket is connected to this machine rather than over the network. */ bool isLocal() const throw(); + //============================================================================== + /** Waits until the socket is ready for reading or writing. + + If readyForReading is true, it will wait until the socket is ready for + reading; if false, it will wait until it's ready for writing. + + If the timeout is < 0, it will wait forever, or else will give up after + the specified time. + + If the socket is ready on return, this returns 1. If it times-out before + the socket becomes ready, it returns 0. If an error occurs, it returns -1. + */ + int waitUntilReady (const bool readyForReading, + const int timeoutMsecs) const; + + /** Reads bytes from the socket (blocking). + + Note that this method will block unless you have checked the socket is ready + for reading before calling it (see the waitUntilReady() method). + + @returns the number of bytes read, or -1 if there was an error. + */ + int read (void* destBuffer, const int maxBytesToRead); + + /** Writes bytes to the socket from a buffer. + + Note that this method will block unless you have checked the socket is ready + for writing before calling it (see the waitUntilReady() method). + + @returns the number of bytes written, or -1 if there was an error. + */ + int write (const void* sourceBuffer, const int numBytesToWrite); + //============================================================================== /** Puts this socket into "listener" mode. @@ -120,7 +141,7 @@ public: @see waitForNextConnection */ - bool createListener (int portNumber); + bool createListener (const int portNumber); /** When in "listener" mode, this waits for a connection and spawns it as a new socket. @@ -131,7 +152,7 @@ public: @see createListener */ - Socket* waitForNextConnection(); + StreamingSocket* waitForNextConnection() const; //============================================================================== @@ -142,11 +163,132 @@ private: int volatile portNumber, handle; bool connected, isListener; - Socket (const String& hostname, const int portNumber, const int handle); - Socket (const Socket&); - const Socket& operator= (const Socket&); + StreamingSocket (const String& hostname, const int portNumber, const int handle); + StreamingSocket (const StreamingSocket&); + const StreamingSocket& operator= (const StreamingSocket&); +}; + + +//============================================================================== +/** + A wrapper for a datagram (UDP) socket. + + This allows low-level use of sockets; for an easier-to-use messaging layer on top of + sockets, you could also try the InterprocessConnection class. + + @see StreamingSocket, InterprocessConnection, InterprocessConnectionServer +*/ +class JUCE_API DatagramSocket +{ +public: + //============================================================================== + /** + Creates an (uninitialised) datagram socket. + + The localPortNumber is the port on which to bind this socket. If this value is 0, + the port number is assigned by the operating system. + + To use the socket for sending, call the connect() method. This will not immediately + make a connection, but will save the destination you've provided. After this, you can + call read() or write(). + + To wait for other sockets to connect to this one, call waitForNextConnection(). + */ + DatagramSocket (const int localPortNumber); + + /** Destructor. */ + ~DatagramSocket(); + + //============================================================================== + /** Binds the socket to the specified local port. + + @returns true on success; false may indicate that another socket is already bound + on the same port + */ + bool bindToPort (const int localPortNumber); + + /** Tries to connect the socket to hostname:port. + + If timeOutMillisecs is 0, then this method will block until the operating system + rejects the connection (which could take a long time). + + @returns true if it succeeds. + @see isConnected + */ + bool connect (const String& remoteHostname, + const int remotePortNumber, + const int timeOutMillisecs = 3000); + + /** True if the socket is currently connected. */ + bool isConnected() const throw() { return connected; } + + /** Closes the connection. */ + void close(); + + /** Returns the name of the currently connected host. */ + const String& getHostName() const throw() { return hostName; } + + /** Returns the port number that's currently open. */ + int getPort() const throw() { return portNumber; } + + /** True if the socket is connected to this machine rather than over the network. */ + bool isLocal() const throw(); + + //============================================================================== + /** Waits until the socket is ready for reading or writing. + + If readyForReading is true, it will wait until the socket is ready for + reading; if false, it will wait until it's ready for writing. + + If the timeout is < 0, it will wait forever, or else will give up after + the specified time. + + If the socket is ready on return, this returns 1. If it times-out before + the socket becomes ready, it returns 0. If an error occurs, it returns -1. + */ + int waitUntilReady (const bool readyForReading, + const int timeoutMsecs) const; + + /** Reads bytes from the socket (blocking). + + Note that this method will block unless you have checked the socket is ready + for reading before calling it (see the waitUntilReady() method). + + @returns the number of bytes read, or -1 if there was an error. + */ + int read (void* destBuffer, const int maxBytesToRead); + + /** Writes bytes to the socket from a buffer. + + Note that this method will block unless you have checked the socket is ready + for writing before calling it (see the waitUntilReady() method). + + @returns the number of bytes written, or -1 if there was an error. + */ + int write (const void* sourceBuffer, const int numBytesToWrite); + + //============================================================================== + /** This waits for incoming data to be sent, and returns a socket that can be used + to read it. + + The object that gets returned is owned by the caller, and can't be used for + sending, but can be used to read the data. + */ + DatagramSocket* waitForNextConnection() const; + + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + String hostName; + int volatile portNumber, handle; + bool connected; + void* serverAddress; - bool resetSocketOptions(); + DatagramSocket (const String& hostname, const int portNumber, const int handle, const int localPortNumber); + DatagramSocket (const DatagramSocket&); + const DatagramSocket& operator= (const DatagramSocket&); };