using System;
using System.Threading.Tasks;
using Vinno.IUS.Common.IO;
using Vinno.IUS.Common.Network.Tcp;
using Vinno.IUS.Common.Network.Tcp.Extensions;
using Vinno.IUS.Common.Network.IO;
namespace Vinno.IUS.Common.Network.Channels
{
public class ClientChannel:Channel
{
private readonly ITcp _tcp;
private int _readTimeoutTranscationLevel;
private int _writeTimeoutTranscationLevel;
///
/// Gets the total data transfered include send and receive.
///
public long DataTransfered { get; private set; }
public ClientChannel(int channelId, ITcp tcp):base(channelId)
{
_tcp = tcp;
Remote = _tcp.Remote;
Local = _tcp.Local;
}
///
/// Read and validate the header from remote.
///
/// Wait forever if the value is 0
protected override void ReadHeader(int timeout = 0)
{
//Notice that when network is shutdown, it may not know it's shutdown,
//But when read data from socket, it will read 0 byte, that's can used for
//Check if network is shutdown.
using (new ReadTimeoutTranscation(this, timeout))
{
//ReadBinary may throw exception when network is shutdown, and readed data length is zero.
var headerData = _tcp.ReadBinary(8);
ValidateHeader(headerData);
DataTransfered += 8;
}
}
///
/// Read and validate the header from remote by async way.
///
/// Wait forever if the value is 0
protected override async Task ReadHeaderAsync(int timeout = 0)
{
//Notice that when network is shutdown, it may not know it's shutdown,
//But when read data from socket, it will read 0 byte, that's can used for
//Check if network is shutdown.
using (new ReadTimeoutTranscation(this, timeout))
{
//ReadBinary may throw exception when network is shutdown, and read data length is zero.
var headerData = await _tcp.ReadBinaryAsync(8).ConfigureAwait(false);
ValidateHeader(headerData);
DataTransfered += 8;
}
}
///
/// Write the header before write buffer.
///
/// Wait forever if the value is 0
protected override void WriteHeader(int timeout = 0)
{
using (new WriteTimeoutTranscation(this, timeout))
{
var headerData = GetHeaderData();
//Write header
_tcp.WriteBinary(headerData);
DataTransfered += 8;
}
}
///
/// Write the header before write buffer by async way.
///
/// Wait forever if the value is 0
protected override async Task WriteHeaderAsync(int timeout = 0)
{
using (new WriteTimeoutTranscation(this, timeout))
{
var headerData = GetHeaderData();
//Write header
await _tcp.WriteBinaryAsync(headerData).ConfigureAwait(false);
DataTransfered += 8;
}
}
///
/// Read buffer from remote side.
///
/// Wait forever if the value is 0
/// The buffer readed from network.
protected IBuffer ReadBuffer(int timeout = 0)
{
using (new ReadTimeoutTranscation(this, timeout))
{
var reader = new BufferReader(_tcp.ReadStream);
var buffer = reader.ReadBuffer();
DataTransfered += buffer.Size;
return buffer;
}
}
///
/// Read buffer from remote side by async way.
///
/// Wait forever if the value is 0
/// The buffer readed from network.
protected async Task ReadBufferAsync(int timeout = 0)
{
using (new ReadTimeoutTranscation(this, timeout))
{
var reader = new AsyncBufferReader(_tcp.ReadStream);
var buffer = await reader.ReadBufferAsync().ConfigureAwait(false);
DataTransfered += buffer.Size;
return buffer;
}
}
///
/// Write buffer to remote side.
///
/// The buffer to be writen
/// Wait forever if the value is 0
protected void WriteBuffer(IBuffer buffer, int timeout = 0)
{
using (new WriteTimeoutTranscation(this, timeout))
{
var writer = new BufferWriter(_tcp.WriteStream);
writer.WriteBuffer(buffer);
DataTransfered += buffer.Size;
}
}
///
/// Write buffer to remote side by async way.
///
/// The buffer to be writen
/// Wait forever if the value is 0
protected async Task WriteBufferAsync(IBuffer buffer, int timeout = 0)
{
using (new WriteTimeoutTranscation(this, timeout))
{
var writer = new AsyncBufferWriter(_tcp.WriteStream);
await writer.WriteBufferAsync(buffer).ConfigureAwait(false);
DataTransfered += buffer.Size;
}
}
protected override void OnClosed()
{
_tcp.Disconnect();
base.OnClosed();
}
public override string ToString()
{
return $"{Remote}<->{Local}";
}
private class ReadTimeoutTranscation : IDisposable
{
private readonly ClientChannel _channel;
public ReadTimeoutTranscation(ClientChannel channel, int timeout)
{
_channel = channel;
if (_channel._readTimeoutTranscationLevel == 0)
{
try
{
_channel._tcp.ReadTimeout = timeout;
}
catch
{
//DO Nothing.
}
}
_channel._readTimeoutTranscationLevel++;
}
public void Dispose()
{
_channel._readTimeoutTranscationLevel--;
if (_channel._readTimeoutTranscationLevel == 0)
{
try
{
_channel._tcp.ReadTimeout = 0;
}
catch
{
//DO Nothing.
}
}
}
}
private class WriteTimeoutTranscation : IDisposable
{
private readonly ClientChannel _channel;
public WriteTimeoutTranscation(ClientChannel channel, int timeout)
{
_channel = channel;
if (_channel._writeTimeoutTranscationLevel == 0)
{
try
{
_channel._tcp.SendTimeout = timeout;
}
catch
{
//DO Nothing.
}
}
_channel._writeTimeoutTranscationLevel++;
}
public void Dispose()
{
_channel._writeTimeoutTranscationLevel--;
if (_channel._writeTimeoutTranscationLevel == 0)
{
try
{
_channel._tcp.SendTimeout = 0;
}
catch
{
//DO Nothing.
}
}
}
}
}
}