mirror of
https://github.com/snltty/linker.git
synced 2025-10-27 19:10:27 +08:00
337 lines
11 KiB
C#
337 lines
11 KiB
C#
using cmonitor.config;
|
|
using common.libs;
|
|
using common.libs.extends;
|
|
using System;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
|
|
namespace cmonitor.server
|
|
{
|
|
public sealed class TcpServer
|
|
{
|
|
private int bufferSize = 8 * 1024;
|
|
private Socket socket;
|
|
private UdpClient socketUdp;
|
|
private CancellationTokenSource cancellationTokenSource;
|
|
private Memory<byte> relayFLagCData = Encoding.UTF8.GetBytes("snltty.relay");
|
|
|
|
public Func<IConnection, Task> OnPacket { get; set; } = async (connection) => { await Task.CompletedTask; };
|
|
public Action<int> OnDisconnected { get; set; }
|
|
|
|
private readonly Config config;
|
|
public TcpServer(Config config)
|
|
{
|
|
this.config = config;
|
|
}
|
|
public void Start()
|
|
{
|
|
if (socket == null)
|
|
{
|
|
cancellationTokenSource = new CancellationTokenSource();
|
|
socket = BindAccept();
|
|
}
|
|
}
|
|
|
|
private Socket BindAccept()
|
|
{
|
|
IPEndPoint localEndPoint = new IPEndPoint(NetworkHelper.IPv6Support ? IPAddress.IPv6Any : IPAddress.Any, config.Data.Server.ServicePort);
|
|
Socket socket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
socket.IPv6Only(localEndPoint.AddressFamily, false);
|
|
socket.ReuseBind(localEndPoint);
|
|
socket.Listen(int.MaxValue);
|
|
|
|
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs
|
|
{
|
|
UserToken = new AsyncUserToken
|
|
{
|
|
Socket = socket
|
|
},
|
|
SocketFlags = SocketFlags.None,
|
|
};
|
|
acceptEventArg.Completed += IO_Completed;
|
|
StartAccept(acceptEventArg);
|
|
|
|
socketUdp = new UdpClient(new IPEndPoint(IPAddress.Any, config.Data.Server.ServicePort));
|
|
//socketUdp.JoinMulticastGroup(config.BroadcastIP);
|
|
socketUdp.Client.EnableBroadcast = true;
|
|
socketUdp.Client.WindowsUdpBug();
|
|
IAsyncResult result = socketUdp.BeginReceive(ReceiveCallbackUdp, null);
|
|
|
|
|
|
return socket;
|
|
|
|
}
|
|
private async void ReceiveCallbackUdp(IAsyncResult result)
|
|
{
|
|
try
|
|
{
|
|
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
|
|
byte[] bytes = socketUdp.EndReceive(result, ref endPoint);
|
|
try
|
|
{
|
|
await socketUdp.SendAsync(endPoint.ToString().ToBytes(), endPoint);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
|
|
result = socketUdp.BeginReceive(ReceiveCallbackUdp, null);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
private void StartAccept(SocketAsyncEventArgs acceptEventArg)
|
|
{
|
|
acceptEventArg.AcceptSocket = null;
|
|
AsyncUserToken token = (AsyncUserToken)acceptEventArg.UserToken;
|
|
try
|
|
{
|
|
if (token.Socket.AcceptAsync(acceptEventArg) == false)
|
|
{
|
|
ProcessAccept(acceptEventArg);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
token.Clear();
|
|
}
|
|
}
|
|
private void IO_Completed(object sender, SocketAsyncEventArgs e)
|
|
{
|
|
switch (e.LastOperation)
|
|
{
|
|
case SocketAsyncOperation.Accept:
|
|
ProcessAccept(e);
|
|
break;
|
|
case SocketAsyncOperation.Receive:
|
|
ProcessReceive(e);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
private void ProcessAccept(SocketAsyncEventArgs e)
|
|
{
|
|
if (e.AcceptSocket != null)
|
|
{
|
|
BindReceive(e.AcceptSocket);
|
|
StartAccept(e);
|
|
}
|
|
}
|
|
|
|
public IConnection BindReceive(Socket socket)
|
|
{
|
|
try
|
|
{
|
|
if (socket == null || socket.RemoteEndPoint == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
socket.KeepAlive();
|
|
AsyncUserToken userToken = new AsyncUserToken
|
|
{
|
|
Socket = socket,
|
|
Connection = CreateConnection(socket)
|
|
};
|
|
|
|
SocketAsyncEventArgs saea = new SocketAsyncEventArgs
|
|
{
|
|
UserToken = userToken,
|
|
SocketFlags = SocketFlags.None,
|
|
};
|
|
userToken.PoolBuffer = new byte[bufferSize];
|
|
saea.SetBuffer(userToken.PoolBuffer, 0, bufferSize);
|
|
saea.Completed += IO_Completed;
|
|
if (socket.ReceiveAsync(saea) == false)
|
|
{
|
|
ProcessReceive(saea);
|
|
}
|
|
return userToken.Connection;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
|
Logger.Instance.Error(ex);
|
|
}
|
|
return null;
|
|
}
|
|
private async void ProcessReceive(SocketAsyncEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
AsyncUserToken token = (AsyncUserToken)e.UserToken;
|
|
|
|
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
|
|
{
|
|
int offset = e.Offset;
|
|
int length = e.BytesTransferred;
|
|
|
|
bool res = await ReadPacket(token, e.Buffer, offset, length);
|
|
if (res == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (token.Socket.Available > 0)
|
|
{
|
|
while (token.Socket.Available > 0)
|
|
{
|
|
length = token.Socket.Receive(e.Buffer);
|
|
if (length > 0)
|
|
{
|
|
res = await ReadPacket(token, e.Buffer, 0, length);
|
|
if (res == false)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CloseClientSocket(e);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (token.Socket.Connected == false)
|
|
{
|
|
CloseClientSocket(e);
|
|
return;
|
|
}
|
|
if (token.Socket.ReceiveAsync(e) == false)
|
|
{
|
|
ProcessReceive(e);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CloseClientSocket(e);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
|
Logger.Instance.Error(ex);
|
|
|
|
CloseClientSocket(e);
|
|
}
|
|
}
|
|
private async Task<bool> ReadPacket(AsyncUserToken token, byte[] data, int offset, int length)
|
|
{
|
|
if (token.Connection.TcpTargetSocket != null)
|
|
{
|
|
if (token.DataBuffer.Size > 0)
|
|
{
|
|
await token.Connection.TcpTargetSocket.SendAsync(token.DataBuffer.Data.Slice(0, token.DataBuffer.Size), SocketFlags.None);
|
|
token.DataBuffer.Clear();
|
|
}
|
|
await token.Connection.TcpTargetSocket.SendAsync(data.AsMemory(offset, length), SocketFlags.None);
|
|
return true;
|
|
}
|
|
else if (length == relayFLagCData.Length && data.AsSpan(offset, relayFLagCData.Length).SequenceEqual(relayFLagCData.Span))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
//是一个完整的包
|
|
if (token.DataBuffer.Size == 0 && length > 4)
|
|
{
|
|
Memory<byte> memory = data.AsMemory(offset, length);
|
|
int packageLen = memory.Span.ToInt32();
|
|
if (packageLen == length - 4)
|
|
{
|
|
token.Connection.ReceiveData = data.AsMemory(offset, packageLen + 4);
|
|
await OnPacket(token.Connection);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//不是完整包
|
|
token.DataBuffer.AddRange(data, offset, length);
|
|
do
|
|
{
|
|
int packageLen = token.DataBuffer.Data.Span.ToInt32();
|
|
if (packageLen > token.DataBuffer.Size - 4)
|
|
{
|
|
break;
|
|
}
|
|
token.Connection.ReceiveData = token.DataBuffer.Data.Slice(0, packageLen + 4);
|
|
await OnPacket(token.Connection);
|
|
|
|
token.DataBuffer.RemoveRange(0, packageLen + 4);
|
|
} while (token.DataBuffer.Size > 4);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void CloseClientSocket(SocketAsyncEventArgs e)
|
|
{
|
|
AsyncUserToken token = e.UserToken as AsyncUserToken;
|
|
if (token.Socket != null)
|
|
{
|
|
token.Clear();
|
|
e.Dispose();
|
|
}
|
|
if (token.Socket != null)
|
|
OnDisconnected?.Invoke(token.Socket.GetHashCode());
|
|
}
|
|
|
|
public IConnection CreateConnection(Socket socket)
|
|
{
|
|
return new TcpConnection(socket)
|
|
{
|
|
ReceiveRequestWrap = new MessageRequestWrap(),
|
|
ReceiveResponseWrap = new MessageResponseWrap()
|
|
};
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
cancellationTokenSource?.Cancel();
|
|
socket?.SafeClose();
|
|
socket = null;
|
|
}
|
|
public void Disponse()
|
|
{
|
|
Stop();
|
|
OnPacket = null;
|
|
}
|
|
}
|
|
|
|
|
|
public sealed class AsyncUserToken
|
|
{
|
|
public IConnection Connection { get; set; }
|
|
public Socket Socket { get; set; }
|
|
public ReceiveDataBuffer DataBuffer { get; set; } = new ReceiveDataBuffer();
|
|
public byte[] PoolBuffer { get; set; }
|
|
|
|
public void Clear()
|
|
{
|
|
Connection?.Disponse();
|
|
Socket = null;
|
|
|
|
|
|
|
|
PoolBuffer = Helper.EmptyArray;
|
|
|
|
DataBuffer.Clear(true);
|
|
|
|
GC.Collect();
|
|
// GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
|
|
public sealed class BroadcastEndpointInfo
|
|
{
|
|
public int Web { get; set; }
|
|
public int Api { get; set; }
|
|
public int Service { get; set; }
|
|
}
|
|
}
|