TCP通讯接口:
接口:
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Sorter.Communication.Socket.Abstractions
{
public interface ITcpClient : IDisposable
{
event EventHandler<bool>? ConnectionChanged;
event EventHandler<string>? DataReceived;
bool IsConnected { get; }
IPEndPoint RemoteEndPoint { get; }
IPEndPoint IPEndPoint { get; }
Task<Result> ConnectAsync(string ip, int port, CancellationToken cancellationToken);
Task<Result> DisconnectAsync();
Task<Result<string>> ReceiveAsync();
Task<Result> SendAsync(string data);
}
}
实现:
cs
using Microsoft.Extensions.Logging;
using Sorter.Communication.Socket.Abstractions;
using SuperSocket.Client;
using SuperSocket.ProtoBase;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Sorter.Communication.Socket.SuperSocket
{
public class SuperTcpClient : ITcpClient
{
private readonly IEasyClient<string> _tcpClient;
//private readonly ILogger<SuperTcpClient> _logger;
private bool _disposed;
private bool _isDisconnectPassively;
public event EventHandler<bool>? ConnectionChanged;
public event EventHandler<string>? DataReceived;
public bool IsConnected { get; private set; }
public IPEndPoint RemoteEndPoint { get; private set; } = new IPEndPoint(IPAddress.None, 0);
public IPEndPoint IPEndPoint => (IPEndPoint)_tcpClient.LocalEndPoint;
public SuperTcpClient(IPipelineFilterFactory<string> pipelineFilterFactory/*, ILogger<SuperTcpClient> logger*/)
{
_tcpClient = new EasyClient<string>(pipelineFilterFactory.Create()/*, logger*/);
//_logger = logger;
}
private async void TcpClientClosed(object? sender, EventArgs e)
{
if (IsConnected)
{
_isDisconnectPassively = true;
}
IsConnected = false;
ConnectionChanged?.Invoke(this, IsConnected);
if (RemoteEndPoint is not null && !RemoteEndPoint.Address.Equals(IPAddress.None) && !RemoteEndPoint.Port.Equals(0))
{
await Task.Run(async () =>
{
while (_isDisconnectPassively && !IsConnected)
{
try
{
_tcpClient.PackageHandler -= TcpClientPackageHandler;
_tcpClient.Closed -= TcpClientClosed;
await _tcpClient.CloseAsync().ConfigureAwait(false);
var result = await this.ConnectAsync(RemoteEndPoint.Address.ToString(), RemoteEndPoint.Port, new CancellationTokenSource(2000).Token).ConfigureAwait(false);
_isDisconnectPassively = result?.Success is not true;
}
catch (OperationCanceledException)
{
}
catch (SocketException)
{
}
}
});
}
}
private ValueTask TcpClientPackageHandler(EasyClient<string> sender, string package)
{
DataReceived?.Invoke(this, package);
//_logger.LogInformation("[{IPEndPoint}] SuperTcpClient Received data: {package}", IPEndPoint, package);
return ValueTask.CompletedTask;
}
~SuperTcpClient()
{
Dispose(false);
}
protected virtual async void Dispose(bool disposing)
{
if (disposing)
{
// 释放托管资源
_tcpClient.PackageHandler -= TcpClientPackageHandler;
_tcpClient.Closed -= TcpClientClosed;
}
// 释放非托管资源
try
{
await _tcpClient.DisposeAsync();
_isDisconnectPassively = false;
IsConnected = false;
ConnectionChanged?.Invoke(this, IsConnected);
}
catch (NullReferenceException)
{
}
}
public void Dispose()
{
if (!_disposed)
{
Dispose(true);
}
_disposed = true;
GC.SuppressFinalize(this);
}
public async Task<Result> ConnectAsync(string ip, int port, CancellationToken cancellationToken = default)
{
try
{
if (!IPEndPoint.TryParse($"{ip}:{port}", out var result))
{
return Result<bool>.Fail("IP Format Error");
}
_isDisconnectPassively = false;
_tcpClient.PackageHandler += TcpClientPackageHandler;
_tcpClient.Closed += TcpClientClosed;
IsConnected = await _tcpClient.ConnectAsync(result, cancellationToken);
if (IsConnected)
{
RemoteEndPoint = result;
_tcpClient.StartReceive();
ConnectionChanged?.Invoke(this, IsConnected);
}
return IsConnected ? Result.Ok() : Result.Fail();
}
catch (Exception)
{
return Result.Fail();
}
finally
{
if (!IsConnected)
{
_tcpClient.PackageHandler -= TcpClientPackageHandler;
_tcpClient.Closed -= TcpClientClosed;
}
}
}
public async Task<Result> DisconnectAsync()
{
_tcpClient.PackageHandler -= TcpClientPackageHandler;
_tcpClient.Closed -= TcpClientClosed;
await _tcpClient.CloseAsync();
_isDisconnectPassively = false;
IsConnected = false;
ConnectionChanged?.Invoke(this, IsConnected);
return Result.Ok();
}
public async Task<Result<string>> ReceiveAsync()
{
if (!IsConnected)
{
return Result<string>.Fail(string.Empty);
}
return Result<string>.Ok(await _tcpClient.ReceiveAsync());
}
public async Task<Result> SendAsync(string data)
{
if (!IsConnected)
{
return Result.Fail();
}
await _tcpClient.SendAsync(Encoding.UTF8.GetBytes(data));
//_logger.LogInformation("[{IPEndPoint}] SuperTcpClient Send data: {data}", IPEndPoint, data);
return Result.Ok();
}
}
}
使用的时候:依赖注入使用
cs
private readonly ITcpClient _visionClient;
public RecipeService([FromKeyedServices("视觉")] ITcpClient tcpClient)
{
_visionClient = tcpClient;
//当 _visionClient 收到数据时,自动调用 _tcpClient_DataReceived 方法去处理它。
_visionClient.DataReceived += _tcpClient_DataReceived;
}
private void _tcpClient_DataReceived(object? sender, string e)
{
if (e.ToUpper().contains("RECIPE"))
{
_tcs.SetResult(e);
}
}