//------------------------------------------------------------------------------ // 此代码版权(除特别声明或在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.IO; using System.Net.Security; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Threading.Tasks; using TouchSocket.Core; using TouchSocket.Resources; namespace TouchSocket.Sockets { /// /// 简单TCP客户端 /// public class TcpClient : TcpClientBase { /// /// 接收到数据 /// public ReceivedEventHandler Received { get; set; } /// /// 接收数据 /// /// /// protected override void HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) { this.Received?.Invoke(this, byteBlock, requestInfo); base.HandleReceivedData(byteBlock, requestInfo); } } /// /// TCP客户端 /// [System.Diagnostics.DebuggerDisplay("{IP}:{Port}")] public class TcpClientBase : BaseSocket, ITcpClient { /// /// 构造函数 /// public TcpClientBase() { this.Protocol = Protocol.TCP; } #region 变量 private DelaySender m_delaySender; private bool m_useDelaySender; private Stream m_workStream; #endregion 变量 #region 事件 /// /// 成功连接到服务器 /// public MessageEventHandler Connected { get; set; } /// /// 准备连接的时候,此时已初始化Socket,但是并未建立Tcp连接 /// public ConnectingEventHandler Connecting { get; set; } /// /// 断开连接。在客户端未设置连接状态时,不会触发 /// public DisconnectEventHandler Disconnected { get; set; } /// /// /// public DisconnectEventHandler Disconnecting { get; set; } private void PrivateOnConnected(MsgEventArgs e) { if (this.UsePlugin) { this.PluginsManager.Raise(nameof(IConnectedPlugin.OnConnected), this, e); if (e.Handled) { return; } } this.OnConnected(e); } /// /// 已经建立Tcp连接 /// /// protected virtual void OnConnected(MsgEventArgs e) { try { this.Connected?.Invoke(this, e); } catch (System.Exception ex) { this.Logger.Log(LogType.Error, this, $"在事件{nameof(this.Connected)}中发生错误。", ex); } } private void PrivateOnConnecting(ConnectingEventArgs e) { this.LastReceivedTime = DateTime.Now; this.LastSendTime = DateTime.Now; if (this.CanSetDataHandlingAdapter) { this.SetDataHandlingAdapter(this.Config.GetValue>(TouchSocketConfigExtension.DataHandlingAdapterProperty).Invoke()); } if (this.UsePlugin) { this.PluginsManager.Raise(nameof(IConnectingPlugin.OnConnecting), this, e); if (e.Handled) { return; } } this.OnConnecting(e); } /// /// 准备连接的时候,此时已初始化Socket,但是并未建立Tcp连接 /// /// protected virtual void OnConnecting(ConnectingEventArgs e) { try { this.Connecting?.Invoke(this, e); } catch (Exception ex) { this.Logger.Log(LogType.Error, this, $"在事件{nameof(this.OnConnecting)}中发生错误。", ex); } } private void PrivateOnDisconnected(DisconnectEventArgs e) { if (this.UsePlugin && this.PluginsManager.Raise(nameof(IDisconnectedPlguin.OnDisconnected), this, e)) { return; } this.OnDisconnected(e); } /// /// 断开连接。在客户端未设置连接状态时,不会触发 /// /// protected virtual void OnDisconnected(DisconnectEventArgs e) { try { this.Disconnected?.Invoke(this, e); } catch (Exception ex) { this.Logger.Log(LogType.Error, this, $"在事件{nameof(this.Disconnected)}中发生错误。", ex); } } private void PrivateOnDisconnecting(DisconnectEventArgs e) { if (this.UsePlugin && this.PluginsManager.Raise(nameof(IDisconnectingPlugin.OnDisconnecting), this, e)) { return; } this.OnDisconnecting(e); } /// /// 即将断开连接(仅主动断开时有效)。 /// /// 当主动调用Close断开时,可通过终止断开行为。 /// /// /// protected virtual void OnDisconnecting(DisconnectEventArgs e) { try { this.Disconnecting?.Invoke(this, e); } catch (Exception ex) { this.Logger.Log(LogType.Error, this, $"在事件{nameof(this.Disconnecting)}中发生错误。", ex); } } #endregion 事件 #region 属性 /// /// /// public DateTime LastReceivedTime { get; private set; } /// /// /// public DateTime LastSendTime { get; private set; } /// /// 处理未经过适配器的数据。返回值表示是否继续向下传递。 /// public Func OnHandleRawBuffer { get; set; } /// /// 处理经过适配器后的数据。返回值表示是否继续向下传递。 /// public Func OnHandleReceivedData { get; set; } /// /// /// public IContainer Container => this.Config?.Container; /// /// /// public virtual bool CanSetDataHandlingAdapter => true; /// /// 客户端配置 /// public TouchSocketConfig Config { get; private set; } /// /// 数据处理适配器 /// public DataHandlingAdapter DataHandlingAdapter { get; private set; } /// /// IP地址 /// public string IP { get; private set; } /// /// 主通信器 /// public Socket MainSocket { get; private set; } /// /// /// public bool CanSend { get; private set; } /// /// /// public bool Online => this.CanSend; /// /// /// public IPluginsManager PluginsManager { get; private set; } /// /// 端口号 /// public int Port { get; private set; } /// /// /// public ReceiveType ReceiveType { get; private set; } /// /// 是否已启用插件 /// public bool UsePlugin { get; private set; } /// /// /// public bool UseSsl { get; private set; } /// /// /// public Protocol Protocol { get; set; } /// /// /// public IPHost RemoteIPHost { get; private set; } /// public bool IsClient => true; #endregion 属性 #region 断开操作 /// /// /// public virtual void Close() { this.Close($"{nameof(Close)}主动断开"); } /// /// 中断终端,传递中断消息。 /// /// public virtual void Close(string msg) { if (this.CanSend) { var args = new DisconnectEventArgs(true, msg) { IsPermitOperation = true }; this.PrivateOnDisconnecting(args); if (this.DisposedValue || args.IsPermitOperation) { this.BreakOut(msg, true); } } } private void BreakOut(string msg, bool manual) { lock (this.SyncRoot) { if (this.CanSend) { this.CanSend = false; this.TryShutdown(); this.MainSocket.SafeDispose(); this.m_delaySender.SafeDispose(); this.m_workStream.SafeDispose(); this.DataHandlingAdapter.SafeDispose(); this.PrivateOnDisconnected(new DisconnectEventArgs(manual, msg)); } } } /// /// /// /// protected override void Dispose(bool disposing) { if (this.CanSend) { var args = new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开"); this.PrivateOnDisconnecting(args); } this.PluginsManager?.Clear(); this.Config = default; this.DataHandlingAdapter.SafeDispose(); this.DataHandlingAdapter = default; this.BreakOut($"{nameof(Dispose)}主动断开", true); base.Dispose(disposing); } #endregion 断开操作 /// /// 建立Tcp的连接。 /// /// /// /// /// /// protected void TcpConnect(int timeout) { lock (this.SyncRoot) { if (this.CanSend) { return; } if (this.DisposedValue) { throw new ObjectDisposedException(this.GetType().FullName); } if (this.Config == null) { throw new ArgumentNullException("配置文件不能为空。"); } IPHost iPHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty); if (iPHost == null) { throw new ArgumentNullException("iPHost不能为空。"); } if (this.MainSocket != null) { this.MainSocket.Dispose(); } this.MainSocket = this.CreateSocket(iPHost); ConnectingEventArgs args = new ConnectingEventArgs(this.MainSocket); this.PrivateOnConnecting(args); if (timeout == 5000) { this.MainSocket.Connect(iPHost.EndPoint); this.CanSend = true; this.LoadSocketAndReadIpPort(); if (this.Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty) is DelaySenderOption senderOption) { this.m_useDelaySender = true; this.m_delaySender.SafeDispose(); this.m_delaySender = new DelaySender(this.MainSocket, senderOption.QueueLength, this.OnDelaySenderError) { DelayLength = senderOption.DelayLength }; } this.BeginReceive(); this.PrivateOnConnected(new MsgEventArgs("连接成功")); } else { var result = this.MainSocket.BeginConnect(iPHost.EndPoint, null, null); if (result.AsyncWaitHandle.WaitOne(timeout)) { if (this.MainSocket.Connected) { this.MainSocket.EndConnect(result); this.CanSend = true; this.LoadSocketAndReadIpPort(); if (this.Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty) is DelaySenderOption senderOption) { this.m_useDelaySender = true; this.m_delaySender.SafeDispose(); this.m_delaySender = new DelaySender(this.MainSocket, senderOption.QueueLength, this.OnDelaySenderError) { DelayLength = senderOption.DelayLength }; } this.BeginReceive(); this.PrivateOnConnected(new MsgEventArgs("连接成功")); return; } else { this.MainSocket.SafeDispose(); throw new Exception("异步已完成,但是socket并未在连接状态,可能发生了绑定端口占用的错误。"); } } this.MainSocket.SafeDispose(); throw new TimeoutException(); } } } /// /// 请求连接到服务器。 /// public virtual ITcpClient Connect(int timeout = 5000) { this.TcpConnect(timeout); return this; } /// /// 异步连接服务器 /// public Task ConnectAsync(int timeout = 5000) { return EasyTask.Run(() => { return this.Connect(timeout); }); } /// /// /// /// public Stream GetStream() { if (this.m_workStream == null) { this.m_workStream = new NetworkStream(this.MainSocket, true); } return this.m_workStream; } /// /// 设置数据处理适配器 /// /// public virtual void SetDataHandlingAdapter(DataHandlingAdapter adapter) { if (!this.CanSetDataHandlingAdapter) { throw new Exception($"不允许自由调用{nameof(SetDataHandlingAdapter)}进行赋值。"); } this.SetAdapter(adapter); } /// /// /// /// /// public ITcpClient Setup(string ipHost) { TouchSocketConfig config = new TouchSocketConfig(); config.SetRemoteIPHost(new IPHost(ipHost)); return this.Setup(config); } /// /// 配置服务器 /// /// /// public ITcpClient Setup(TouchSocketConfig config) { this.Config = config; this.PluginsManager = config.PluginsManager; if (config.IsUsePlugin) { this.PluginsManager.Raise(nameof(IConfigPlugin.OnLoadingConfig), this, new ConfigEventArgs(config)); } this.LoadConfig(this.Config); if (this.UsePlugin) { this.PluginsManager.Raise(nameof(IConfigPlugin.OnLoadedConfig), this, new ConfigEventArgs(config)); } return this; } private void PrivateHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) { if (this.OnHandleReceivedData?.Invoke(byteBlock, requestInfo) == false) { return; } if (this.UsePlugin) { ReceivedDataEventArgs args = new ReceivedDataEventArgs(byteBlock, requestInfo); this.PluginsManager.Raise(nameof(ITcpPlugin.OnReceivedData), this, args); if (args.Handled) { return; } } this.HandleReceivedData(byteBlock, requestInfo); } /// /// 处理已接收到的数据。 /// /// 以二进制流形式传递 /// 以解析的数据对象传递 protected virtual void HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo) { } /// /// 当即将发送时,如果覆盖父类方法,则不会触发插件。 /// /// 数据缓存区 /// 偏移 /// 长度 /// 返回值表示是否允许发送 protected virtual bool HandleSendingData(byte[] buffer, int offset, int length) { if (this.UsePlugin) { SendingEventArgs args = new SendingEventArgs(buffer, offset, length); this.PluginsManager.Raise(nameof(ITcpPlugin.OnSendingData), this, args); if (args.IsPermitOperation) { return true; } return false; } return true; } /// /// 加载配置 /// /// protected virtual void LoadConfig(TouchSocketConfig config) { if (config == null) { throw new Exception("配置文件为空"); } this.RemoteIPHost = config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty); this.BufferLength = config.GetValue(TouchSocketConfigExtension.BufferLengthProperty); this.ReceiveType = config.GetValue(TouchSocketConfigExtension.ReceiveTypeProperty); this.UsePlugin = config.IsUsePlugin; this.Logger = this.Container.Resolve(); if (config.GetValue(TouchSocketConfigExtension.SslOptionProperty) != null) { this.UseSsl = true; } } /// /// 在延迟发生错误 /// /// protected virtual void OnDelaySenderError(Exception ex) { this.Logger.Log(LogType.Error, this, "发送错误", ex); } /// /// 设置适配器,该方法不会检验的值。 /// /// protected void SetAdapter(DataHandlingAdapter adapter) { if (adapter is null) { throw new ArgumentNullException(nameof(adapter)); } if (this.Config != null) { if (this.Config.GetValue(TouchSocketConfigExtension.MaxPackageSizeProperty) is int v1) { adapter.MaxPackageSize = v1; } if (this.Config.GetValue(TouchSocketConfigExtension.CacheTimeoutProperty) != TimeSpan.Zero) { adapter.CacheTimeout = this.Config.GetValue(TouchSocketConfigExtension.CacheTimeoutProperty); } if (this.Config.GetValue(TouchSocketConfigExtension.CacheTimeoutEnableProperty) is bool v2) { adapter.CacheTimeoutEnable = v2; } if (this.Config.GetValue(TouchSocketConfigExtension.UpdateCacheTimeWhenRevProperty) is bool v3) { adapter.UpdateCacheTimeWhenRev = v3; } } adapter.OnLoaded(this); adapter.ReceivedCallBack = this.PrivateHandleReceivedData; adapter.SendCallBack = this.DefaultSend; this.DataHandlingAdapter = adapter; } private void BeginReceive() { this.m_workStream.SafeDispose(); if (this.UseSsl) { ClientSslOption sslOption = (ClientSslOption)this.Config.GetValue(TouchSocketConfigExtension.SslOptionProperty); SslStream sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.MainSocket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.MainSocket, false), false); if (sslOption.ClientCertificates == null) { sslStream.AuthenticateAsClient(sslOption.TargetHost); } else { sslStream.AuthenticateAsClient(sslOption.TargetHost, sslOption.ClientCertificates, sslOption.SslProtocols, sslOption.CheckCertificateRevocation); } this.m_workStream = sslStream; if (this.ReceiveType != ReceiveType.None) { this.BeginSsl(); } } else { if (this.ReceiveType == ReceiveType.Auto) { SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs(); eventArgs.Completed += this.EventArgs_Completed; ByteBlock byteBlock = BytePool.Default.GetByteBlock(this.BufferLength); eventArgs.UserToken = byteBlock; eventArgs.SetBuffer(byteBlock.Buffer, 0, byteBlock.Buffer.Length); if (!this.MainSocket.ReceiveAsync(eventArgs)) { this.ProcessReceived(eventArgs); } } } } private void BeginSsl() { ByteBlock byteBlock = new ByteBlock(this.BufferLength); try { this.m_workStream.BeginRead(byteBlock.Buffer, 0, byteBlock.Capacity, this.EndSsl, byteBlock); } catch (Exception ex) { byteBlock.Dispose(); this.BreakOut(ex.Message, false); } } private void EndSsl(IAsyncResult result) { ByteBlock byteBlock = (ByteBlock)result.AsyncState; try { int r = this.m_workStream.EndRead(result); if (r == 0) { this.BreakOut("远程终端主动关闭", false); } byteBlock.SetLength(r); this.HandleBuffer(byteBlock); this.BeginSsl(); } catch (System.Exception ex) { byteBlock.Dispose(); this.BreakOut(ex.Message, false); } } private Socket CreateSocket(IPHost iPHost) { Socket socket = new Socket(iPHost.AddressFamily, SocketType.Stream, ProtocolType.Tcp); socket.ReceiveBufferSize = this.BufferLength; socket.SendBufferSize = this.BufferLength; #if NET45_OR_GREATER KeepAliveValue keepAliveValue = this.Config.GetValue(TouchSocketConfigExtension.KeepAliveValueProperty); if (keepAliveValue.Enable) { socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); socket.IOControl(IOControlCode.KeepAliveValues, keepAliveValue.KeepAliveTime, null); } #else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { KeepAliveValue keepAliveValue = Config.GetValue(TouchSocketConfigExtension.KeepAliveValueProperty); if (keepAliveValue.Enable) { socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); socket.IOControl(IOControlCode.KeepAliveValues, keepAliveValue.KeepAliveTime, null); } } #endif socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, this.Config.GetValue(TouchSocketConfigExtension.NoDelayProperty)); if (this.Config.GetValue(TouchSocketConfigExtension.BindIPHostProperty) != null) { if (this.Config.GetValue(TouchSocketConfigExtension.ReuseAddressProperty)) { socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); } socket.Bind(this.Config.GetValue(TouchSocketConfigExtension.BindIPHostProperty).EndPoint); } return socket; } private void EventArgs_Completed(object sender, SocketAsyncEventArgs e) { try { this.ProcessReceived(e); } catch (System.Exception ex) { e.Dispose(); this.BreakOut(ex.Message, false); } } /// /// 处理数据 /// private void HandleBuffer(ByteBlock byteBlock) { try { this.LastReceivedTime = DateTime.Now; if (this.OnHandleRawBuffer?.Invoke(byteBlock) == false) { return; } if (this.DisposedValue) { return; } if (this.UsePlugin && this.PluginsManager.Raise(nameof(ITcpPlugin.OnReceivingData), this, new ByteBlockEventArgs(byteBlock))) { return; } if (this.DataHandlingAdapter == null) { this.Logger.Error(this, TouchSocketStatus.NullDataAdapter.GetDescription()); return; } this.DataHandlingAdapter.ReceivedInput(byteBlock); } catch (Exception ex) { this.Logger.Log(LogType.Error, this, "在处理数据时发生错误", ex); } finally { byteBlock.Dispose(); } } #region 发送 #region 同步发送 /// /// /// /// /// /// /// public void Send(IRequestInfo requestInfo) { if (this.DisposedValue) { return; } if (this.DataHandlingAdapter == null) { throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketStatus.NullDataAdapter.GetDescription()); } if (!this.DataHandlingAdapter.CanSendRequestInfo) { throw new NotSupportedException($"当前适配器不支持对象发送。"); } this.DataHandlingAdapter.SendInput(requestInfo); } /// /// /// /// /// /// /// /// /// public virtual void Send(byte[] buffer, int offset, int length) { if (this.DataHandlingAdapter == null) { throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketStatus.NullDataAdapter.GetDescription()); } this.DataHandlingAdapter.SendInput(buffer, offset, length); } /// /// /// /// /// /// /// public virtual void Send(IList> transferBytes) { if (this.DataHandlingAdapter == null) { throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketStatus.NullDataAdapter.GetDescription()); } if (this.DataHandlingAdapter.CanSplicingSend) { this.DataHandlingAdapter.SendInput(transferBytes); } else { ByteBlock byteBlock = BytePool.Default.GetByteBlock(this.BufferLength); try { foreach (var item in transferBytes) { byteBlock.Write(item.Array, item.Offset, item.Count); } this.DataHandlingAdapter.SendInput(byteBlock.Buffer, 0, byteBlock.Len); } finally { byteBlock.Dispose(); } } } #endregion 同步发送 #region 异步发送 /// /// /// /// /// /// /// /// /// public virtual Task SendAsync(byte[] buffer, int offset, int length) { return EasyTask.Run(() => { this.Send(buffer, offset, length); }); } /// /// /// /// /// /// /// public virtual Task SendAsync(IRequestInfo requestInfo) { return EasyTask.Run(() => { this.Send(requestInfo); }); } /// /// /// /// /// /// /// public virtual Task SendAsync(IList> transferBytes) { return EasyTask.Run(() => { this.Send(transferBytes); }); } #endregion 异步发送 /// /// /// /// /// /// /// /// /// public void DefaultSend(byte[] buffer, int offset, int length) { if (!this.CanSend) { throw new NotConnectedException(TouchSocketStatus.NotConnected.GetDescription()); } if (this.HandleSendingData(buffer, offset, length)) { if (this.UseSsl) { this.m_workStream.Write(buffer, offset, length); } else { if (this.m_useDelaySender && length < TouchSocketUtility.BigDataBoundary) { this.m_delaySender.Send(new QueueDataBytes(buffer, offset, length)); } else { this.MainSocket.AbsoluteSend(buffer, offset, length); } } this.LastSendTime = DateTime.Now; } } /// /// /// /// /// /// /// /// /// public Task DefaultSendAsync(byte[] buffer, int offset, int length) { return EasyTask.Run(() => { this.DefaultSend(buffer, offset, length); }); } #endregion 发送 private void LoadSocketAndReadIpPort() { if (this.MainSocket == null) { this.IP = null; this.Port = -1; return; } string ipport; if (this.MainSocket.Connected && this.MainSocket.RemoteEndPoint != null) { ipport = this.MainSocket.RemoteEndPoint.ToString(); } else if (this.MainSocket.IsBound && this.MainSocket.LocalEndPoint != null) { ipport = this.MainSocket.LocalEndPoint.ToString(); } else { return; } int r = ipport.LastIndexOf(":"); this.IP = ipport.Substring(0, r); this.Port = Convert.ToInt32(ipport.Substring(r + 1, ipport.Length - (r + 1))); } private void ProcessReceived(SocketAsyncEventArgs e) { if (!this.CanSend) { e.Dispose(); return; } if (e.SocketError == SocketError.Success && e.BytesTransferred > 0) { ByteBlock byteBlock = (ByteBlock)e.UserToken; byteBlock.SetLength(e.BytesTransferred); this.HandleBuffer(byteBlock); try { ByteBlock newByteBlock = BytePool.Default.GetByteBlock(this.BufferLength); e.UserToken = newByteBlock; e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Buffer.Length); if (!this.MainSocket.ReceiveAsync(e)) { this.ProcessReceived(e); } } catch (System.Exception ex) { e.Dispose(); this.BreakOut(ex.Message, false); } } else { e.Dispose(); this.BreakOut("远程终端主动关闭", false); } } } }