172 lines
5.9 KiB
C#
172 lines
5.9 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Security;
|
|
using System.Net.Sockets;
|
|
using System.Security.Authentication;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace EonaCat.Network
|
|
{
|
|
public class SocketTcpClient
|
|
{
|
|
private const int BUFFER_SIZE = 4096;
|
|
|
|
private Socket _socket;
|
|
private bool _isIPv6;
|
|
|
|
public bool IsIPv6 => _isIPv6;
|
|
|
|
public event Action<RemoteInfo> OnConnect;
|
|
public event Action<RemoteInfo> OnReceive;
|
|
public event Action<RemoteInfo> OnDisconnect;
|
|
public event Action<Exception, string> OnError;
|
|
|
|
public async Task ConnectAsync(string ipAddress, int port, bool useSsl = false, SslOptions sslOptions = null)
|
|
{
|
|
if (!IPAddress.TryParse(ipAddress, out var ip))
|
|
{
|
|
throw new ArgumentException("Invalid ipAddress given");
|
|
}
|
|
|
|
await CreateSocketTcpClientAsync(ip, port, useSsl, sslOptions);
|
|
}
|
|
|
|
private async Task CreateSocketTcpClientAsync(IPAddress ipAddress, int port, bool useSsl, SslOptions sslOptions)
|
|
{
|
|
_isIPv6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
|
|
_socket = new Socket(_isIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
try
|
|
{
|
|
await _socket.ConnectAsync(ipAddress, port).ConfigureAwait(false);
|
|
OnConnect?.Invoke(new RemoteInfo { IsTcp = true, IsIPv6 = _isIPv6 });
|
|
|
|
if (useSsl)
|
|
{
|
|
var sslStream = new SslStream(new NetworkStream(_socket), false, ValidateRemoteCertificate, null);
|
|
try
|
|
{
|
|
await sslStream.AuthenticateAsClientAsync(ipAddress.ToString(), sslOptions.ClientCertificates, sslOptions.SslProtocol, sslOptions.CheckCertificateRevocation);
|
|
_ = StartReceivingSslAsync(sslStream);
|
|
}
|
|
catch (AuthenticationException ex)
|
|
{
|
|
OnError?.Invoke(ex, $"AuthenticationException: {ex.Message}");
|
|
Disconnect();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_ = StartReceivingAsync();
|
|
}
|
|
}
|
|
catch (SocketException ex)
|
|
{
|
|
OnError?.Invoke(ex, $"SocketException: {ex.Message}");
|
|
Disconnect();
|
|
}
|
|
}
|
|
|
|
private async Task StartReceivingAsync()
|
|
{
|
|
var buffer = new byte[BUFFER_SIZE];
|
|
while (_socket.Connected)
|
|
{
|
|
try
|
|
{
|
|
var received = await _socket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None).ConfigureAwait(false);
|
|
if (received > 0)
|
|
{
|
|
var data = new byte[received];
|
|
Buffer.BlockCopy(buffer, 0, data, 0, received);
|
|
OnReceive?.Invoke(new RemoteInfo
|
|
{
|
|
IsTcp = true,
|
|
Data = data,
|
|
EndPoint = _socket.RemoteEndPoint,
|
|
IsIPv6 = _isIPv6
|
|
});
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
catch (SocketException ex)
|
|
{
|
|
OnError?.Invoke(ex, $"SocketException: {ex.Message}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
OnDisconnect?.Invoke(new RemoteInfo
|
|
{
|
|
IsTcp = true,
|
|
EndPoint = _socket.RemoteEndPoint,
|
|
IsIPv6 = _isIPv6
|
|
});
|
|
}
|
|
|
|
private async Task StartReceivingSslAsync(SslStream sslStream)
|
|
{
|
|
var buffer = new byte[BUFFER_SIZE];
|
|
while (_socket.Connected)
|
|
{
|
|
try
|
|
{
|
|
var received = await sslStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
|
|
if (received > 0)
|
|
{
|
|
var data = new byte[received];
|
|
Buffer.BlockCopy(buffer, 0, data, 0, received);
|
|
OnReceive?.Invoke(new RemoteInfo
|
|
{
|
|
IsTcp = true,
|
|
Data = data,
|
|
EndPoint = _socket.RemoteEndPoint,
|
|
IsIPv6 = _isIPv6
|
|
});
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
catch (IOException ex)
|
|
{
|
|
OnError?.Invoke(ex, $"IOException: {ex.Message}");
|
|
break;
|
|
}
|
|
catch (AuthenticationException ex)
|
|
{
|
|
OnError?.Invoke(ex, $"AuthenticationException: {ex.Message}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
OnDisconnect?.Invoke(new RemoteInfo
|
|
{
|
|
IsTcp = true,
|
|
EndPoint = _socket.RemoteEndPoint,
|
|
IsIPv6 = _isIPv6
|
|
});
|
|
}
|
|
|
|
public async Task SendAsync(byte[] data)
|
|
{
|
|
await _socket.SendAsync(new ArraySegment<byte>(data), SocketFlags.None).ConfigureAwait(false);
|
|
}
|
|
|
|
public void Disconnect()
|
|
{
|
|
_socket.Close();
|
|
}
|
|
|
|
private bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
|
{
|
|
return sslPolicyErrors == SslPolicyErrors.None;
|
|
}
|
|
}
|
|
}
|