using HslCommunication.Core; using HslCommunication.Core.Net; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace HslCommunication.Enthernet { /// /// 高性能的异步网络服务器类,适合搭建局域网聊天程序,消息推送程序 /// /// /// 详细的使用说明,请参照博客http://www.cnblogs.com/dathlin/p/8097897.html /// /// /// 此处贴上了Demo项目的服务器配置的示例代码 /// /// public class NetComplexServer : NetworkServerBase { #region Constructor /// /// 实例化一个网络服务器类对象 /// public NetComplexServer() { appSessions = new List( ); lockSessions = new SimpleHybirdLock( ); } #endregion #region Private Member private int connectMaxClient = 1000; // 允许同时登录的最大客户端数量 private List appSessions = null; // 所有客户端连接的对象信息 private SimpleHybirdLock lockSessions = null; // 对象列表操作的锁 #endregion #region Public Properties /// /// 所支持的同时在线客户端的最大数量,商用限制1000个,最小10个 /// public int ConnectMax { get { return connectMaxClient; } set { if (value >= 10 && value < 1001) { connectMaxClient = value; } } } /// /// 获取或设置服务器是否记录客户端上下线信息 /// public bool IsSaveLogClientLineChange { get; set; } = true; /// /// 所有在线客户端的数量 /// public int ClientCount => appSessions.Count; #endregion #region NetworkServerBase Override /// /// 初始化操作 /// protected override void StartInitialization() { Thread_heart_check = new Thread( new ThreadStart( ThreadHeartCheck ) ) { IsBackground = true, Priority = ThreadPriority.AboveNormal }; Thread_heart_check.Start( ); base.StartInitialization( ); } /// /// 关闭网络时的操作 /// protected override void CloseAction() { Thread_heart_check?.Abort( ); ClientOffline = null; ClientOnline = null; AcceptString = null; AcceptByte = null; //关闭所有的网络 appSessions.ForEach( m => m.WorkSocket?.Close( ) ); base.CloseAction( ); } /// /// 异常下线 /// /// 会话信息 /// 异常 internal override void SocketReceiveException( AppSession session, Exception ex ) { if (ex.Message.Contains( StringResources.Language.SocketRemoteCloseException )) { //异常掉线 TcpStateDownLine( session, false ); } } /// /// 正常下线 /// /// 会话信息 internal override void AppSessionRemoteClose( AppSession session ) { TcpStateDownLine( session, true ); } #endregion #region Client Online Offline private void TcpStateUpLine( AppSession state ) { lockSessions.Enter( ); appSessions.Add( state ); lockSessions.Leave( ); // 提示上线 ClientOnline?.Invoke( state ); AllClientsStatusChange?.Invoke( ClientCount ); // 是否保存上线信息 if (IsSaveLogClientLineChange) { LogNet?.WriteInfo( ToString( ), $"[{state.IpEndPoint}] Name:{ state?.LoginAlias } { StringResources.Language.NetClientOnline }" ); } } private void TcpStateClose( AppSession state ) { state?.WorkSocket?.Close( ); } private void TcpStateDownLine( AppSession state, bool is_regular, bool logSave = true ) { lockSessions.Enter( ); bool success = appSessions.Remove( state ); lockSessions.Leave( ); if (!success) return; // 关闭连接 TcpStateClose( state ); // 判断是否正常下线 string str = is_regular ? StringResources.Language.NetClientOffline : StringResources.Language.NetClientBreak; ClientOffline?.Invoke( state, str ); AllClientsStatusChange?.Invoke( ClientCount ); // 是否保存上线信息 if (IsSaveLogClientLineChange && logSave) { LogNet?.WriteInfo( ToString( ), $"[{state.IpEndPoint}] Name:{ state?.LoginAlias } { str }" ); } } #endregion #region Event Handle /// /// 客户端的上下限状态变更时触发,仅作为在线客户端识别 /// public event Action AllClientsStatusChange; /// /// 当客户端上线的时候,触发此事件 /// public event Action ClientOnline; /// /// 当客户端下线的时候,触发此事件 /// public event Action ClientOffline; /// /// 当接收到文本数据的时候,触发此事件 /// public event Action AcceptString; /// /// 当接收到字节数据的时候,触发此事件 /// public event Action AcceptByte; #endregion #region Login Server /// /// 当接收到了新的请求的时候执行的操作 /// /// 异步对象 /// 终结点 protected override void ThreadPoolLogin( Socket socket, IPEndPoint endPoint ) { // 判断连接数是否超出规定 if (appSessions.Count > ConnectMax) { socket?.Close( ); LogNet?.WriteWarn( ToString( ), StringResources.Language.NetClientFull ); return; } // 接收用户别名并验证令牌 OperateResult result = new OperateResult( ); OperateResult readResult = ReceiveStringContentFromSocket( socket ); if (!readResult.IsSuccess) return; // 登录成功 AppSession session = new AppSession( ) { WorkSocket = socket, LoginAlias = readResult.Content2, }; try { session.IpEndPoint = (IPEndPoint)socket.RemoteEndPoint; session.IpAddress = ((IPEndPoint)socket.RemoteEndPoint).Address.ToString( ); } catch (Exception ex) { LogNet?.WriteException( ToString( ), StringResources.Language.GetClientIpaddressFailed, ex ); } if (readResult.Content1 == 1) { // 电脑端客户端 session.ClientType = "Windows"; } else if (readResult.Content1 == 2) { // Android 客户端 session.ClientType = "Android"; } try { session.WorkSocket.BeginReceive( session.BytesHead, session.AlreadyReceivedHead, session.BytesHead.Length - session.AlreadyReceivedHead, SocketFlags.None, new AsyncCallback( HeadBytesReceiveCallback ), session ); TcpStateUpLine( session ); Thread.Sleep( 100 );// 留下一些时间进行反应 } catch (Exception ex) { // 登录前已经出错 TcpStateClose( session ); LogNet?.WriteException( ToString( ), StringResources.Language.NetClientLoginFailed, ex ); } } #endregion #region SendAsync Support /// /// 服务器端用于数据发送文本的方法 /// /// 数据发送对象 /// 用户自定义的数据对象,如不需要,赋值为0 /// 发送的文本 public void Send( AppSession session, NetHandle customer, string str ) { SendBytes( session, HslProtocol.CommandBytes( customer, Token, str ) ); } /// /// 服务器端用于发送字节的方法 /// /// 数据发送对象 /// 用户自定义的数据对象,如不需要,赋值为0 /// 实际发送的数据 public void Send( AppSession session, NetHandle customer, byte[] bytes ) { SendBytes( session, HslProtocol.CommandBytes( customer, Token, bytes ) ); } private void SendBytes( AppSession session, byte[] content ) { SendBytesAsync( session, content ); } /// /// 服务端用于发送所有数据到所有的客户端 /// /// 用户自定义的命令头 /// 需要传送的实际的数据 public void SendAllClients( NetHandle customer, string str ) { for (int i = 0; i < appSessions.Count; i++) { Send( appSessions[i], customer, str ); } } /// /// 服务端用于发送所有数据到所有的客户端 /// /// 用户自定义的命令头 /// 需要群发客户端的字节数据 public void SendAllClients( NetHandle customer, byte[] data ) { for (int i = 0; i < appSessions.Count; i++) { Send( appSessions[i], customer, data ); } } /// /// 根据客户端设置的别名进行发送消息 /// /// 客户端上线的别名 /// 用户自定义的命令头 /// 需要传送的实际的数据 public void SendClientByAlias( string Alias, NetHandle customer, string str ) { for (int i = 0; i < appSessions.Count; i++) { if (appSessions[i].LoginAlias == Alias) { Send( appSessions[i], customer, str ); } } } /// /// 根据客户端设置的别名进行发送消息 /// /// 客户端上线的别名 /// 用户自定义的命令头 /// 需要传送的实际的数据 public void SendClientByAlias( string Alias, NetHandle customer, byte[] data ) { for (int i = 0; i < appSessions.Count; i++) { if (appSessions[i].LoginAlias == Alias) { Send( appSessions[i], customer, data ); } } } #endregion #region DataProcessingCenter /// /// 数据处理中心 /// /// 会话对象 /// 消息的代码 /// 用户消息 /// 数据内容 internal override void DataProcessingCenter( AppSession session, int protocol, int customer, byte[] content ) { if (protocol == HslProtocol.ProtocolCheckSecends) { BitConverter.GetBytes( DateTime.Now.Ticks ).CopyTo( content, 8 ); SendBytes( session, HslProtocol.CommandBytes( HslProtocol.ProtocolCheckSecends, customer, Token, content ) ); session.HeartTime = DateTime.Now; } else if (protocol == HslProtocol.ProtocolClientQuit) { TcpStateDownLine( session, true ); } else if (protocol == HslProtocol.ProtocolUserBytes) { //接收到字节数据 AcceptByte?.Invoke( session, customer, content ); } else if (protocol == HslProtocol.ProtocolUserString) { //接收到文本数据 string str = Encoding.Unicode.GetString( content ); AcceptString?.Invoke( session, customer, str ); } else { // 其他一概不处理 } } #endregion #region Heart Check private Thread Thread_heart_check { get; set; } = null; private void ThreadHeartCheck() { while (true) { Thread.Sleep( 2000 ); try { for (int i = appSessions.Count - 1; i >= 0; i--) { if (appSessions[i] == null) { appSessions.RemoveAt( i ); continue; } if ((DateTime.Now - appSessions[i].HeartTime).TotalSeconds > 1 * 8)//8次没有收到失去联系 { LogNet?.WriteWarn( ToString( ), StringResources.Language.NetHeartCheckTimeout + appSessions[i].IpAddress.ToString( ) ); TcpStateDownLine( appSessions[i], false, false ); continue; } } } catch (Exception ex) { LogNet?.WriteException( ToString( ), StringResources.Language.NetHeartCheckFailed, ex ); } if (!IsStarted) break; } } #endregion #region Object Override /// /// 获取本对象的字符串表示形式 /// /// 字符串 public override string ToString() { return "NetComplexServer"; } #endregion } }