114 lines
2.5 KiB
C#
114 lines
2.5 KiB
C#
|
using System.Net.Sockets;
|
||
|
|
||
|
namespace InnovEnergy.Lib.Channels.V2;
|
||
|
|
||
|
public class TcpChannel : IChannel<IReadOnlyList<Byte>>
|
||
|
{
|
||
|
private Int32 TimeoutMs { get; }
|
||
|
private String Hostname { get; }
|
||
|
private UInt16 Port { get; }
|
||
|
private Byte[] Buffer { get; }
|
||
|
|
||
|
private Socket? Socket { get; set; }
|
||
|
|
||
|
public TcpChannel(String hostname,
|
||
|
UInt16 port,
|
||
|
TimeSpan timeout = default,
|
||
|
Int32 bufferSize = 8192)
|
||
|
{
|
||
|
if (bufferSize <= 0)
|
||
|
throw new ArgumentOutOfRangeException(nameof(bufferSize));
|
||
|
|
||
|
Hostname = hostname;
|
||
|
Port = port;
|
||
|
TimeoutMs = (Int32)timeout.TotalMilliseconds;
|
||
|
Buffer = new Byte[bufferSize];
|
||
|
}
|
||
|
|
||
|
|
||
|
public async Task<IReadOnlyList<Byte>> Read()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
await Open();
|
||
|
|
||
|
var nRead = await Socket!.ReceiveAsync(Buffer, SocketFlags.None);
|
||
|
|
||
|
if (nRead <= 0)
|
||
|
throw new Exception("TCP Connection was closed");
|
||
|
|
||
|
// copy from buffer, so we can reuse it
|
||
|
var data = new Byte[nRead];
|
||
|
Array.Copy(Buffer, data, nRead);
|
||
|
return data;
|
||
|
}
|
||
|
catch
|
||
|
{
|
||
|
Close();
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private async Task Open()
|
||
|
{
|
||
|
if (Socket is not null)
|
||
|
return;
|
||
|
|
||
|
Socket = new Socket(SocketType.Stream, ProtocolType.Tcp)
|
||
|
{
|
||
|
Blocking = true,
|
||
|
NoDelay = true,
|
||
|
LingerState = new LingerOption(false, 0),
|
||
|
ReceiveBufferSize = Buffer.Length,
|
||
|
SendBufferSize = Buffer.Length,
|
||
|
ReceiveTimeout = TimeoutMs,
|
||
|
SendTimeout = TimeoutMs,
|
||
|
};
|
||
|
|
||
|
var cts = new CancellationTokenSource();
|
||
|
cts.CancelAfter(TimeoutMs);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
await Socket.ConnectAsync(Hostname, Port, cts.Token).AsTask();
|
||
|
}
|
||
|
catch
|
||
|
{
|
||
|
Socket = null!;
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public async Task Write(IReadOnlyList<Byte> tx)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
await Open();
|
||
|
await Socket!.SendAsync(tx as Byte[] ?? tx.ToArray(), SocketFlags.None);
|
||
|
}
|
||
|
catch
|
||
|
{
|
||
|
Close();
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void Close()
|
||
|
{
|
||
|
if (Socket is null)
|
||
|
return;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
Socket.Dispose();
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
Socket = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|