1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Add SocketOptions and avoid reducing the system default buffer sizes

This change affects the DatagramSocket and StreamingSocket classes.
This commit is contained in:
Attila Szarvas 2023-12-01 16:55:30 +00:00
parent 005040da77
commit 62bba21655
2 changed files with 131 additions and 15 deletions

View file

@ -82,11 +82,38 @@ namespace SocketHelpers
return setOption (handle, SOL_SOCKET, property, value);
}
static bool resetSocketOptions (SocketHandle handle, bool isDatagram, bool allowBroadcast) noexcept
static std::optional<int> getBufferSize (SocketHandle handle, int property)
{
int result;
socklen_t outParamSize = sizeof (result);
if (getsockopt (handle, SOL_SOCKET, property, reinterpret_cast<char*> (&result), &outParamSize) != 0
|| outParamSize != sizeof (result))
{
return std::nullopt;
}
return result;
}
static bool resetSocketOptions (SocketHandle handle, bool isDatagram, bool allowBroadcast, const SocketOptions& options) noexcept
{
auto getCurrentBufferSizeWithMinimum = [handle] (int property)
{
constexpr auto minBufferSize = 65536;
if (auto currentBufferSize = getBufferSize (handle, property))
return std::max (*currentBufferSize, minBufferSize);
return minBufferSize;
};
const auto receiveBufferSize = options.getReceiveBufferSize().value_or (getCurrentBufferSizeWithMinimum (SO_RCVBUF));
const auto sendBufferSize = options.getSendBufferSize() .value_or (getCurrentBufferSizeWithMinimum (SO_SNDBUF));
return handle != invalidSocket
&& setOption (handle, SO_RCVBUF, (int) 65536)
&& setOption (handle, SO_SNDBUF, (int) 65536)
&& setOption (handle, SO_RCVBUF, receiveBufferSize)
&& setOption (handle, SO_SNDBUF, sendBufferSize)
&& (isDatagram ? ((! allowBroadcast) || setOption (handle, SO_BROADCAST, (int) 1))
: setOption (handle, IPPROTO_TCP, TCP_NODELAY, (int) 1));
}
@ -370,7 +397,8 @@ namespace SocketHelpers
CriticalSection& readLock,
const String& hostName,
int portNumber,
int timeOutMillisecs) noexcept
int timeOutMillisecs,
const SocketOptions& options) noexcept
{
bool success = false;
@ -421,7 +449,7 @@ namespace SocketHelpers
{
auto h = (SocketHandle) handle.load();
setSocketBlockingState (h, true);
resetSocketOptions (h, false, false);
resetSocketOptions (h, false, false, options);
}
}
@ -458,8 +486,9 @@ StreamingSocket::StreamingSocket()
SocketHelpers::initSockets();
}
StreamingSocket::StreamingSocket (const String& host, int portNum, int h)
: hostName (host),
StreamingSocket::StreamingSocket (const String& host, int portNum, int h, const SocketOptions& optionsIn)
: options (optionsIn),
hostName (host),
portNumber (portNum),
handle (h),
connected (true)
@ -467,7 +496,7 @@ StreamingSocket::StreamingSocket (const String& host, int portNum, int h)
jassert (SocketHelpers::isValidPortNumber (portNum));
SocketHelpers::initSockets();
SocketHelpers::resetSocketOptions ((SocketHandle) h, false, false);
SocketHelpers::resetSocketOptions ((SocketHandle) h, false, false, options);
}
StreamingSocket::~StreamingSocket()
@ -535,12 +564,12 @@ bool StreamingSocket::connect (const String& remoteHostName, int remotePortNumbe
isListener = false;
connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName,
remotePortNumber, timeOutMillisecs);
remotePortNumber, timeOutMillisecs, options);
if (! connected)
return false;
if (! SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(), false, false))
if (! SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(), false, false, options))
{
close();
return false;
@ -606,7 +635,7 @@ StreamingSocket* StreamingSocket::waitForNextConnection() const
if (newSocket >= 0 && connected)
return new StreamingSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr),
portNumber, newSocket);
portNumber, newSocket, options);
}
return nullptr;
@ -629,7 +658,8 @@ bool StreamingSocket::isLocal() const noexcept
//==============================================================================
//==============================================================================
DatagramSocket::DatagramSocket (bool canBroadcast)
DatagramSocket::DatagramSocket (bool canBroadcast, const SocketOptions& optionsIn)
: options { optionsIn }
{
SocketHelpers::initSockets();
@ -637,7 +667,7 @@ DatagramSocket::DatagramSocket (bool canBroadcast)
if (handle >= 0)
{
SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(), true, canBroadcast);
SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(), true, canBroadcast, options);
SocketHelpers::makeReusable (handle);
}
}

View file

@ -23,6 +23,50 @@
namespace juce
{
/**
Options used for the configuration of the underlying system socket in the
StreamingSocket and DatagramSocket classes.
@see StreamingSocket, DatagramSocket
@tags{Core}
*/
class JUCE_API SocketOptions
{
public:
/** The provided size will be used to configure the socket's SO_RCVBUF property. Increasing the
buffer size can reduce the number of lost packets with the DatagramSocket class, if the
socket is to receive packets in large bursts.
If this property is not specified, the system default value will be used, but a minimum of
65536 will be ensured.
*/
[[nodiscard]] SocketOptions withReceiveBufferSize (int size) const
{
return withMember (*this, &SocketOptions::receiveBufferSize, size);
}
/** The provided size will be used to configure the socket's SO_SNDBUF property.
If this property is not specified, the system default value will be used, but a minimum of
65536 will be ensured.
*/
[[nodiscard]] SocketOptions withSendBufferSize (int size) const
{
return withMember (*this, &SocketOptions::sendBufferSize, size);
}
/** @see withReceiveBufferSize() */
[[nodiscard]] auto getReceiveBufferSize() const { return receiveBufferSize; }
/** @see withSendBufferSize() */
[[nodiscard]] auto getSendBufferSize() const { return sendBufferSize; }
private:
std::optional<int> receiveBufferSize;
std::optional<int> sendBufferSize;
};
//==============================================================================
/**
A wrapper for a streaming (TCP) socket.
@ -37,6 +81,8 @@ namespace juce
class JUCE_API StreamingSocket final
{
public:
using Options = SocketOptions;
//==============================================================================
/** Creates an uninitialised socket.
@ -49,6 +95,20 @@ public:
*/
StreamingSocket();
/** Creates an uninitialised socket and allows specifying options related to the
configuration of the underlying socket.
To connect it, use the connect() method, after which you can read() or write()
to it.
To wait for other sockets to connect to this one, the createListener() method
enters "listener" mode, and can be used to spawn new sockets for each connection
that comes along.
*/
explicit StreamingSocket (const SocketOptions& optionsIn)
: options { optionsIn }
{}
/** Destructor. */
~StreamingSocket();
@ -178,12 +238,13 @@ public:
private:
//==============================================================================
SocketOptions options;
String hostName;
std::atomic<int> portNumber { 0 }, handle { -1 };
std::atomic<bool> connected { false }, isListener { false };
mutable CriticalSection readLock;
StreamingSocket (const String& hostname, int portNumber, int handle);
StreamingSocket (const String& hostname, int portNumber, int handle, const SocketOptions& options);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StreamingSocket)
};
@ -203,7 +264,20 @@ private:
class JUCE_API DatagramSocket final
{
public:
using Options = SocketOptions;
//==============================================================================
/** Creates a datagram socket and allows specifying options related to the
configuration of the underlying socket.
You first need to bind this socket to a port with bindToPort if you intend to read
from this socket.
If enableBroadcasting is true, the socket will be allowed to send broadcast messages
(may require extra privileges on linux)
*/
DatagramSocket (bool enableBroadcasting, const SocketOptions& optionsIn);
/** Creates a datagram socket.
You first need to bind this socket to a port with bindToPort if you intend to read
@ -212,8 +286,19 @@ public:
If enableBroadcasting is true, the socket will be allowed to send broadcast messages
(may require extra privileges on linux)
*/
DatagramSocket (bool enableBroadcasting = false);
explicit DatagramSocket (bool enableBroadcasting)
: DatagramSocket (enableBroadcasting, SocketOptions{})
{}
/** Creates a datagram socket.
You first need to bind this socket to a port with bindToPort if you intend to read
from this socket.
This constructor creates a socket that does not allow sending broadcast messages.
*/
DatagramSocket() : DatagramSocket (false)
{}
/** Destructor. */
~DatagramSocket();
@ -354,6 +439,7 @@ public:
private:
//==============================================================================
SocketOptions options;
std::atomic<int> handle { -1 };
bool isBound = false;
String lastBindAddress, lastServerHost;