Files
2025-08-19 12:45:50 +02:00

220 lines
7.3 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Inspectron.HawkEye.UDPB
{
public class UDPBSocket : IDisposable
{
public int LossSimulation { get; set; }
public EndPoint LastConnection => _lastConnection;
private readonly UDPBQueue _queue;
private readonly UDPBReceiveBuffer _receiveBuffer;
private readonly byte[] _receivePacketBuffer = new byte[10 * 1024 * 1024];
private readonly Random _rnd = new Random();
private Socket _localSocket;
private uint _currentSquenceId = 1;
private EndPoint _lastConnection = new IPEndPoint(IPAddress.Any, 27001);
private bool _isServer;
private readonly ConcurrentQueue<byte[]> _receiveQueue = new ConcurrentQueue<byte[]>();
private bool _isReceiveing = true;
private readonly Thread _receiveThread;
private readonly List<UDPBPacket> _packetCache = new List<UDPBPacket>();
public UDPBSocket()
{
_queue = new UDPBQueue();
_receiveBuffer = new UDPBReceiveBuffer(this, _receiveQueue);
_receiveThread = new Thread(ReceiveLoop);
}
public void Dispose()
{
if (!_isServer) _localSocket.Disconnect(false);
_isReceiveing = false;
}
public static int FindFreePort(IPAddress adapter)
{
var port = 0;
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
try
{
var localEP = new IPEndPoint(adapter, 0);
socket.Bind(localEP);
localEP = (IPEndPoint) socket.LocalEndPoint;
port = localEP.Port;
}
finally
{
socket.Close();
}
return port;
}
public void Listen(IPAddress adapter, int port)
{
Console.WriteLine($"Listen on {adapter}");
_isServer = true;
_localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_localSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.ReuseAddress, true);
//_localSocket.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.DontFragment, true);
_localSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 10 * 1024 * 1024);
_localSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, 10 * 1024 * 1024);
_localSocket.Bind(new IPEndPoint(adapter, port));
_receiveThread.Start();
}
public void Connect(IPEndPoint endpoint, IPAddress adapter)
{
Console.WriteLine($"Connect to {adapter}");
_localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//_localSocket.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.DontFragment, true);
_localSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 10 * 1024 * 1024);
_localSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, 10 * 1024 * 1024);
Console.WriteLine($"Binding on {adapter?.MapToIPv4()}");
if (adapter != null)
_localSocket.Bind(new IPEndPoint(adapter.MapToIPv4(), 0));
_localSocket.Connect(endpoint);
_receiveThread.Start();
}
public void SendData(byte[] data)
{
_packetCache.Clear();
IncrementSequence();
var begin = new UDPBPacket {Type = UDPBPacket.EPacketType.BeginSequence, SequenceId = _currentSquenceId};
SendPacket(begin, true);
_queue.Reset();
_queue.AddBuffer(data, 0, data.Length);
var lastId = Flush();
var finish = new UDPBPacket
{Type = UDPBPacket.EPacketType.CloseSequence, MessageId = lastId + 1, SequenceId = _currentSquenceId};
SendPacket(finish, true);
do
{
byte[] dataOK;
dataOK = Receive();
_localSocket.ReceiveTimeout = 0;
var packet = new UDPBPacket();
packet.Deserialize(dataOK, 0, dataOK.Length);
if (packet.Type == UDPBPacket.EPacketType.Ok)
{
Console.WriteLine("OK");
break;
}
if (packet.Type == UDPBPacket.EPacketType.Nak)
{
var ms = new MemoryStream(packet.Payload);
var br = new BinaryReader(ms);
var packets = br.ReadInt32();
Console.WriteLine("NAK " + packets);
for (var i = 0; i < packets; i++)
{
var id = br.ReadUInt32();
var p = _packetCache.First(x => x.MessageId == id);
SendPacket(p, true);
}
SendPacket(finish, true);
}
} while (true);
}
public byte[] Receive()
{
byte[] res;
while (!_receiveQueue.TryDequeue(out res)) Thread.Sleep(1);
return res;
}
internal void SendPacket(UDPBPacket packet, bool noCache = false)
{
if (!noCache) _packetCache.Add(packet);
if (LossSimulation > 0)
if (_rnd.Next(LossSimulation) == LossSimulation - 1)
return;
if (_isServer)
_localSocket.SendTo(packet.Serialize(), _lastConnection);
else
_localSocket.Send(packet.Serialize());
}
private void ReceiveLoop()
{
while (_isReceiveing)
{
var received = ReceiveFrom(_receivePacketBuffer, 0);
_receiveBuffer.AddPacketBytes(_receivePacketBuffer, 0, received);
}
}
private int ReceiveFrom(byte[] buffer, int bufferOffset)
{
return _localSocket.ReceiveFrom(buffer, bufferOffset,
UDPBQueue.PACKET_SIZE + UDPBPacket.packetHeaderSize,
SocketFlags.None, ref _lastConnection);
}
private void IncrementSequence()
{
_currentSquenceId++;
if (_currentSquenceId == uint.MaxValue) _currentSquenceId = 1;
}
private uint Flush()
{
uint lastPacketId = 0;
while (_queue.PacketsToSend() != 0)
{
var packet = new UDPBPacket();
byte[] data = null;
uint msgNo = 0;
var payload = _queue.ReadData(ref data, ref msgNo);
packet.Type = UDPBPacket.EPacketType.Data;
packet.Length = payload;
packet.Payload = data;
packet.MessageId = msgNo;
packet.SequenceId = _currentSquenceId;
SendPacket(packet);
lastPacketId = msgNo;
}
return lastPacketId;
}
}
}