cleanup unused script
This commit is contained in:
parent
aebc4011c7
commit
3446dcd6f1
|
|
@ -1,11 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Network.Defines
|
|
||||||
{
|
|
||||||
public class SystemMessage
|
|
||||||
{
|
|
||||||
public string Content { get; set; }
|
|
||||||
public DateTime Timestamp { get; set; } = DateTime.Now;
|
|
||||||
public string Level { get; set; } = "info"; // "info", "warning", "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 9a9d4f06e72ce954598ae990364ac8ac
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace Network.NetworkTransport
|
|
||||||
{
|
|
||||||
public class ClientSession
|
|
||||||
{
|
|
||||||
public IPEndPoint EndPoint { get; }
|
|
||||||
public DateTime LastActivity { get; private set; }
|
|
||||||
|
|
||||||
public uint SendSequenceNumber { get; private set; } = 0;
|
|
||||||
|
|
||||||
//TODO: 数据结构——ConcurrentDictionary
|
|
||||||
public ConcurrentDictionary<uint, (Packet packet, DateTime sendTime)> PendingAcks { get; } =
|
|
||||||
new ConcurrentDictionary<uint, (Packet packet, DateTime sendTime)>();
|
|
||||||
|
|
||||||
public uint ExpectedReceiveSequence { get; private set; } = 0;
|
|
||||||
private HashSet<uint> _receivedSequences { get; } = new HashSet<uint>();
|
|
||||||
|
|
||||||
private readonly object _lockObj = new object();
|
|
||||||
|
|
||||||
public ClientSession(IPEndPoint endPoint)
|
|
||||||
{
|
|
||||||
EndPoint = endPoint;
|
|
||||||
LastActivity = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GetNextSendSequence()
|
|
||||||
{
|
|
||||||
lock (_lockObj)
|
|
||||||
{
|
|
||||||
return SendSequenceNumber++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryProcessReceiveSequence(uint sequenceNumber, out bool shouldDeliver)
|
|
||||||
{
|
|
||||||
lock (_lockObj)
|
|
||||||
{
|
|
||||||
LastActivity = DateTime.Now;
|
|
||||||
|
|
||||||
if (sequenceNumber == ExpectedReceiveSequence)
|
|
||||||
{
|
|
||||||
ExpectedReceiveSequence++;
|
|
||||||
_receivedSequences.Add(sequenceNumber);
|
|
||||||
shouldDeliver = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (sequenceNumber < ExpectedReceiveSequence)
|
|
||||||
{
|
|
||||||
shouldDeliver = false;
|
|
||||||
return _receivedSequences.Contains(sequenceNumber);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shouldDeliver = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: c007e0986e972b14cb4b4ab2151459c3
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Network.NetworkTransport
|
|
||||||
{
|
|
||||||
public enum PacketType : byte
|
|
||||||
{
|
|
||||||
Data = 1,
|
|
||||||
Ack = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Packet
|
|
||||||
{
|
|
||||||
public PacketType Type;
|
|
||||||
public uint SequenceNumber;
|
|
||||||
public byte[] Data;
|
|
||||||
|
|
||||||
public byte[] ToBytes()
|
|
||||||
{
|
|
||||||
var result = new byte[1 + 4 + Data.Length];
|
|
||||||
result[0] = (byte)Type;
|
|
||||||
BitConverter.GetBytes(SequenceNumber).CopyTo(result, 1);
|
|
||||||
Data.CopyTo(result, 5);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Packet FromBytes(byte[] data)
|
|
||||||
{
|
|
||||||
return new Packet
|
|
||||||
{
|
|
||||||
Type = (PacketType)data[0],
|
|
||||||
SequenceNumber = BitConverter.ToUInt32(data, 1),
|
|
||||||
//TODO: 结构体——ArraySegment
|
|
||||||
Data = new ArraySegment<byte>(data, 5, data.Length - 5).ToArray()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Packet CreateDataPacket(uint seqNum, byte[] data)
|
|
||||||
{
|
|
||||||
return new Packet
|
|
||||||
{
|
|
||||||
Type = PacketType.Data,
|
|
||||||
SequenceNumber = seqNum,
|
|
||||||
Data = data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Packet CreateAckPacket(uint seqNum)
|
|
||||||
{
|
|
||||||
return new Packet
|
|
||||||
{
|
|
||||||
Type = PacketType.Ack,
|
|
||||||
SequenceNumber = seqNum,
|
|
||||||
Data = Array.Empty<byte>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: a16c0628d84e6274bbe164e0e57533e4
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,312 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Network.NetworkTransport
|
|
||||||
{
|
|
||||||
internal sealed class LegacyUdpTransportAdapter : ITransport, IPeerSessionTransport
|
|
||||||
{
|
|
||||||
private readonly UdpClient _client;
|
|
||||||
private readonly IPEndPoint? _defaultRemoteEndPoint;
|
|
||||||
private readonly bool _isServer;
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, ClientSession> _sessions = new ConcurrentDictionary<string, ClientSession>();
|
|
||||||
private readonly ConcurrentDictionary<Packet, int> _resentPacketTimes = new ConcurrentDictionary<Packet, int>();
|
|
||||||
private readonly Timer _retransmitTimer;
|
|
||||||
private readonly Timer _cleanupTimer;
|
|
||||||
|
|
||||||
//TODO: volatile 关键字
|
|
||||||
private volatile bool _isRunning;
|
|
||||||
|
|
||||||
// 配置参数
|
|
||||||
private const int RetransmitTimeoutMs = 1000;
|
|
||||||
private const int SessionTimeoutMs = 30000;
|
|
||||||
private const int MaxRetransmitAttempts = 5;
|
|
||||||
|
|
||||||
public event Action<byte[], IPEndPoint>? OnReceive;
|
|
||||||
|
|
||||||
// 构造函数——服务端模式
|
|
||||||
public LegacyUdpTransportAdapter(int listenPort)
|
|
||||||
{
|
|
||||||
_client = new UdpClient(listenPort);
|
|
||||||
_isServer = true;
|
|
||||||
_retransmitTimer = new Timer(CheckRetransmit, null, 100, 100);
|
|
||||||
_cleanupTimer = new Timer(CleanupSessions, null, 5000, 5000);
|
|
||||||
Console.WriteLine($"[Transport] 服务端模式,监听端口: {listenPort}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构造函数——客户端模式
|
|
||||||
public LegacyUdpTransportAdapter(string serverIP, int serverPort)
|
|
||||||
{
|
|
||||||
_client = new UdpClient(0);
|
|
||||||
_defaultRemoteEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);
|
|
||||||
|
|
||||||
_isServer = false;
|
|
||||||
_retransmitTimer = new Timer(CheckRetransmit, null, 100, 100);
|
|
||||||
_cleanupTimer = new Timer(CleanupSessions, null, 5000, 5000);
|
|
||||||
Console.WriteLine($"[Transport] 客户端模式,目标: {_defaultRemoteEndPoint}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task StartAsync()
|
|
||||||
{
|
|
||||||
_sessions.Clear();
|
|
||||||
|
|
||||||
_isRunning = true;
|
|
||||||
Console.WriteLine("[Transport] 传输层启动");
|
|
||||||
|
|
||||||
// 开始接收数据
|
|
||||||
_ = Task.Run(ReceiveLoop);
|
|
||||||
await Task.Delay(100); // 给接收循环一点启动时间
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
_isRunning = false;
|
|
||||||
_retransmitTimer.Dispose();
|
|
||||||
_cleanupTimer.Dispose();
|
|
||||||
_client.Close();
|
|
||||||
_sessions.Clear();
|
|
||||||
Console.WriteLine("[Transport] 传输层停止");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Send(byte[] data)
|
|
||||||
{
|
|
||||||
if (!_isServer && _defaultRemoteEndPoint != null)
|
|
||||||
{
|
|
||||||
SendTo(data, _defaultRemoteEndPoint);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("服务端模式必须使用 SendTo 指定目标");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SendTo(byte[] data, IPEndPoint target)
|
|
||||||
{
|
|
||||||
if (!_isRunning)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var session = GetOrCreateSession(target);
|
|
||||||
uint seqNum = session.GetNextSendSequence();
|
|
||||||
var packet = Packet.CreateDataPacket(seqNum, data);
|
|
||||||
|
|
||||||
session.PendingAcks[seqNum] = (packet, DateTime.Now);
|
|
||||||
|
|
||||||
SendPacketTo(packet, target);
|
|
||||||
Console.WriteLine($"[Transport] 发送数据包到 {target} SeqNum={seqNum}, DataLen={data.Length}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SendToAll(byte[] data)
|
|
||||||
{
|
|
||||||
foreach (var session in _sessions.Values)
|
|
||||||
{
|
|
||||||
SendTo(data, session.EndPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RemovePeerSession(IPEndPoint remoteEndPoint)
|
|
||||||
{
|
|
||||||
if (remoteEndPoint == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(remoteEndPoint));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _sessions.TryRemove(remoteEndPoint.ToString(), out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void ReceiveLoop()
|
|
||||||
{
|
|
||||||
while (_isRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var result = await _client.ReceiveAsync();
|
|
||||||
var packet = Packet.FromBytes(result.Buffer);
|
|
||||||
|
|
||||||
if (packet.Type == PacketType.Data)
|
|
||||||
{
|
|
||||||
HandleDataPacket(packet, result.RemoteEndPoint);
|
|
||||||
}
|
|
||||||
else if (packet.Type == PacketType.Ack)
|
|
||||||
{
|
|
||||||
HandleAckPacket(packet, result.RemoteEndPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
return; // 正常关闭
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[Transport] 接收错误:{e.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleDataPacket(Packet packet, IPEndPoint senderEndPoint)
|
|
||||||
{
|
|
||||||
var session = GetOrCreateSession(senderEndPoint);
|
|
||||||
|
|
||||||
Console.WriteLine(
|
|
||||||
$"[Transport] 收到数据包从{senderEndPoint} SeqNum={packet.SequenceNumber}, DataLen={packet.Data.Length}");
|
|
||||||
|
|
||||||
// 发送ACK
|
|
||||||
var ackPacket = Packet.CreateAckPacket(packet.SequenceNumber);
|
|
||||||
SendPacketTo(ackPacket, senderEndPoint);
|
|
||||||
Console.WriteLine($"[Transport] 发送ACK 到 {senderEndPoint} SeqNum={packet.SequenceNumber}");
|
|
||||||
|
|
||||||
// 检查是否应该交付
|
|
||||||
if (session.TryProcessReceiveSequence(packet.SequenceNumber, out bool shouldDeliver))
|
|
||||||
{
|
|
||||||
if (shouldDeliver)
|
|
||||||
{
|
|
||||||
OnReceive?.Invoke(packet.Data, senderEndPoint);
|
|
||||||
Console.WriteLine($"[Transport] 交付数据包从 {senderEndPoint} SeqNum={packet.SequenceNumber}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[Transport] 重复包从 {senderEndPoint} SeqNum={packet.SequenceNumber},忽略");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 乱序到达,暂存(简化处理:直接丢弃,依赖重传)
|
|
||||||
Console.WriteLine($"[Transport] 乱序包从 {senderEndPoint} SeqNum={packet.SequenceNumber},丢弃");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleAckPacket(Packet packet, IPEndPoint senderEndPoint)
|
|
||||||
{
|
|
||||||
var session = GetOrCreateSession(senderEndPoint);
|
|
||||||
Console.WriteLine($"[Transport] 收到ACK从 {senderEndPoint} SeqNum={packet.SequenceNumber}");
|
|
||||||
|
|
||||||
if (session.PendingAcks.TryRemove(packet.SequenceNumber, out _))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[Transport] 确认包到 {senderEndPoint} SeqNum={packet.SequenceNumber}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClientSession GetOrCreateSession(IPEndPoint endPoint)
|
|
||||||
{
|
|
||||||
string key = endPoint.ToString();
|
|
||||||
return _sessions.GetOrAdd(key, _ =>
|
|
||||||
{
|
|
||||||
var session = new ClientSession(endPoint);
|
|
||||||
Console.WriteLine($"创建新会话:{endPoint}");
|
|
||||||
return session;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckRetransmit(object? state)
|
|
||||||
{
|
|
||||||
if (!_isRunning)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = DateTime.Now;
|
|
||||||
var toRetransmit = new List<(IPEndPoint target, uint seqNum, Packet packet)>();
|
|
||||||
|
|
||||||
foreach (var sessionKvp in _sessions)
|
|
||||||
{
|
|
||||||
var session = sessionKvp.Value;
|
|
||||||
foreach (var ackKvp in session.PendingAcks)
|
|
||||||
{
|
|
||||||
var timeSinceLastSend = now - ackKvp.Value.sendTime;
|
|
||||||
if (timeSinceLastSend.TotalMilliseconds > RetransmitTimeoutMs)
|
|
||||||
{
|
|
||||||
toRetransmit.Add((session.EndPoint, ackKvp.Key, ackKvp.Value.packet));
|
|
||||||
_resentPacketTimes.TryAdd(ackKvp.Value.packet, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var (target, seqNum, packet) in toRetransmit)
|
|
||||||
{
|
|
||||||
var session = GetOrCreateSession(target);
|
|
||||||
if (session.PendingAcks.ContainsKey(seqNum))
|
|
||||||
{
|
|
||||||
// 更新发送时间
|
|
||||||
session.PendingAcks[seqNum] = (packet, now);
|
|
||||||
SendPacketTo(packet, target);
|
|
||||||
Console.WriteLine($"[Transport] 重传包到 {target} SeqNum={seqNum}");
|
|
||||||
|
|
||||||
_resentPacketTimes[packet]++;
|
|
||||||
if (_resentPacketTimes[packet] >= MaxRetransmitAttempts)
|
|
||||||
{
|
|
||||||
// 达到最大重传次数,放弃该会话
|
|
||||||
Console.WriteLine($"[Transport] 达到最大重传次数,放弃会话 {target}");
|
|
||||||
_sessions.TryRemove(target.ToString(), out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CleanupSessions(object? state)
|
|
||||||
{
|
|
||||||
if (!_isRunning)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = DateTime.Now;
|
|
||||||
var toRemove = new List<string>();
|
|
||||||
|
|
||||||
foreach (var sessionKvp in _sessions)
|
|
||||||
{
|
|
||||||
var session = sessionKvp.Value;
|
|
||||||
var timeSinceLastActivity = now - session.LastActivity;
|
|
||||||
|
|
||||||
if (timeSinceLastActivity.TotalMilliseconds > SessionTimeoutMs)
|
|
||||||
{
|
|
||||||
toRemove.Add(sessionKvp.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string key in toRemove)
|
|
||||||
{
|
|
||||||
//TODO: 清理会话的同时清理PlayerManager中的玩家数据
|
|
||||||
if (_sessions.TryRemove(key, out var session))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[Transport] 清理超时会话:{session.EndPoint}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_isServer)
|
|
||||||
{
|
|
||||||
PrintSessionInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void SendPacketTo(Packet packet, IPEndPoint? endPoint)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var data = packet.ToBytes();
|
|
||||||
await _client.SendAsync(data, data.Length, endPoint);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[Transport] 发送错误:{e.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrintSessionInfo()
|
|
||||||
{
|
|
||||||
Console.WriteLine($"当前活跃会话数:{_sessions.Count}");
|
|
||||||
foreach (var sessionKvp in _sessions)
|
|
||||||
{
|
|
||||||
var session = sessionKvp.Value;
|
|
||||||
Console.WriteLine(
|
|
||||||
$" 会话:{session.EndPoint},发送SeqNum:{session.SendSequenceNumber},期望接收:{session.ExpectedReceiveSequence},待确认: {session.PendingAcks.Count}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e740e71ab20f62d40bf5f56c2142bf02
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
Loading…
Reference in New Issue