using Google.Protobuf; using HunterProtobufCore; using System.Net; using System.Net.Sockets; namespace HaoYueNet.ServerNetwork { public class TcpSaeaServer { /// /// 心跳包数据 /// static byte[] HeartbeatData = new byte[5] { 0x05, 0x00, 0x00, 0x00, 0x00 }; //响应倒计时计数最大值 //public int MaxRevIndexNum { get; set; } = 5; ////发送倒计时计数最大值 //public int MaxSendIndexNum { get; set; } = 3; //响应倒计时计数最大值 public int MaxRevIndexNum { get; set; } = 50; //发送倒计时计数最大值 public int MaxSendIndexNum { get; set; } = 3; //计时器间隔 private static int TimerInterval = 3000; /// /// 心跳包计数器 /// private System.Timers.Timer _heartTimer; public int m_maxConnectNum; //最大连接数 public int m_revBufferSize; //最大接收字节数 protected BufferManager m_bufferManager; protected const int opsToAlloc = 2; Socket listenSocket; //监听Socket protected SocketEventPool m_Receivepool; protected SocketEventPool m_Sendpool; protected TokenMsgPool msg_pool; protected int m_clientCount; //连接的客户端数量 protected Semaphore m_maxNumberAcceptedClients;//信号量 List m_clients; //客户端列表 protected Dictionary _DictSocketAsyncUserToken = new Dictionary(); #region 定义委托 /// /// 客户端连接数量变化时触发 /// /// 当前增加客户的个数(用户退出时为负数,增加时为正数,一般为1) /// 增加用户的信息 public delegate void OnClientNumberChangeHandler(int num, AsyncUserToken token); /// /// 接收到客户端的数据 /// /// 客户端 /// 客户端数据 public delegate void OnReceiveDataHandler(AsyncUserToken sk, int CMDID, byte[] data); /// /// 断开连接 /// /// public delegate void OnDisconnectHandler(AsyncUserToken sk); /// /// 日志 /// /// public delegate void OnNetLogHandler(string msg); #endregion #region 定义事件 /// /// 客户端连接数量变化事件 /// public event OnClientNumberChangeHandler OnClientNumberChange; /// /// 接收到客户端的数据事件 /// public event OnReceiveDataHandler OnReceive; /// /// 接收到客户端的断开连接 /// public event OnDisconnectHandler OnDisconnected; /// /// 网络库内部输出 /// public event OnNetLogHandler OnNetLog; #endregion #region 定义属性 /// /// 获取客户端列表 /// public List ClientList { get { return m_clients; } } #endregion /// /// 构造函数 /// /// 最大连接数 /// 缓存区大小 public TcpSaeaServer(int numConnections, int receiveBufferSize) { m_clientCount = 0; m_maxConnectNum = numConnections; m_revBufferSize = receiveBufferSize; // allocate buffers such that the maximum number of sockets can have one outstanding read and //write posted to the socket simultaneously m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToAlloc, receiveBufferSize); m_Receivepool = new SocketEventPool(numConnections); m_Sendpool = new SocketEventPool(numConnections); msg_pool = new TokenMsgPool(numConnections); m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections); } #region 连接操作 /// /// 初始化 /// public void Init() { // Allocates one large byte buffer which all I/O operations use a piece of. This gaurds // against memory fragmentation m_bufferManager.InitBuffer(); m_clients = new List(); // preallocate pool of SocketAsyncEventArgs objects SocketAsyncEventArgs readWriteEventArg; for (int i = 0; i < m_maxConnectNum; i++) { readWriteEventArg = new SocketAsyncEventArgs(); readWriteEventArg.Completed += new EventHandler(IO_Completed); readWriteEventArg.UserToken = new AsyncUserToken(); // assign a byte buffer from the buffer pool to the SocketAsyncEventArg object m_bufferManager.SetBuffer(readWriteEventArg); // add SocketAsyncEventArg to the pool m_Receivepool.Push(readWriteEventArg); } for (int i = 0; i < m_maxConnectNum; i++) { readWriteEventArg = new SocketAsyncEventArgs(); readWriteEventArg.Completed += new EventHandler(IO_Completed); readWriteEventArg.UserToken = new AsyncUserToken(); //发送是否需要如此设置 TODO m_bufferManager.SetBuffer(readWriteEventArg); m_Sendpool.Push(readWriteEventArg); } OutNetLog("初始化完毕"); } /// /// 启动服务 /// /// /// 是否端口重用 /// public bool Start(IPEndPoint localEndPoint, bool bReuseAddress = false) { try { ClearUserToken(); listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); if (bReuseAddress) { listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); } listenSocket.Bind(localEndPoint); // start the server with a listen backlog of 100 connections listenSocket.Listen(m_maxConnectNum); // post accepts on the listening socket StartAccept(null); OutNetLog("监听:" + listenSocket.LocalEndPoint.ToString()); _heartTimer = new System.Timers.Timer(); _heartTimer.Interval = TimerInterval; _heartTimer.Elapsed += CheckUpdatetimer_Elapsed; _heartTimer.AutoReset = true; _heartTimer.Enabled = true; OutNetLog("开启定时心跳包"); return true; } catch (Exception) { return false; } } /// /// 停止服务 /// public void Stop() { foreach (AsyncUserToken token in m_clients) { try { token.Socket.Shutdown(SocketShutdown.Both); } catch (Exception) { } } try { listenSocket.Shutdown(SocketShutdown.Both); } catch (Exception) { } listenSocket.Close(); int c_count = m_clients.Count; ClearUserToken(); if (OnClientNumberChange != null) OnClientNumberChange(-c_count, null); } public void CloseClient(AsyncUserToken token) { try { token.Socket.Shutdown(SocketShutdown.Both); } catch (Exception) { } } //关闭客户端连接 private void CloseClientSocket(SocketAsyncEventArgs e) { AsyncUserToken token = e.UserToken as AsyncUserToken; //调用关闭连接 OnDisconnected?.Invoke(token); RemoveUserToken(token); //如果有事件,则调用事件,发送客户端数量变化通知 OnClientNumberChange?.Invoke(-1, token); // close the socket associated with the client try { token.Socket.Shutdown(SocketShutdown.Send); } catch (Exception) { } token.Socket.Close(); // decrement the counter keeping track of the total number of clients connected to the server Interlocked.Decrement(ref m_clientCount); m_maxNumberAcceptedClients.Release(); // Free the SocketAsyncEventArg so they can be reused by another client ReleaseSocketAsyncEventArgs(e); } #endregion #region Token管理 void AddUserToken(AsyncUserToken userToken) { lock (_DictSocketAsyncUserToken) { m_clients.Add(userToken); _DictSocketAsyncUserToken.Add(userToken.Socket, userToken); } } void RemoveUserToken(AsyncUserToken userToken) { lock (_DictSocketAsyncUserToken) { m_clients.Remove(userToken); _DictSocketAsyncUserToken.Remove(userToken.Socket); } } void ClearUserToken() { lock (_DictSocketAsyncUserToken) { m_clients.Clear(); _DictSocketAsyncUserToken.Clear(); } } public AsyncUserToken GetAsyncUserTokenForSocket(Socket sk) { return _DictSocketAsyncUserToken.ContainsKey(sk) ? _DictSocketAsyncUserToken[sk] : null; } #endregion #region 监听客户端建立连接 // Begins an operation to accept a connection request from the client // // The context object to use when issuing // the accept operation on the server's listening socket public void StartAccept(SocketAsyncEventArgs acceptEventArg) { if (acceptEventArg == null) { acceptEventArg = new SocketAsyncEventArgs(); acceptEventArg.Completed += new EventHandler(AcceptEventArg_Completed); } else { // socket must be cleared since the context object is being reused acceptEventArg.AcceptSocket = null; } m_maxNumberAcceptedClients.WaitOne(); if (!listenSocket.AcceptAsync(acceptEventArg)) { ProcessAccept(acceptEventArg); } } // This method is the callback method associated with Socket.AcceptAsync // operations and is invoked when an accept operation is complete // void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) { ProcessAccept(e); } private void ProcessAccept(SocketAsyncEventArgs e) { try { Interlocked.Increment(ref m_clientCount); // Get the socket for the accepted client connection and put it into the //ReadEventArg object user token SocketAsyncEventArgs readEventArgs = m_Receivepool.Pop(); //TODO readEventArgs.UserToken这里的 UserToken 有可能是空 AsyncUserToken userToken; if (readEventArgs.UserToken == null) readEventArgs.UserToken = new AsyncUserToken(); userToken = (AsyncUserToken)readEventArgs.UserToken; userToken.Socket = e.AcceptSocket; userToken.ConnectTime = DateTime.Now; userToken.Remote = e.AcceptSocket.RemoteEndPoint; userToken.IPAddress = ((IPEndPoint)(e.AcceptSocket.RemoteEndPoint)).Address; userToken.RevIndex = MaxRevIndexNum; userToken.SendIndex = MaxSendIndexNum; AddUserToken(userToken); OnClientNumberChange?.Invoke(1, userToken); if (!e.AcceptSocket.ReceiveAsync(readEventArgs)) { ProcessReceive(readEventArgs); } } catch (Exception me) { //RuncomLib.Log.LogUtils.Info(me.Message + "\r\n" + me.StackTrace); } // Accept the next connection request if (e.SocketError == SocketError.OperationAborted) return; StartAccept(e); } #endregion #region 完成端口收发处理 // This method is invoked when an asynchronous receive operation completes. // If the remote host closed the connection, then the socket is closed. // If data was received then the data is echoed back to the client. // private void ProcessReceive(SocketAsyncEventArgs e) { try { // check if the remote host closed the connection AsyncUserToken token = (AsyncUserToken)e.UserToken; if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) { //读取数据 //byte[] data = new byte[e.BytesTransferred]; //Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred); //lock (token.Buffer) lock(token.memoryStream) { //token.Buffer.AddRange(data); token.memoryStream.Write(e.Buffer, e.Offset, e.BytesTransferred); } do { //如果包头不完整 //if (token.Buffer.Count < 4) if (token.memoryStream.Length < 4) break; //判断包的长度 //byte[] lenBytes = token.Buffer.GetRange(0, 4).ToArray(); //int packageLen = BitConverter.ToInt32(lenBytes, 0) - 4; //if (packageLen > token.Buffer.Count - 4) //{ //长度不够时,退出循环,让程序继续接收 // break; //} long FristBeginPos = token.memoryStream.Position; byte[] lenBytes = new byte[4]; token.memoryStream.Seek(0, SeekOrigin.Begin); token.memoryStream.Read(lenBytes,0,4); int packageLen = BitConverter.ToInt32(lenBytes, 0) - 4; if (packageLen > token.memoryStream.Length - 4) { //长度不够时,退出循环,让程序继续接收 break; } //包够长时,则提取出来,交给后面的程序去处理 //byte[] rev = token.Buffer.GetRange(4, packageLen).ToArray(); byte[] rev = new byte[packageLen]; token.memoryStream.Seek(4, SeekOrigin.Begin); token.memoryStream.Read(rev, 0, packageLen); ////从数据池中移除这组数据 //lock (token.Buffer) //{ // token.Buffer.RemoveRange(0, packageLen + 4); //} token.memoryStream.Seek(FristBeginPos, SeekOrigin.Begin); //从数据池中移除这组数据 lock (token.memoryStream) { int numberOfBytesToRemove = packageLen + 4; byte[] buf = token.memoryStream.GetBuffer(); Buffer.BlockCopy(buf, numberOfBytesToRemove, buf, 0, (int)token.memoryStream.Length - numberOfBytesToRemove); token.memoryStream.SetLength(token.memoryStream.Length - numberOfBytesToRemove); } DataCallBackReady(token, rev); //这里API处理完后,并没有返回结果,当然结果是要返回的,却不是在这里, 这里的代码只管接收. //若要返回结果,可在API处理中调用此类对象的SendMessage方法,统一打包发送.不要被微软的示例给迷惑了. //} while (token.Buffer.Count > 4); } while (token.memoryStream.Length > 4); //继续接收. 为什么要这么写,请看Socket.ReceiveAsync方法的说明 if (!token.Socket.ReceiveAsync(e)) { this.ProcessReceive(e); } } else { CloseClientSocket(e); } } catch (Exception xe) { //RuncomLib.Log.LogUtils.Info(xe.Message + "\r\n" + xe.StackTrace); } } private void ProcessSend(SocketAsyncEventArgs e) { if (e.SocketError == SocketError.Success) { //TODO } else { CloseClientSocket(e); return; } ReleaseSocketAsyncEventArgs(e); SendForMsgPool(); } void IO_Completed(object sender, SocketAsyncEventArgs e) { // determine which type of operation just completed and call the associated handler switch (e.LastOperation) { case SocketAsyncOperation.Receive: ProcessReceive(e); break; case SocketAsyncOperation.Send: ProcessSend(e); break; default: throw new ArgumentException("The last operation completed on the socket was not a receive or send"); } } #endregion /// /// 回收SocketAsyncEventArgs /// /// void ReleaseSocketAsyncEventArgs(SocketAsyncEventArgs saea) { saea.UserToken = null;//TODO saea.SetBuffer(null, 0, 0); switch (saea.LastOperation) { case SocketAsyncOperation.Receive: m_Receivepool.Push(saea); break; case SocketAsyncOperation.Send: m_Sendpool.Push(saea); break; } } int sendrun = 0; private void SendForMsgPool() { //if (flag_SendForMsgPool) return; try { if (sendrun < msg_pool.Count || msg_pool.Count < 1) return; sendrun++; while (msg_pool.Count > 0) { try { TokenWithMsg msg = msg_pool.Dequeue(); //OutNetLog("从信息池取出发送"); SendMessage(msg.token, msg.message); msg = null; } catch { OutNetLog("==============================================>"); } } sendrun--; OutNetLog("!!!!!!!!!!!!!!!!!!!!!!!!!!"); } catch (Exception ex) { OutNetLog(ex.ToString()); } } public void SendMessage(AsyncUserToken token, byte[] message,bool dontNeedHead = false) { if (token == null || token.Socket == null || !token.Socket.Connected) return; try { if (!dontNeedHead) { message = SendDataWithHead(message); } if (m_Sendpool.Count > 0) { SocketAsyncEventArgs myreadEventArgs = m_Sendpool.Pop(); myreadEventArgs.UserToken = token; myreadEventArgs.AcceptSocket = token.Socket; myreadEventArgs.SetBuffer(message, 0, message.Length); //将数据放置进去. //若不需要等待 if (!token.Socket.SendAsync(myreadEventArgs)) { m_Sendpool.Push(myreadEventArgs); } return; } else { //先压入队列,等待m_Sendpool回收 msg_pool.Enqueue(new TokenWithMsg() { token = token, message = message }); //OutNetLog("!!!!压入消息发送队列MSG_Pool"); return; } } catch (Exception e) { OutNetLog(e.ToString()); } } //拼接长度 private static byte[] SendDataWithHead(byte[] message) { MemoryStream memoryStream = new MemoryStream();//创建一个内存流 byte[] BagHead = BitConverter.GetBytes(message.Length + 4);//往字节数组中写入包头(包头自身的长度和消息体的长度)的长度 memoryStream.Write(BagHead, 0, BagHead.Length);//将包头写入内存流 memoryStream.Write(message, 0, message.Length);//将消息体写入内存流 byte[] HeadAndBody = memoryStream.ToArray();//将内存流中的数据写入字节数组 memoryStream.Close();//关闭内存 memoryStream.Dispose();//释放资源 return HeadAndBody; } #region private void OnCloseReady(AsyncUserToken token) { OnDisconnected?.Invoke(token); RemoveUserToken(token); //如果有事件,则调用事件,发送客户端数量变化通知 OnClientNumberChange?.Invoke(-1, token); // close the socket associated with the client try { token.Socket.Shutdown(SocketShutdown.Send); } catch (Exception) { } token.Socket.Close(); // decrement the counter keeping track of the total number of clients connected to the server Interlocked.Decrement(ref m_clientCount); m_maxNumberAcceptedClients.Release(); } /// /// 发送数据并计数 /// /// private void SendWithIndex(AsyncUserToken token, byte[] data) { try { //发送数据 SendMessage(token, data); token.SendIndex = MaxSendIndexNum; } catch { OnCloseReady(token); } } /// /// 对外暴露的发送消息 /// /// /// 序列化之后的数据 public void SendToSocket(Socket sk, int CMDID, int ERRCODE, byte[] data) { AsyncUserToken token = GetAsyncUserTokenForSocket(sk); HunterNet_S2C _s2cdata = new HunterNet_S2C(); _s2cdata.HunterNetCoreCmdID = CMDID; _s2cdata.HunterNetCoreData = ByteString.CopyFrom(data); _s2cdata.HunterNetCoreERRORCode = ERRCODE; byte[] _finaldata = Serizlize(_s2cdata); SendWithIndex(token, _finaldata); } private void DataCallBackReady(AsyncUserToken sk, byte[] data) { //增加接收计数 sk.RevIndex = MaxRevIndexNum; if (data.Length == 1 && data[0] == 0x00)//心跳包 { //OutNetLog("收到心跳包"); //无处理 } else { try { HunterNet_C2S _s2c = DeSerizlize(data); //将数据包交给后台处理,这里你也可以新开个线程来处理.加快速度. OnReceive?.Invoke(sk, (int)_s2c.HunterNetCoreCmdID, _s2c.HunterNetCoreData.ToArray()); //DataCallBack(sk, (int)_s2c.HunterNetCoreCmdID, _s2c.HunterNetCoreData.ToArray()); } catch (Exception ex) { OutNetLog("数据解析错误"); } } } private void OutNetLog(string msg) { OnNetLog?.Invoke(msg); } #endregion #region 心跳包 /// /// 发送心跳包 /// /// /// private void SendHeartbeat(AsyncUserToken token) { if (token == null || token.Socket == null || !token.Socket.Connected) return; try { //OutNetLog(DateTime.Now.ToString() + "发送心跳包"); token.SendIndex = MaxSendIndexNum; SendMessage(token, HeartbeatData, true); } catch (Exception e) { OnCloseReady(token); } } /// /// 心跳包时钟事件 /// /// /// private void CheckUpdatetimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { for (int i = 0; i < m_clients.Count(); i++) { //接收服务器数据计数 m_clients[i].RevIndex--; if (m_clients[i].RevIndex <= 0) { //判定掉线 OnCloseReady(m_clients[i]); return; } //发送计数 m_clients[i].SendIndex--; if (m_clients[i].SendIndex <= 0)//需要发送心跳包了 { //重置倒计时计数 m_clients[i].SendIndex = MaxSendIndexNum; SendHeartbeat(m_clients[i]); } } } #endregion public static byte[] Serizlize(IMessage msg) { return msg.ToByteArray(); } public static T DeSerizlize(byte[] bytes) { var msgType = typeof(T); object msg = Activator.CreateInstance(msgType); ((IMessage)msg).MergeFrom(bytes); return (T)msg; } } }