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
{
///
/// 高性能的异步网络服务器类,适合搭建局域网聊天程序,消息推送程序
///
public class NetComplexServer : NetworkServerBase
{
#region 构造方法块
///
/// 实例化一个网络服务器类对象
///
public NetComplexServer( )
{
AsyncCoordinator = new HslAsyncCoordinator( new Action( CalculateOnlineClients ) );
}
#endregion
#region 基本属性块
private int m_Connect_Max = 1000;
///
/// 所支持的同时在线客户端的最大数量,商用限制1000个,最小10个
///
public int ConnectMax
{
get { return m_Connect_Max; }
set
{
if (value >= 10 && value < 1001)
{
m_Connect_Max = value;
}
}
}
///
/// 客户端在线信息显示的格式化文本,如果自定义,必须#开头,
/// 示例:"#IP:{0} Name:{1}"
///
public string FormatClientOnline { get; set; } = "#IP:{0} Name:{1}";
///
/// 客户端在线信息缓存
///
private string m_AllClients = string.Empty;
#region 高性能乐观并发模型的上下线控制
private void CalculateOnlineClients( )
{
StringBuilder builder = new StringBuilder( );
HybirdLockSockets.Enter( );
for (int i = 0; i < All_sockets_connect.Count; i++)
{
builder.Append( string.Format( FormatClientOnline, All_sockets_connect[i].IpAddress
, All_sockets_connect[i].LoginAlias ) );
}
HybirdLockSockets.Leave( );
if (builder.Length > 0)
{
m_AllClients = builder.Remove( 0, 1 ).ToString( );
}
else
{
m_AllClients = string.Empty;
}
// 触发状态变更
AllClientsStatusChange?.Invoke( m_AllClients );
}
///
/// 一个计算上线下线的高性能缓存对象
///
private HslAsyncCoordinator AsyncCoordinator { get; set; }
#endregion
///
/// 计算所有客户端在线的信息
///
///
/// 获取或设置服务器是否记录客户端上下线信息
///
public bool IsSaveLogClientLineChange { get; set; } = true;
///
/// 所有在线客户端的数量
///
public int ClientCount => All_sockets_connect.Count;
///
/// 所有的客户端连接的核心对象
///
private List All_sockets_connect { get; set; } = new List( );
///
/// 客户端数组操作的线程混合锁
///
private SimpleHybirdLock HybirdLockSockets = new SimpleHybirdLock( );
#endregion
#region 启动停止块
///
/// 初始化操作
///
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;
//关闭所有的网络
All_sockets_connect.ForEach( m => m.WorkSocket?.Close( ) );
base.CloseAction( );
}
#endregion
#region 客户端上下线块
private void TcpStateUpLine( AppSession state )
{
HybirdLockSockets.Enter( );
All_sockets_connect.Add( state );
HybirdLockSockets.Leave( );
// 提示上线
ClientOnline?.Invoke( state );
// 是否保存上线信息
if (IsSaveLogClientLineChange)
{
LogNet?.WriteInfo( ToString(), "IP:" + state.IpAddress + " Name:" + state?.LoginAlias + " " + StringResources.NetClientOnline );
}
// 计算客户端在线情况
AsyncCoordinator.StartOperaterInfomation( );
}
private void TcpStateClose( AppSession state )
{
state?.WorkSocket.Close( );
}
private void TcpStateDownLine( AppSession state, bool is_regular )
{
HybirdLockSockets.Enter( );
All_sockets_connect.Remove( state );
HybirdLockSockets.Leave( );
// 关闭连接
TcpStateClose( state );
// 判断是否正常下线
string str = is_regular ? StringResources.NetClientOffline : StringResources.NetClientBreak;
ClientOffline?.Invoke( state, str );
// 是否保存上线信息
if (IsSaveLogClientLineChange)
{
LogNet?.WriteInfo( ToString(), "IP:" + state.IpAddress + " Name:" + state?.LoginAlias + " " + str );
}
// 计算客户端在线情况
AsyncCoordinator.StartOperaterInfomation( );
}
#endregion
#region 事件委托块
///
/// 客户端的上下限状态变更时触发,仅作为在线客户端识别
///
public event Action AllClientsStatusChange;
///
/// 当客户端上线的时候,触发此事件
///
public event Action ClientOnline;
///
/// 当客户端下线的时候,触发此事件
///
public event Action ClientOffline;
///
/// 当接收到文本数据的时候,触发此事件
///
public event Action AcceptString;
///
/// 当接收到字节数据的时候,触发此事件
///
public event Action AcceptByte;
#endregion
#region 请求接入块
///
/// 登录后的处理方法
///
///
protected override void ThreadPoolLogin( object obj )
{
if (obj is Socket)
{
Socket socket = obj as Socket;
// 判断连接数是否超出规定
if (All_sockets_connect.Count > ConnectMax)
{
socket?.Close( );
LogNet?.WriteWarn( ToString(), StringResources.NetClientFull );
return;
}
// 接收用户别名并验证令牌
OperateResult result = new OperateResult( );
OperateResult readResult = ReceiveStringContentFromSocket( socket );
if (!readResult.IsSuccess)
{
socket?.Close( );
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( ), "客户端地址获取失败:", 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( 500 );//留下一些时间进行反应
}
catch (Exception ex)
{
//登录前已经出错
TcpStateClose( session );
LogNet?.WriteException( ToString(), StringResources.NetClientLoginFailed, ex );
}
}
}
#endregion
#region 异步接收发送块
///
/// 异常下线
///
///
///
internal override void SocketReceiveException( AppSession session, Exception ex )
{
if (ex.Message.Contains( StringResources.SocketRemoteCloseException ))
{
//异常掉线
TcpStateDownLine( session, false );
}
}
///
/// 正常下线
///
///
internal override void AppSessionRemoteClose( AppSession session )
{
TcpStateDownLine( session, true );
}
///
/// 服务器端用于数据发送文本的方法
///
/// 数据发送对象
/// 用户自定义的数据对象,如不需要,赋值为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 < All_sockets_connect.Count; i++)
{
Send( All_sockets_connect[i], customer, str );
}
}
///
/// 服务端用于发送所有数据到所有的客户端
///
/// 用户自定义的命令头
/// 需要群发客户端的字节数据
public void SendAllClients( NetHandle customer, byte[] data )
{
for (int i = 0; i < All_sockets_connect.Count; i++)
{
Send( All_sockets_connect[i], customer, data );
}
}
///
/// 根据客户端设置的别名进行发送消息
///
/// 客户端上线的别名
/// 用户自定义的命令头
/// 需要传送的实际的数据
public void SendClientByAlias( string Alias, NetHandle customer, string str )
{
for (int i = 0; i < All_sockets_connect.Count; i++)
{
if (All_sockets_connect[i].LoginAlias == Alias)
{
Send( All_sockets_connect[i], customer, str );
}
}
}
///
/// 根据客户端设置的别名进行发送消息
///
/// 客户端上线的别名
/// 用户自定义的命令头
/// 需要传送的实际的数据
public void SendClientByAlias( string Alias, NetHandle customer, byte[] data )
{
for (int i = 0; i < All_sockets_connect.Count; i++)
{
if (All_sockets_connect[i].LoginAlias == Alias)
{
Send( All_sockets_connect[i], customer, data );
}
}
}
#endregion
#region 数据中心处理块
///
/// 数据处理中心
///
///
///
///
///
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 心跳线程块
private Thread Thread_heart_check { get; set; } = null;
private void ThreadHeartCheck( )
{
while (true)
{
Thread.Sleep( 2000 );
try
{
for (int i = All_sockets_connect.Count - 1; i >= 0; i--)
{
if (All_sockets_connect[i] == null)
{
All_sockets_connect.RemoveAt( i );
continue;
}
if ((DateTime.Now - All_sockets_connect[i].HeartTime).TotalSeconds > 1 * 8)//8次没有收到失去联系
{
LogNet?.WriteWarn( ToString(), "心跳验证超时,强制下线:" + All_sockets_connect[i].IpAddress.ToString( ) );
TcpStateDownLine( All_sockets_connect[i], false );
continue;
}
}
}
catch (Exception ex)
{
LogNet?.WriteException( ToString(), "心跳线程异常:", ex );
}
if (!IsStarted) break;
}
}
#endregion
#region Object Override
///
/// 获取本对象的字符串表示形式
///
///
public override string ToString( )
{
return "NetComplexServer";
}
#endregion
}
}