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.
 
 
 
 
 
 

389 lines
13 KiB

using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography;
using System.Net.Security;
using System.Net.WebSockets;
using System.Runtime.InteropServices;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
namespace SogClient
{
public class WebSession
{
const int BUFFER_LENGTH_MAX = 1024 * 1024 * 100;
private ClientSessionSettings m_clientSettings;
private ClientWebSocket m_webSocket;
internal DateTime LastDataTime;
private byte[] m_recvBuffer = new byte[1024 * 64];
private int m_alreadyRecvLength = 0;
private int m_messageLength = 0;
private Queue<MessageData> m_recvBigDataMessageQueue = new Queue<MessageData>();
private Queue<MessageData> m_recvMessageQueue = new Queue<MessageData>();
private Queue<MessageData> m_sendMessageQueue = new Queue<MessageData>();
private int m_dataLengthInSendQueue = 0;
private byte[] m_sendBuffer = new byte[1024 * 64];
private int m_sendDataLeft = 0;
private object m_sendLock = new object();
private bool m_sendPending = false;
private byte[] m_headerBytes;
//消息头的长度
private int m_messageHeaderLength;
private IProtoPacker m_protoPacker;
private string m_uri;
public long SessionID { get; private set; }
public bool IsConnected { get; private set; }
public bool IsConnecting { get; private set; }
public bool IsError { get; private set; }
public bool IsClosed { get; private set; }
public bool IsClosing { get; private set; }
public IPEndPoint RemoteEndPoint => m_clientSettings.RemoteEndPoint;
/// <summary>
/// </summary>
/// <param name="webSocketClient">Socket.</param>
public WebSession(ClientSessionSettings clientSettings)
{
m_clientSettings = clientSettings;
string uri = $"ws://{clientSettings.RemoteEndPoint.Address}:{clientSettings.RemoteEndPoint.Port}/";
m_uri = uri;
Console.WriteLine($"Connect:{uri}");
ClientWebSocket webSocketClient = new ClientWebSocket();
webSocketClient.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
m_protoPacker = ProtoPackerFactory.Instance.GetProtoPacker();
m_messageHeaderLength = m_protoPacker.GetHeaderLength();
m_headerBytes = new byte[m_protoPacker.GetHeaderLength()];
this.m_webSocket = webSocketClient;
}
public void ResetSessionID(long sesssionID)
{
SessionID = sesssionID;
}
public void Dispose()
{
Console.WriteLine($"Connect:{SessionID} Dispose");
if (m_webSocket != null)
{
m_webSocket.Dispose();
m_webSocket = null;
}
m_clientSettings = null;
}
private void OnOpen()
{
IsConnected = true;
}
private void OnError(Exception e)
{
TraceLog.Debug($"WebSocketClientSession OnError {e}");
IsError = true;
}
private void OnClose()
{
TraceLog.Debug($"WebSocketClientSession OnClose");
IsClosed = true;
}
public async Task Close()
{
// 直接丢弃所有数据
m_alreadyRecvLength = 0;
if (m_webSocket == null)
{
return;
}
if (IsClosed || IsClosing)
{
return;
}
IsConnected = false;
IsClosing = true;
try
{
await m_webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
OnClose();
}
catch (Exception e)
{
TraceLog.Write("WebSocket netsession Close err, message {0} ", e);
}
finally
{
TraceLog.Write("WebSocket netsession Close message {0} ");
m_webSocket = null;
}
}
public async void Connect()
{
if (m_webSocket == null)
{
return;
}
if (IsConnected || IsConnecting)
{
TraceLog.WriteError($"repeated connect websocket");
return;
}
TraceLog.Debug($"WebSocket connect {m_uri}");
IsConnecting = true;
try
{
await m_webSocket.ConnectAsync(new Uri(m_uri), CancellationToken.None);
OnOpen();
await Receive();
}
catch (Exception ex)
{
OnError(ex);
TraceLog.Error($"WebSocketSession Connected err! message:{ex}");
}
finally {
if (m_webSocket != null && m_webSocket.State == WebSocketState.Open)
{
await m_webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
OnClose();
}
}
}
private async Task Receive()
{
while (true)
{
var result = await m_webSocket.ReceiveAsync(new ArraySegment<byte>(m_recvBuffer, m_alreadyRecvLength, m_recvBuffer.Length-m_alreadyRecvLength), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Binary)
{
m_alreadyRecvLength += result.Count;
TryReadMessageFromBuffer();
}
else if (result.MessageType == WebSocketMessageType.Close)
{
OnClose();
break;
}
}
}
public void TryReadMessageFromBuffer()
{
if (m_alreadyRecvLength < m_messageHeaderLength)
{
return;
}
int iBufferLeftLength = m_alreadyRecvLength;
int iBufferStartPos = 0;
TraceLog.Debug($"WebSocket TryReadMessageFromBuffer111 :{m_recvMessageQueue.Count} {m_alreadyRecvLength}");
while (iBufferLeftLength >= m_messageHeaderLength)
{
MessageHeader header;
header.Type = 0;
header.Length = 0;
header.ObjectID = 0;
header.ServerID = 0;
m_messageLength = m_protoPacker.UnPackHeader(m_recvBuffer, iBufferStartPos, iBufferLeftLength, ref header);
m_messageLength = header.Length;
//判断是否合法
if (m_messageLength < 0 || m_messageLength > 64 * 1024 - 24)
{
//主动断线
TraceLog.WriteError("websocket recv a invalid length {0} packet,close session {1}, remote {2}",
m_messageLength, SessionID, RemoteEndPoint);
Close();
return;
}
//有完整的消息了
if (m_messageLength + m_messageHeaderLength <= iBufferLeftLength)
{
if (m_messageLength >= 0 && header.Type > 0)
{
byte[] data = new byte[m_messageLength];
Buffer.BlockCopy(m_recvBuffer, iBufferStartPos + m_messageHeaderLength, data, 0, m_messageLength);
MessageData message = new MessageData();
message.Data = data;
message.Header = header;
if (header.Type == (int)SpecialMessageType.BigMessageStart
|| header.Type == (int)SpecialMessageType.BigMessageTrans)
{
TraceLog.Debug($"WebSocket TryReadMessageFromBuffer222 bigmessage:{m_messageLength} {m_messageHeaderLength} {iBufferLeftLength}");
m_recvBigDataMessageQueue.Enqueue(message);
BigDataMessage.TryGetFullBigDataMessage(m_recvBigDataMessageQueue, m_recvMessageQueue);
}
else
{
m_recvMessageQueue.Enqueue(message);
}
}
iBufferStartPos += m_messageLength + m_messageHeaderLength;
iBufferLeftLength -= m_messageLength + m_messageHeaderLength;
}
else
{
break;
}
}
if (iBufferLeftLength > 0 && iBufferStartPos > 0)
{
//剩下的内容向前移动
Buffer.BlockCopy(m_recvBuffer, iBufferStartPos, m_recvBuffer, 0, iBufferLeftLength);
}
m_alreadyRecvLength = iBufferLeftLength;
TraceLog.Debug($"WebSocket TryReadMessageFromBuffer333 {m_recvMessageQueue.Count} {m_alreadyRecvLength}");
}
public bool IsHaveDataRead()
{
return m_recvMessageQueue.Count > 0;
}
public Queue<MessageData> GetReceivedDataQueue()
{
return m_recvMessageQueue;
}
public bool IsFull()
{
return m_dataLengthInSendQueue >= BUFFER_LENGTH_MAX;
}
public void SendMessageToQueue(MessageData message)
{
TraceLog.Debug($"WebSocket SendMessageToQueue 11111 {m_sendMessageQueue.Count} {message.Header.Type}");
if (IsFull())
{
TraceLog.WriteError("websocket send buff full drop msg type {0}, length {1} remote {2}",
message.Header.Type,
message.Header.Length,
RemoteEndPoint.ToString());
return;
}
m_sendMessageQueue.Enqueue(message);
m_dataLengthInSendQueue += message.Header.Length;
}
public async Task RealSendAsync()
{
if (m_webSocket == null)
{
return;
}
if (!IsConnected)
{
return;
}
if (m_sendDataLeft <= 0)
{
return;
}
if (m_webSocket.State != WebSocketState.Open)
{
return;
}
lock(m_sendLock){
if (m_sendPending)
{
return;
}
m_sendPending = true;
}
try
{
TraceLog.Debug($"WebSocket RealSendAsync 111111 {m_sendDataLeft} {m_sendMessageQueue.Count}");
await m_webSocket.SendAsync(new ArraySegment<byte>(m_sendBuffer, 0, m_sendDataLeft), WebSocketMessageType.Binary, true, CancellationToken.None);
OnSendCallBack();
TraceLog.Debug($"WebSocket RealSendAsync 22222 {m_sendDataLeft} {m_sendMessageQueue.Count}");
}
catch (Exception ex)
{
TraceLog.WriteError($"WebSocket RealSendAsync err SessionID:{SessionID} errmessage:{ex}");
}
}
private void OnSendCallBack()
{
lock (m_sendLock)
{
if (m_sendPending == false)
{
return;
}
m_sendPending = false;
m_sendDataLeft = 0;
}
}
public bool IsHaveDataSend()
{
if (m_webSocket == null)
{
return false;
}
if (m_sendMessageQueue.Count > 0)
{
return true;
}
return m_sendDataLeft > 0;
}
public void TryCopySendBuffer()
{
//只有buffer的数据全部发送完毕后才行
if (m_sendDataLeft > 0)
{
RealSendAsync();
return;
}
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, m_messageHeaderLength);
m_sendDataLeft += m_messageHeaderLength;
Buffer.BlockCopy(message.Data, 0, m_sendBuffer, m_sendDataLeft, message.Data.Length);
m_sendDataLeft += message.Data.Length;
//查看一下,下个消息长度能否copy成功,不行就跳出
if (m_sendMessageQueue.Count > 0)
{
message = m_sendMessageQueue.Peek();
if (m_sendDataLeft + message.Data.Length + m_messageHeaderLength >= m_sendBuffer.Length)
{
break;
}
}
}
}
}
}