Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ Dict | dictionary | 99

## Interacting with kdb+ via an open `c` instance

### Synchronous I/O

Interacting with the kdb+ server is very simple.
You must make a basic choice between sending a message to the server where you expect no answer,
or will check later for an answer.
Expand Down Expand Up @@ -196,6 +198,29 @@ As a special case of the `k` method, we may receive a message from the server wi
public object k()
```

#### Synchronous I/O send and receive timeouts

The time allowed for synchronous socket I/O can be configured in milliseconds using `SendTimeout` and `ReceiveTimeout`:

```c#
using (var connection = new c("localhost", 5000))
{
connection.SendTimeout = 5000;
connection.ReceiveTimeout = 10000;

object result = connection.k("select from trade");
}
```

A value of zero, which is the default, means that no timeout is applied.
These properties configure the underlying socket and apply to synchronous reads and writes performed by methods such as `k`, `ks`, `kn`, and `kr`.
They can be used with TCP and Unix-domain socket connections.

If a synchronous read or write times out, the stream reports an `IOException`.
The connection is closed before the exception is rethrown because the q IPC message may have been only partially sent or received and the connection can no longer be reused safely.

The properties are set after construction, so they do not apply to establishing the connection, TLS authentication, or the initial q IPC authentication handshake.

### Asynchronous I/O

The `c` class also provides methods that return `Task` or `Task<object>`. These are an alternative to the `ks` and `k` methods.
Expand Down
14 changes: 14 additions & 0 deletions kx.Test/Connection/ConnectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ public void ConnectionInitialises()
}
}

[Test]
public void ConnectionExposesSynchronousSocketTimeouts()
{
using (var server = new TestableTcpServer())
using (var connection = new c("localhost", server.TestPort))
{
connection.SendTimeout = 1000;
connection.ReceiveTimeout = 2000;

Assert.AreEqual(1000, connection.SendTimeout);
Assert.AreEqual(2000, connection.ReceiveTimeout);
}
}

[Test]
public void ConnectionThrowsIfHostIsNull()
{
Expand Down
69 changes: 54 additions & 15 deletions kx/c.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
/// This class is essentially a serializer/deserializer of .NET types
/// to/from the KDB+ IPC wire format, enabling remote method invocation in KDB+ via TCP/IP.
/// </remarks>
public class c : IDisposable

Check warning on line 21 in kx/c.cs

View workflow job for this annotation

GitHub Actions / build

The type name 'c' only contains lower-cased ascii characters. Such names may become reserved for the language.

Check warning on line 21 in kx/c.cs

View workflow job for this annotation

GitHub Actions / build

The type name 'c' only contains lower-cased ascii characters. Such names may become reserved for the language.
{
private readonly Socket _socket;

Expand Down Expand Up @@ -495,15 +495,38 @@
public static Encoding e { get; set; } = Encoding.ASCII;

/// <summary>
/// Requests that the underlying stream and TCP connection be closed.
/// Gets or sets the amount of time, in milliseconds, that a synchronous send operation
/// blocks waiting for completion. A value of zero means no timeout.
/// </summary>
public int SendTimeout
{
get { return _socket.SendTimeout; }
set { _socket.SendTimeout = value; }
}

/// <summary>
/// Gets or sets the amount of time, in milliseconds, that a synchronous receive operation
/// blocks waiting for data. A value of zero means no timeout.
/// </summary>
public int ReceiveTimeout
{
get { return _socket.ReceiveTimeout; }
set { _socket.ReceiveTimeout = value; }
}

/// <summary>
/// Requests that the underlying stream and connection be closed.
/// </summary>
public void Close()
{
if (_clientStream != null)
{
_clientStream.Close();
}
_socket.Close();
if (_socket != null)
{
_socket.Close();
}
}

/// <summary>
Expand Down Expand Up @@ -1035,7 +1058,15 @@
/// <param name="number">The number of bytes to be written to the client stream.</param>
protected void Write(byte[] bytes, int number)
{
_clientStream.Write(bytes, 0, number);
try
{
_clientStream.Write(bytes, 0, number);
}
catch (IOException)
{
Close();
throw;
}
}

/// <summary>
Expand Down Expand Up @@ -1709,7 +1740,7 @@
private void w(int i, object x)
{
byte[] buffer = Serialize(i, x);
_clientStream.Write(buffer, 0, buffer.Length);
Write(buffer, buffer.Length);
}

private bool rb()
Expand Down Expand Up @@ -2088,23 +2119,31 @@

private void read(byte[] b)
{
int k = 0;
int j = b.Length;
while (true)
try
{
if (k < j)
int k = 0;
int j = b.Length;
while (true)
{
int i;
if ((i = _clientStream.Read(b, k, Math.Min(_maxBufferSize, j - k))) == 0)
if (k < j)
{
break;
int i;
if ((i = _clientStream.Read(b, k, Math.Min(_maxBufferSize, j - k))) == 0)
{
break;
}
k += i;
continue;
}
k += i;
continue;
return;
}
return;
throw new KException("read");
}
catch (IOException)
{
Close();
throw;
}
throw new KException("read");
}

private async Task ReadAsync(byte[] b)
Expand Down
Loading