You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
485 lines
16 KiB
485 lines
16 KiB
/*
|
|
Sog 游戏基础库
|
|
2016 by zouwei
|
|
*/
|
|
|
|
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
using Sog.IO;
|
|
|
|
|
|
namespace Sog
|
|
{
|
|
/// <summary>
|
|
/// NetSession
|
|
/// </summary>
|
|
public abstract class NetSession
|
|
{
|
|
const int BUFFER_LENGTH_MAX = 1024 * 1024 * 100;
|
|
|
|
|
|
public Socket WorkSocket => m_socket;
|
|
public EndPoint RemoteEndPoint => m_remoteEndPoint;
|
|
|
|
public long SessionID { get; private set; }
|
|
public bool IsSocketClosed { get; private set; }
|
|
|
|
|
|
protected object m_socketLocker = new object();
|
|
protected Socket m_socket;
|
|
protected EndPoint m_remoteEndPoint;
|
|
|
|
|
|
internal long LastRecvDataTimeSecond;
|
|
internal long LastSendDataTimeSecond;
|
|
protected int m_noWriteableCheckCount;
|
|
|
|
|
|
protected Queue<MessageData> m_recvMessageQueue = new Queue<MessageData>();
|
|
protected object m_recvMessageQueueLocker = new object();
|
|
protected Queue<MessageData> m_recvBigDataMessageQueue = new Queue<MessageData>();
|
|
|
|
protected Queue<MessageData> m_sendMessageQueue = new Queue<MessageData>();
|
|
protected int m_dataLengthInSendQueue;
|
|
|
|
// 异步模式先发送到Async队列
|
|
protected Queue<MessageData> m_sendMessageAsyncQueue = new Queue<MessageData>();
|
|
protected object m_sendMessageAsyncQueueLocker = new object();
|
|
|
|
|
|
// protected bool m_recvAsyncPending;
|
|
// protected bool m_sendAsyncPending;
|
|
// protected object m_recvAsyncLocker;
|
|
// protected object m_sendAsyncLocker;
|
|
// protected SocketAsyncEventArgs m_recvAsyncEventArgs;
|
|
// protected SocketAsyncEventArgs m_sendAsyncEventArgs;
|
|
|
|
|
|
protected byte[] m_sendBuffer;
|
|
protected int m_sendDataLeft;
|
|
protected int m_sendDataPos;
|
|
|
|
|
|
protected byte[] m_recvBuffer;
|
|
protected int m_alreadyRecvLength;
|
|
|
|
|
|
//长度,消息头信息
|
|
protected byte[] m_headerBytes;
|
|
protected byte[] m_zeroLengthBytes;
|
|
|
|
//消息头的长度
|
|
protected int m_minHeaderLength;
|
|
protected ProtoCSStructPacker m_protoPacker;
|
|
|
|
public bool WriteSendRecvLog { get; set; }
|
|
|
|
public long TotalRecvLength;
|
|
public long TotalSendLength;
|
|
|
|
|
|
public abstract void WriteWriteableSocket();
|
|
|
|
public abstract void RecvReadableSocket();
|
|
|
|
public abstract void StartSendAsync();
|
|
|
|
public abstract void StartRecvAsync();
|
|
|
|
protected abstract void OnBindSocket();
|
|
|
|
protected abstract void CloseSocket();
|
|
|
|
public abstract void CheckSocketAndSendKeepAlive();
|
|
|
|
/// <summary>
|
|
/// 异步模式下接收到数据事件
|
|
/// </summary>
|
|
public event NetEventHandler AsyncModeDataReceived;
|
|
private void OnAsyncModeDataReceived(SessionEventArgs e)
|
|
{
|
|
TraceLog.TraceDetail("NetSession.OnAsyncModeDataReceived remote {0} session {1} length {2}"
|
|
, e.Session.RemoteEndPoint, e.Session.SessionID, e.Message.Header.Length);
|
|
|
|
if (AsyncModeDataReceived != null)
|
|
{
|
|
AsyncModeDataReceived(null, e);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="socket">Socket.</param>
|
|
public NetSession(Socket socket)
|
|
{
|
|
m_protoPacker = ProtoPackerFactory.Instance.GetProtoCSStructPacker();
|
|
m_minHeaderLength = m_protoPacker.GetHeaderLength();
|
|
|
|
m_headerBytes = new byte[m_protoPacker.GetFullHeaderLength()];
|
|
m_zeroLengthBytes = new byte[8];
|
|
|
|
SessionID = SessionIDUtil.GenID();
|
|
m_socket = socket;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 重新设置sessionid,gateserver会用到
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
public void ResetSessionID(long id)
|
|
{
|
|
SessionID = id;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置读写缓冲大小,系统缺省是65k
|
|
/// </summary>
|
|
/// <param name="iNewBufferSize"></param>
|
|
public void SetSocketBufferSize(int iNewBufferSize)
|
|
{
|
|
//设置bufersize
|
|
int iOldBufferSize = m_socket.SendBufferSize;
|
|
|
|
m_socket.SendBufferSize = iNewBufferSize;
|
|
m_socket.ReceiveBufferSize = iNewBufferSize;
|
|
|
|
TraceLog.Debug("NetSession.SetSocketBufferSize socket set new buffersize {0}, old size {1}", iNewBufferSize, iOldBufferSize);
|
|
}
|
|
|
|
// bind只会发生在连接成功建立后
|
|
public void Bind(Socket socket)
|
|
{
|
|
if (socket == null)
|
|
{
|
|
TraceLog.Error("NetSession.Bind session {0} socket is null", SessionID);
|
|
return;
|
|
}
|
|
|
|
if (m_socket != null && m_socket != socket)
|
|
{
|
|
TraceLog.Error("NetSession.Bind session {0} already bind socket, close first", SessionID);
|
|
Close();
|
|
}
|
|
|
|
lock (m_socketLocker)
|
|
{
|
|
m_socket = socket;
|
|
IsSocketClosed = false;
|
|
|
|
OnBindSocket();
|
|
|
|
// 如果有正在发送的数据,直接丢弃
|
|
m_sendDataLeft = 0;
|
|
m_sendDataPos = 0;
|
|
}
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
TraceLog.Trace("NetSession.Close session {0} remote {1}", SessionID, RemoteEndPoint);
|
|
|
|
if (IsSocketClosed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lock (m_socketLocker)
|
|
{
|
|
if (IsSocketClosed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CloseSocket();
|
|
}
|
|
|
|
// todo 关闭前要不要尝试再解析一次
|
|
// TryReadMessageFromBuffer(false, false);
|
|
|
|
// 如果有正在接收的数据,直接丢弃
|
|
m_alreadyRecvLength = 0;
|
|
|
|
//如果有正在发送的数据,直接丢弃
|
|
m_sendDataLeft = 0;
|
|
m_sendDataPos = 0;
|
|
|
|
IsSocketClosed = true;
|
|
}
|
|
|
|
protected void TryReadMessageFromBuffer(bool threadSafe, bool raiseAsyncModeEvent)
|
|
{
|
|
if(m_alreadyRecvLength < m_minHeaderLength)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int iBufferLeftLength = m_alreadyRecvLength;
|
|
int iBufferStartPos = 0;
|
|
|
|
while (iBufferLeftLength >= m_minHeaderLength)
|
|
{
|
|
MessageHeader header = new MessageHeader();
|
|
int headLen = m_protoPacker.UnPackHeader(m_recvBuffer, iBufferStartPos, iBufferLeftLength, ref header);
|
|
|
|
//没有完整消息头
|
|
if (headLen == 0)
|
|
{
|
|
TraceLog.Trace("NetSession.TryReadMessageFromBuffer no enougth head, BufferLeftLength {0} BufferStartPos {1}"
|
|
, iBufferLeftLength, iBufferStartPos);
|
|
break;
|
|
}
|
|
|
|
int dataLen = header.Length;
|
|
|
|
//判断是否合法
|
|
if(dataLen < 0 || dataLen > 64*1024 - 24)
|
|
{
|
|
//主动断线
|
|
TraceLog.Error("NetSession.TryReadMessageFromBuffer recv a invalid length {0} packet, close session {1}, remote {2}",
|
|
dataLen, SessionID, RemoteEndPoint);
|
|
|
|
Close();
|
|
return;
|
|
}
|
|
|
|
|
|
//有完整的消息了
|
|
if (dataLen + headLen <= iBufferLeftLength)
|
|
{
|
|
//注意消息长度可以为0,就是只有消息头的消息,没有实际内容
|
|
//类型为0,长度为0的消息不处理
|
|
if (dataLen >= 0 && header.Type > 0)
|
|
{
|
|
MessageData message = new MessageData();
|
|
message.MallocData(dataLen);
|
|
Buffer.BlockCopy(m_recvBuffer, iBufferStartPos + headLen, message.Buffer.Data, 0, dataLen);
|
|
|
|
message.Header = header;
|
|
|
|
if(header.Type == (int)SpecialMessageType.BigMessageStart
|
|
|| header.Type == (int)SpecialMessageType.BigMessageTrans)
|
|
{
|
|
m_recvBigDataMessageQueue.Enqueue(message);
|
|
|
|
if(threadSafe)
|
|
{
|
|
lock(m_recvMessageQueueLocker)
|
|
{
|
|
BigDataMessage.TryGetFullBigDataMessage(m_recvBigDataMessageQueue, m_recvMessageQueue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BigDataMessage.TryGetFullBigDataMessage(m_recvBigDataMessageQueue, m_recvMessageQueue);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if(threadSafe)
|
|
{
|
|
lock(m_recvMessageQueueLocker)
|
|
{
|
|
m_recvMessageQueue.Enqueue(message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_recvMessageQueue.Enqueue(message);
|
|
}
|
|
|
|
if (WriteSendRecvLog)
|
|
{
|
|
TraceLog.TraceDetail("NetSession.TryReadMessageFromBuffer remote {0} session {1} recv type {2} length {3}"
|
|
, RemoteEndPoint, SessionID, header.Type, header.Length);
|
|
|
|
//TraceLog.Trace("data content {0}", StringUtils.ToHexString(data));
|
|
}
|
|
}
|
|
}
|
|
|
|
iBufferStartPos += dataLen + headLen;
|
|
iBufferLeftLength -= dataLen + headLen;
|
|
}
|
|
else
|
|
{
|
|
TraceLog.TraceDetail("NetSession.TryReadMessageFromBuffer no full message, BufferLeftLength {0} BufferStartPos {1}"
|
|
, iBufferLeftLength, iBufferStartPos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(iBufferLeftLength > 0 && iBufferStartPos > 0)
|
|
{
|
|
//剩下的内容向前移动
|
|
Buffer.BlockCopy(m_recvBuffer, iBufferStartPos, m_recvBuffer, 0, iBufferLeftLength);
|
|
}
|
|
|
|
m_alreadyRecvLength = iBufferLeftLength;
|
|
|
|
if(raiseAsyncModeEvent && m_recvMessageQueue.Count > 0)
|
|
{
|
|
while(m_recvMessageQueue.Count > 0)
|
|
{
|
|
MessageData message = m_recvMessageQueue.Dequeue();
|
|
|
|
SessionEventArgs e = new SessionEventArgs();
|
|
e.Message = message;
|
|
e.Session = this;
|
|
|
|
OnAsyncModeDataReceived(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Queue<MessageData> GetReceivedDataQueue()
|
|
{
|
|
return m_recvMessageQueue;
|
|
}
|
|
|
|
protected void CopySendBuffer()
|
|
{
|
|
//只有buffer的数据全部发送完毕后才行
|
|
if (m_sendDataLeft > 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(m_sendMessageAsyncQueue.Count > 0)
|
|
{
|
|
lock(m_sendMessageAsyncQueueLocker)
|
|
{
|
|
while(m_sendMessageAsyncQueue.Count > 0)
|
|
{
|
|
MessageData message = m_sendMessageAsyncQueue.Dequeue();
|
|
//调用这个,大消息需要拆分下
|
|
//线程安全,也不会有顺序问题
|
|
SendMessageToQueue(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
while (m_sendMessageQueue.Count > 0)
|
|
{
|
|
MessageData message = m_sendMessageQueue.Dequeue();
|
|
m_dataLengthInSendQueue -= message.Header.Length;
|
|
|
|
int headerLength = m_protoPacker.PackHeader(message.Header, m_headerBytes);
|
|
|
|
Buffer.BlockCopy(m_headerBytes, 0, m_sendBuffer, m_sendDataLeft, headerLength);
|
|
m_sendDataLeft += headerLength;
|
|
|
|
Buffer.BlockCopy(message.Buffer.Data, 0, m_sendBuffer, m_sendDataLeft, message.Buffer.Length);
|
|
m_sendDataLeft += message.Buffer.Length;
|
|
|
|
message.FreeData();
|
|
|
|
//查看一下,下个消息长度能否copy成功,不行就跳出
|
|
if (m_sendMessageQueue.Count > 0 )
|
|
{
|
|
message = m_sendMessageQueue.Peek();
|
|
if(m_sendDataLeft + message.Buffer.Length + 100 >= m_sendBuffer.Length)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
public bool IsFull()
|
|
{
|
|
return m_dataLengthInSendQueue >= BUFFER_LENGTH_MAX;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 是否有数据需要发送
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool IsHaveDataNeedSend()
|
|
{
|
|
if (m_socket == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (m_sendMessageQueue.Count > 0 || m_sendMessageAsyncQueue.Count > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return m_sendDataLeft > 0;
|
|
}
|
|
|
|
public void SendMessageToAsyncQueue(MessageData message)
|
|
{
|
|
lock(m_sendMessageAsyncQueueLocker)
|
|
{
|
|
m_sendMessageAsyncQueue.Enqueue(message);
|
|
}
|
|
}
|
|
|
|
public void SendMessageToQueue(MessageData message)
|
|
{
|
|
if (IsFull())
|
|
{
|
|
TraceLog.Error("send buff full drop msg type {0}, length {1} remote {2}",
|
|
message.Header.Type,
|
|
message.Header.Length,
|
|
m_remoteEndPoint.ToString());
|
|
|
|
return;
|
|
}
|
|
|
|
//大消息需要拆开
|
|
if(message.Header.Length > BigDataMessage.MessageLenghMinNeedSplit)
|
|
{
|
|
List<MessageData> splits = BigDataMessage.SplitMessage(message);
|
|
foreach(var one in splits)
|
|
{
|
|
m_sendMessageQueue.Enqueue(one);
|
|
m_dataLengthInSendQueue += one.Header.Length;
|
|
}
|
|
message.FreeData();
|
|
}
|
|
else
|
|
{
|
|
m_sendMessageQueue.Enqueue(message);
|
|
m_dataLengthInSendQueue += message.Header.Length;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 逻辑层不要调用,底层用
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
internal void DirectSend(MessageData message)
|
|
{
|
|
m_sendDataLeft = 0;
|
|
m_sendDataPos = 0;
|
|
|
|
int headerLength = m_protoPacker.PackHeader(message.Header, m_headerBytes);
|
|
|
|
Buffer.BlockCopy(m_headerBytes, 0, m_sendBuffer, m_sendDataLeft, headerLength);
|
|
m_sendDataLeft += headerLength;
|
|
if (message.Buffer.Data != null)
|
|
{
|
|
Buffer.BlockCopy(message.Buffer.Data, 0, m_sendBuffer, m_sendDataLeft, message.Buffer.Length);
|
|
m_sendDataLeft += message.Buffer.Length;
|
|
}
|
|
}
|
|
|
|
public void StartSendAsyncMessage(MessageData message)
|
|
{
|
|
SendMessageToAsyncQueue(message);
|
|
StartSendAsync();
|
|
}
|
|
}
|
|
}
|