//------------------------------------------------------------------------------
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
// CSDN博客:https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频:https://space.bilibili.com/94253567
// Gitee源代码仓库:https://gitee.com/RRQM_Home
// Github源代码仓库:https://github.com/RRQM
// API首页:https://www.yuque.com/rrqm/touchsocket/index
// 交流QQ群:234762506
// 感谢您的下载和使用
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Threading.Tasks;
using TouchSocket.Core;
using TouchSocket.Resources;
namespace TouchSocket.Sockets
{
///
/// 服务器辅助类
///
[DebuggerDisplay("ID={ID},IPAdress={IP}:{Port}")]
public class SocketClient : BaseSocket, ISocketClient
{
///
/// 构造函数
///
public SocketClient()
{
Protocol = Protocol.TCP;
}
#region 变量
internal string m_id;
internal ReceiveType m_receiveType;
internal TcpServiceBase m_service;
internal bool m_usePlugin;
private DataHandlingAdapter m_adapter;
private DelaySender m_delaySender;
private Socket m_mainSocket;
//private int m_maxPackageSize;
private bool m_online;
private bool m_useDelaySender;
private Stream m_workStream;
#endregion 变量
#region 属性
///
public bool IsClient => false;
///
///
///
public bool CanSend => m_online;
///
///
///
public virtual bool CanSetDataHandlingAdapter => true;
///
///
///
public TouchSocketConfig Config { get; internal set; }
///
///
///
public IContainer Container => Config?.Container;
///
///
///
public DataHandlingAdapter DataHandlingAdapter => m_adapter;
///
/// 用于索引的ID
///
public string ID => m_id;
///
///
///
public string IP { get; private set; }
///
///
///
public Socket MainSocket => m_mainSocket;
///
///
///
public bool Online => m_online;
///
///
///
public IPluginsManager PluginsManager => Config?.PluginsManager;
///
///
///
public int Port { get; private set; }
///
///
///
public Protocol Protocol { get; set; }
///
///
///
public ReceiveType ReceiveType => m_receiveType;
///
///
///
public TcpServiceBase Service => m_service;
///
///
///
public bool UsePlugin => m_usePlugin;
///
///
///
public bool UseSsl { get; private set; }
#endregion 属性
#region 事件&委托
///
///
///
public DisconnectEventHandler Disconnected { get; set; }
///
///
///
public DisconnectEventHandler Disconnecting { get; set; }
///
/// 即将断开连接(仅主动断开时有效)。
///
/// 当主动调用Close断开时,可通过终止断开行为。
///
///
///
protected virtual void OnDisconnecting(DisconnectEventArgs e)
{
try
{
Disconnecting?.Invoke(this, e);
}
catch (Exception ex)
{
Logger.Log(LogType.Error, this, $"在事件{nameof(Disconnecting)}中发生错误。", ex);
}
}
///
/// 当客户端完整建立TCP连接,如果覆盖父类方法,则不会触发插件。
///
///
protected virtual void OnConnected(TouchSocketEventArgs e)
{
m_service.OnInternalConnected(this, e);
}
///
/// 客户端正在连接,如果覆盖父类方法,则不会触发插件。
///
protected virtual void OnConnecting(OperationEventArgs e)
{
m_service.OnInternalConnecting(this, e);
}
///
/// 在延迟发生错误
///
///
protected virtual void OnDelaySenderError(Exception ex)
{
Logger.Log(LogType.Error, this, "发送错误", ex);
}
///
/// 客户端已断开连接,如果从Connecting中拒绝连接,则不会触发。如果覆盖父类方法,则不会触发插件。
///
///
protected virtual void OnDisconnected(DisconnectEventArgs e)
{
Disconnected?.Invoke(this, e);
}
///
/// 当初始化完成时,执行在之前。
///
protected virtual void OnInitialized()
{
}
private void PrivateOnDisconnected(DisconnectEventArgs e)
{
if (m_usePlugin && PluginsManager.Raise(nameof(IDisconnectedPlguin.OnDisconnected), this, e))
{
return;
}
OnDisconnected(e);
if (!e.Handled)
{
m_service.OnInternalDisconnected(this, e);
}
}
private void PrivateOnDisconnecting(DisconnectEventArgs e)
{
if (m_usePlugin && PluginsManager.Raise(nameof(IDisconnectingPlugin.OnDisconnecting), this, e))
{
return;
}
OnDisconnecting(e);
if (!e.Handled)
{
m_service.OnInternalDisconnecting(this, e);
}
}
#endregion 事件&委托
///
///
///
public DateTime LastReceivedTime { get; private set; }
///
///
///
public DateTime LastSendTime { get; private set; }
///
///
///
public Func OnHandleRawBuffer { get; set; }
///
///
///
public Func OnHandleReceivedData { get; set; }
///
///
///
public string ServiceIP { get; private set; }
///
///
///
public int ServicePort { get; private set; }
///
public virtual void Close()
{
Close($"主动调用{nameof(Close)}");
}
///
public virtual void Close(string msg)
{
if (this.m_online)
{
var args = new DisconnectEventArgs(true, msg)
{
IsPermitOperation = true
};
PrivateOnDisconnecting(args);
if (this.DisposedValue || args.IsPermitOperation)
{
BreakOut(msg, true);
}
}
}
///
///
///
///
public Stream GetStream()
{
if (m_workStream == null)
{
m_workStream = new NetworkStream(m_mainSocket, true);
}
return m_workStream;
}
///
/// 直接重置内部ID。
///
///
protected void DirectResetID(string newId)
{
if (string.IsNullOrEmpty(newId))
{
throw new ArgumentException($"“{nameof(newId)}”不能为 null 或空。", nameof(newId));
}
if (m_id == newId)
{
return;
}
string oldId = m_id;
if (Service.SocketClients.TryRemove(m_id, out SocketClient socketClient))
{
socketClient.m_id = newId;
if (Service.SocketClients.TryAdd(socketClient))
{
if (m_usePlugin)
{
IDChangedEventArgs e = new IDChangedEventArgs(oldId, newId);
PluginsManager.Raise(nameof(ITcpPlugin.OnIDChanged), socketClient, e);
}
return;
}
else
{
socketClient.m_id = oldId;
if (Service.SocketClients.TryAdd(socketClient))
{
throw new Exception("ID重复");
}
else
{
socketClient.Close("修改新ID时操作失败,且回退旧ID时也失败。");
}
}
}
else
{
throw new ClientNotFindException(TouchSocketStatus.ClientNotFind.GetDescription(oldId));
}
}
///
///
///
///
///
///
///
public virtual void ResetID(string newId)
{
DirectResetID(newId);
}
///
///
///
///
public virtual void SetDataHandlingAdapter(DataHandlingAdapter adapter)
{
if (!CanSetDataHandlingAdapter)
{
throw new Exception($"不允许自由调用{nameof(SetDataHandlingAdapter)}进行赋值。");
}
SetAdapter(adapter);
}
internal void BeginReceive(ReceiveType receiveType)
{
try
{
if (receiveType == ReceiveType.Auto)
{
SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs();
eventArgs.Completed += EventArgs_Completed;
ByteBlock byteBlock = BytePool.Default.GetByteBlock(BufferLength);
eventArgs.UserToken = byteBlock;
eventArgs.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity);
if (!m_mainSocket.ReceiveAsync(eventArgs))
{
ProcessReceived(eventArgs);
}
}
}
catch (Exception ex)
{
BreakOut(ex.Message, false);
}
}
internal void BeginReceiveSsl(ReceiveType receiveType, ServiceSslOption sslOption)
{
SslStream sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(m_mainSocket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(m_mainSocket, false), false);
sslStream.AuthenticateAsServer(sslOption.Certificate, sslOption.ClientCertificateRequired, sslOption.SslProtocols, sslOption.CheckCertificateRevocation);
m_workStream = sslStream;
UseSsl = true;
if (receiveType == ReceiveType.Auto)
{
BeginSsl();
}
}
internal void InternalConnected(TouchSocketEventArgs e)
{
m_online = true;
if (Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty) is DelaySenderOption senderOption)
{
m_useDelaySender = true;
m_delaySender.SafeDispose();
m_delaySender = new DelaySender(m_mainSocket, senderOption.QueueLength, OnDelaySenderError)
{
DelayLength = senderOption.DelayLength
};
}
if (m_usePlugin && PluginsManager.Raise(nameof(IConnectedPlugin.OnConnected), this, e))
{
return;
}
OnConnected(e);
}
internal void InternalConnecting(OperationEventArgs e)
{
if (m_usePlugin && PluginsManager.Raise(nameof(IConnectingPlugin.OnConnecting), this, e))
{
return;
}
OnConnecting(e);
}
internal void InternalInitialized()
{
LastReceivedTime = DateTime.Now;
LastSendTime = DateTime.Now;
OnInitialized();
}
internal void SetSocket(Socket mainSocket)
{
m_mainSocket = mainSocket ?? throw new ArgumentNullException(nameof(mainSocket));
IP = mainSocket.RemoteEndPoint.GetIP();
Port = mainSocket.RemoteEndPoint.GetPort();
ServiceIP = mainSocket.LocalEndPoint.GetIP();
ServicePort = mainSocket.LocalEndPoint.GetPort();
}
///
///
///
///
protected override void Dispose(bool disposing)
{
if (this.m_online)
{
var args = new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开");
PrivateOnDisconnecting(args);
}
m_adapter.SafeDispose();
m_adapter = default;
BreakOut($"{nameof(Dispose)}主动断开", true);
base.Dispose(disposing);
}
///
/// 处理已接收到的数据。
/// 根据不同的数据处理适配器,会传递不同的数据
///
/// 以二进制流形式传递
/// 以解析的数据对象传递
protected virtual void HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
{
}
///
/// 当即将发送时,如果覆盖父类方法,则不会触发插件。
///
/// 数据缓存区
/// 偏移
/// 长度
/// 返回值表示是否允许发送
protected virtual bool HandleSendingData(byte[] buffer, int offset, int length)
{
if (m_usePlugin)
{
SendingEventArgs args = new SendingEventArgs(buffer, offset, length);
PluginsManager.Raise(nameof(ITcpPlugin.OnSendingData), this, args);
if (args.IsPermitOperation)
{
return true;
}
return false;
}
return true;
}
///
/// 设置适配器,该方法不会检验的值。
///
///
protected void SetAdapter(DataHandlingAdapter adapter)
{
if (adapter is null)
{
throw new ArgumentNullException(nameof(adapter));
}
if (Config != null)
{
if (Config.GetValue(TouchSocketConfigExtension.MaxPackageSizeProperty) is int v1)
{
adapter.MaxPackageSize = v1;
}
if (Config.GetValue(TouchSocketConfigExtension.CacheTimeoutProperty) != TimeSpan.Zero)
{
adapter.CacheTimeout = Config.GetValue(TouchSocketConfigExtension.CacheTimeoutProperty);
}
if (Config.GetValue(TouchSocketConfigExtension.CacheTimeoutEnableProperty) is bool v2)
{
adapter.CacheTimeoutEnable = v2;
}
if (Config.GetValue(TouchSocketConfigExtension.UpdateCacheTimeWhenRevProperty) is bool v3)
{
adapter.UpdateCacheTimeWhenRev = v3;
}
}
adapter.OnLoaded(this);
adapter.ReceivedCallBack = PrivateHandleReceivedData;
adapter.SendCallBack = DefaultSend;
m_adapter = adapter;
}
private void BeginSsl()
{
if (!DisposedValue)
{
ByteBlock byteBlock = new ByteBlock(BufferLength);
try
{
m_workStream.BeginRead(byteBlock.Buffer, 0, byteBlock.Capacity, EndSsl, byteBlock);
}
catch (System.Exception ex)
{
byteBlock.Dispose();
BreakOut(ex.Message, false);
}
}
}
private void BreakOut(string msg, bool manual)
{
lock (this.SyncRoot)
{
if (m_online)
{
m_online = false;
this.TryShutdown();
m_mainSocket.SafeDispose();
m_delaySender.SafeDispose();
m_adapter.SafeDispose();
m_service?.SocketClients.TryRemove(m_id, out _);
PrivateOnDisconnected(new DisconnectEventArgs(manual, msg));
Disconnected = null;
}
base.Dispose(true);
}
}
private void EndSsl(IAsyncResult result)
{
ByteBlock byteBlock = (ByteBlock)result.AsyncState;
try
{
int r = m_workStream.EndRead(result);
if (r == 0)
{
BreakOut("远程终端主动关闭", false);
}
byteBlock.SetLength(r);
HandleBuffer(byteBlock);
BeginSsl();
}
catch (Exception ex)
{
byteBlock.Dispose();
BreakOut(ex.Message, false);
}
}
private void EventArgs_Completed(object sender, SocketAsyncEventArgs e)
{
try
{
ProcessReceived(e);
}
catch (Exception ex)
{
e.SafeDispose();
BreakOut(ex.Message, false);
}
}
private void HandleBuffer(ByteBlock byteBlock)
{
try
{
LastReceivedTime = DateTime.Now;
if (OnHandleRawBuffer?.Invoke(byteBlock) == false)
{
return;
}
if (UsePlugin && PluginsManager.Raise(nameof(ITcpPlugin.OnReceivingData), this, new ByteBlockEventArgs(byteBlock)))
{
return;
}
if (DisposedValue)
{
return;
}
if (m_adapter == null)
{
Logger.Error(this, TouchSocketStatus.NullDataAdapter.GetDescription());
return;
}
m_adapter.ReceivedInput(byteBlock);
}
catch (System.Exception ex)
{
Logger.Log(LogType.Error, this, "在处理数据时发生错误", ex);
}
finally
{
byteBlock.Dispose();
}
}
private void PrivateHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
{
if (OnHandleReceivedData?.Invoke(byteBlock, requestInfo) == false)
{
return;
}
if (m_usePlugin)
{
ReceivedDataEventArgs args = new ReceivedDataEventArgs(byteBlock, requestInfo);
PluginsManager.Raise(nameof(ITcpPlugin.OnReceivedData), this, args);
if (args.Handled)
{
return;
}
}
HandleReceivedData(byteBlock, requestInfo);
m_service.OnInternalReceivedData(this, byteBlock, requestInfo);
}
private void ProcessReceived(SocketAsyncEventArgs e)
{
if (DisposedValue)
{
e.SafeDispose();
}
else
{
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
{
ByteBlock byteBlock = (ByteBlock)e.UserToken;
byteBlock.SetLength(e.BytesTransferred);
HandleBuffer(byteBlock);
try
{
ByteBlock newByteBlock = new ByteBlock(BufferLength);
e.UserToken = newByteBlock;
e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Capacity);
if (!m_mainSocket.ReceiveAsync(e))
{
ProcessReceived(e);
}
}
catch (Exception ex)
{
BreakOut(ex.Message, false);
}
}
else
{
e.SafeDispose();
BreakOut("远程主机主动断开连接", false);
}
}
}
#region 发送
///
///
///
///
///
///
///
///
///
public void DefaultSend(byte[] buffer, int offset, int length)
{
if (!m_online)
{
throw new NotConnectedException(TouchSocketStatus.NotConnected.GetDescription());
}
if (HandleSendingData(buffer, offset, length))
{
if (UseSsl)
{
m_workStream.Write(buffer, offset, length);
}
else
{
if (m_useDelaySender && length < TouchSocketUtility.BigDataBoundary)
{
m_delaySender.Send(new QueueDataBytes(buffer, offset, length));
}
else
{
m_mainSocket.AbsoluteSend(buffer, offset, length);
}
}
LastSendTime = DateTime.Now;
}
}
///
///
///
///
///
///
///
///
///
public Task DefaultSendAsync(byte[] buffer, int offset, int length)
{
return EasyTask.Run(() =>
{
DefaultSend(buffer, offset, length);
});
}
#region 同步发送
///
///
///
///
///
///
///
public virtual void Send(IRequestInfo requestInfo)
{
if (DisposedValue)
{
return;
}
if (m_adapter == null)
{
throw new ArgumentNullException(nameof(DataHandlingAdapter), TouchSocketStatus.NullDataAdapter.GetDescription());
}
if (!m_adapter.CanSendRequestInfo)
{
throw new NotSupportedException($"当前适配器不支持对象发送。");
}
m_adapter.SendInput(requestInfo);
}
///
/// 发送字节流
///
///
///
///
///
///
///
public virtual void Send(byte[] buffer, int offset, int length)
{
if (DisposedValue)
{
return;
}
if (m_adapter == null)
{
throw new ArgumentNullException(nameof(DataHandlingAdapter), TouchSocketStatus.NullDataAdapter.GetDescription());
}
m_adapter.SendInput(buffer, offset, length);
}
///
///
///
///
public virtual void Send(IList> transferBytes)
{
if (DisposedValue)
{
return;
}
if (m_adapter == null)
{
throw new ArgumentNullException(nameof(DataHandlingAdapter), TouchSocketStatus.NullDataAdapter.GetDescription());
}
if (m_adapter.CanSplicingSend)
{
m_adapter.SendInput(transferBytes);
}
else
{
ByteBlock byteBlock = new ByteBlock(BufferLength);
try
{
foreach (var item in transferBytes)
{
byteBlock.Write(item.Array, item.Offset, item.Count);
}
m_adapter.SendInput(byteBlock.Buffer, 0, byteBlock.Len);
}
finally
{
byteBlock.Dispose();
}
}
}
#endregion 同步发送
#region 异步发送
///
/// IOCP发送
///
///
///
///
///
///
///
public virtual Task SendAsync(byte[] buffer, int offset, int length)
{
return EasyTask.Run(() =>
{
Send(buffer, offset, length);
});
}
///
///
///
///
///
///
///
public virtual Task SendAsync(IRequestInfo requestInfo)
{
return EasyTask.Run(() =>
{
Send(requestInfo);
});
}
///
///
///
///
public virtual Task SendAsync(IList> transferBytes)
{
return EasyTask.Run(() =>
{
Send(transferBytes);
});
}
#endregion 异步发送
#region ID发送
///
/// 发送字节流
///
/// 用于检索TcpSocketClient
///
///
///
///
///
///
///
public void Send(string id, byte[] buffer, int offset, int length)
{
m_service.Send(id, buffer, offset, length);
}
///
///
///
///
///
public void Send(string id, IRequestInfo requestInfo)
{
m_service.Send(id, requestInfo);
}
///
/// 发送字节流
///
/// 用于检索TcpSocketClient
///
///
///
///
///
///
///
public Task SendAsync(string id, byte[] buffer, int offset, int length)
{
return m_service.SendAsync(id, buffer, offset, length);
}
///
///
///
///
///
public Task SendAsync(string id, IRequestInfo requestInfo)
{
return m_service.SendAsync(id, requestInfo);
}
#endregion ID发送
#endregion 发送
}
}