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
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|