Compare commits
8
Commits
2d82d60f9e
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1542df493 | ||
|
|
da2a9431df | ||
|
|
c198d8a727 | ||
|
|
8862c6c8a6 | ||
|
|
57855e80e3 | ||
|
|
5cd58eeec5 | ||
|
|
9e5a79fb68 | ||
|
|
287e268df7 |
No files matched your search
@@ -66,7 +66,10 @@
|
|||||||
"System.Memory": "4.5.5"
|
"System.Memory": "4.5.5"
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"LobbyServerDto.dll": {}
|
"LobbyServerDto.dll": {
|
||||||
|
"assemblyVersion": "1.0.0.0",
|
||||||
|
"fileVersion": "1.0.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "com.incobyte.lobbyclient",
|
"name": "com.incobyte.lobbyclient",
|
||||||
"version": "1.0.3",
|
"version": "1.0.8",
|
||||||
"displayName": "Game Lobby Client",
|
"displayName": "Game Lobby Client",
|
||||||
"description": "Provides a client for the game lobvy server to list and join lobbies",
|
"description": "Provides a client for the game lobby server to list and join lobbies",
|
||||||
"unity": "2022.3",
|
"unity": "2022.3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"nat punch",
|
"nat punch",
|
||||||
|
|||||||
+199
-66
@@ -21,7 +21,7 @@ namespace Lobbies
|
|||||||
|
|
||||||
UdpEchoServer udpEchoServer = new UdpEchoServer();
|
UdpEchoServer udpEchoServer = new UdpEchoServer();
|
||||||
|
|
||||||
private Dictionary<Guid, LobbyInfo> lobbyInformation = new Dictionary<Guid, LobbyInfo>();
|
private ConcurrentDictionary<Guid, LobbyInfo> lobbyInformation = new ConcurrentDictionary<Guid, LobbyInfo>();
|
||||||
private string? host;
|
private string? host;
|
||||||
private int port;
|
private int port;
|
||||||
private int connectionId;
|
private int connectionId;
|
||||||
@@ -29,6 +29,8 @@ namespace Lobbies
|
|||||||
public string? externalIp;
|
public string? externalIp;
|
||||||
public int externalPort;
|
public int externalPort;
|
||||||
|
|
||||||
|
private CancellationTokenRegistration connectionCancellationRegistration;
|
||||||
|
|
||||||
public void Connect(string host, int port, CancellationToken cancellationToken)
|
public void Connect(string host, int port, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
this.host = host;
|
this.host = host;
|
||||||
@@ -37,7 +39,8 @@ namespace Lobbies
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
using var cts = cancellationToken.Register(() =>
|
connectionCancellationRegistration.Dispose();
|
||||||
|
connectionCancellationRegistration = cancellationToken.Register(() =>
|
||||||
{
|
{
|
||||||
tcpClient.Stop();
|
tcpClient.Stop();
|
||||||
});
|
});
|
||||||
@@ -56,24 +59,9 @@ namespace Lobbies
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<LobbyClientEvent> ReadEvents(int maxEvents)
|
public bool TryReadEvent(out LobbyClientEvent? result)
|
||||||
{
|
{
|
||||||
if (events.Count > 0)
|
return events.TryDequeue(out result);
|
||||||
{
|
|
||||||
maxEvents = Math.Min(maxEvents, events.Count);
|
|
||||||
|
|
||||||
while (maxEvents > 0)
|
|
||||||
{
|
|
||||||
if(events.TryDequeue(out var _event))
|
|
||||||
{
|
|
||||||
yield return _event;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HostLobby(Guid gameId, string name, int gameMode, int maxPlayerCount, string? password, int port)
|
public void HostLobby(Guid gameId, string name, int gameMode, int maxPlayerCount, string? password, int port)
|
||||||
@@ -102,8 +90,16 @@ namespace Lobbies
|
|||||||
};
|
};
|
||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
var len = lobbyCreate.Serialize(messageData);
|
var len = lobbyCreate.Serialize(messageData);
|
||||||
await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData);
|
await tcpClient.Send(messageData, 0, len);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bufferRental.Return(messageData);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,8 +107,11 @@ namespace Lobbies
|
|||||||
{
|
{
|
||||||
_ = Task.Run(async () => {
|
_ = Task.Run(async () => {
|
||||||
byte[]? passwordHash = null;
|
byte[]? passwordHash = null;
|
||||||
if (!string.IsNullOrEmpty(password) && lobbyInformation.ContainsKey(lobbyId) && lobbyInformation[lobbyId].PasswordSalt != null)
|
|
||||||
passwordHash = PasswordHash.Hash(password, lobbyInformation[lobbyId].PasswordSalt!);
|
if (!string.IsNullOrEmpty(password) &&
|
||||||
|
lobbyInformation.TryGetValue(lobbyId, out var lobby) &&
|
||||||
|
lobby.PasswordSalt != null)
|
||||||
|
passwordHash = PasswordHash.Hash(password, lobby.PasswordSalt!);
|
||||||
|
|
||||||
var lobbyCreate = new LobbyRequestHostInfo()
|
var lobbyCreate = new LobbyRequestHostInfo()
|
||||||
{
|
{
|
||||||
@@ -120,9 +119,16 @@ namespace Lobbies
|
|||||||
PasswordHash = passwordHash,
|
PasswordHash = passwordHash,
|
||||||
};
|
};
|
||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
var messageData = bufferRental.Rent();
|
||||||
|
try
|
||||||
|
{
|
||||||
var len = lobbyCreate.Serialize(messageData);
|
var len = lobbyCreate.Serialize(messageData);
|
||||||
await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData);
|
await tcpClient.Send(messageData, 0, len);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bufferRental.Return(messageData);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,21 +155,48 @@ namespace Lobbies
|
|||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
byte[]? passwordHash = null;
|
byte[]? passwordHash = null;
|
||||||
if (!string.IsNullOrEmpty(password) && lobbyInformation.ContainsKey(lobbyId) && lobbyInformation[lobbyId].PasswordSalt != null)
|
if (!string.IsNullOrEmpty(password) &&
|
||||||
passwordHash = PasswordHash.Hash(password, lobbyInformation[lobbyId].PasswordSalt!);
|
lobbyInformation.TryGetValue(lobbyId, out var lobby) &&
|
||||||
|
lobby.PasswordSalt != null)
|
||||||
|
{
|
||||||
|
passwordHash = PasswordHash.Hash(password, lobby.PasswordSalt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!QueryExternalIpAndPort(sendUdpToGetExternalPortMappingCallback, out var ip, out var mappedPort))
|
||||||
|
{
|
||||||
|
events.Enqueue(new LobbyClientEvent
|
||||||
|
{
|
||||||
|
EventType = LobbyClientEventTypes.Failed,
|
||||||
|
EventData = new LobbyClientDisconnectReason
|
||||||
|
{
|
||||||
|
WasError = true,
|
||||||
|
ErrorMessage = "Could not determine external IP/port."
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QueryExternalIpAndPort(sendUdpToGetExternalPortMappingCallback);
|
|
||||||
var lobbyRequestNatPunch = new LobbyRequestNatPunch()
|
var lobbyRequestNatPunch = new LobbyRequestNatPunch()
|
||||||
{
|
{
|
||||||
LobbyId = lobbyId,
|
LobbyId = lobbyId,
|
||||||
PasswordHash = passwordHash,
|
PasswordHash = passwordHash,
|
||||||
ClientIp = externalIp,
|
ClientIp = ip,
|
||||||
ClientPort = externalPort
|
ClientPort = mappedPort
|
||||||
};
|
};
|
||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
var len = lobbyRequestNatPunch.Serialize(messageData);
|
var len = lobbyRequestNatPunch.Serialize(messageData);
|
||||||
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await tcpClient.Send(messageData, 0, len);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bufferRental.Return(messageData);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,31 +212,65 @@ namespace Lobbies
|
|||||||
/// after that the udp client will be disposed.</param>
|
/// after that the udp client will be disposed.</param>
|
||||||
public int RequestLobbyNatPunch(Guid lobbyId, string? password, int port = 0)
|
public int RequestLobbyNatPunch(Guid lobbyId, string? password, int port = 0)
|
||||||
{
|
{
|
||||||
if(port < 0 && port > 65535)
|
if(port < 0 || port > 65535)
|
||||||
throw new ArgumentOutOfRangeException(nameof(port));
|
throw new ArgumentOutOfRangeException(nameof(port));
|
||||||
|
|
||||||
var udpEchoServer = new UdpEchoServer();
|
|
||||||
udpEchoServer.Start(port);
|
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
byte[]? passwordHash = null;
|
var udpEchoServer = new UdpEchoServer();
|
||||||
if (!string.IsNullOrEmpty(password) && lobbyInformation.ContainsKey(lobbyId) && lobbyInformation[lobbyId].PasswordSalt != null)
|
|
||||||
passwordHash = PasswordHash.Hash(password, lobbyInformation[lobbyId].PasswordSalt!);
|
try
|
||||||
|
{
|
||||||
|
udpEchoServer.Start(port);
|
||||||
|
|
||||||
|
byte[]? passwordHash = null;
|
||||||
|
if (!string.IsNullOrEmpty(password) &&
|
||||||
|
lobbyInformation.TryGetValue(lobbyId, out var lobby) &&
|
||||||
|
lobby.PasswordSalt != null)
|
||||||
|
{
|
||||||
|
passwordHash = PasswordHash.Hash(password, lobby.PasswordSalt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!QueryExternalIpAndPort(udpEchoServer.Send, out var ip, out var mappedPort))
|
||||||
|
{
|
||||||
|
events.Enqueue(new LobbyClientEvent
|
||||||
|
{
|
||||||
|
EventType = LobbyClientEventTypes.Failed,
|
||||||
|
EventData = new LobbyClientDisconnectReason
|
||||||
|
{
|
||||||
|
WasError = true,
|
||||||
|
ErrorMessage = "Could not determine external IP/port."
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QueryExternalIpAndPort(udpEchoServer.Send);
|
|
||||||
udpEchoServer.Dispose();
|
|
||||||
var lobbyRequestNatPunch = new LobbyRequestNatPunch()
|
var lobbyRequestNatPunch = new LobbyRequestNatPunch()
|
||||||
{
|
{
|
||||||
LobbyId = lobbyId,
|
LobbyId = lobbyId,
|
||||||
PasswordHash = passwordHash,
|
PasswordHash = passwordHash,
|
||||||
ClientIp = externalIp,
|
ClientIp = ip,
|
||||||
ClientPort = externalPort
|
ClientPort = mappedPort
|
||||||
};
|
};
|
||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
var len = lobbyRequestNatPunch.Serialize(messageData);
|
var len = lobbyRequestNatPunch.Serialize(messageData);
|
||||||
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await tcpClient.Send(messageData, 0, len);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bufferRental.Return(messageData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
udpEchoServer.Dispose();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return udpEchoServer.Port;
|
return udpEchoServer.Port;
|
||||||
@@ -233,7 +300,11 @@ namespace Lobbies
|
|||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
var len = lobbyUpdate.Serialize(messageData);
|
var len = lobbyUpdate.Serialize(messageData);
|
||||||
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
|
_ = Task.Run(async () => {
|
||||||
|
try { await tcpClient.Send(messageData, 0, len); }
|
||||||
|
finally { bufferRental.Return(messageData); }
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseLobby()
|
public void CloseLobby()
|
||||||
@@ -247,7 +318,9 @@ namespace Lobbies
|
|||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
var len = lobbyDelete.Serialize(messageData);
|
var len = lobbyDelete.Serialize(messageData);
|
||||||
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
|
_ = Task.Run(async () => {
|
||||||
|
try { await tcpClient.Send(messageData, 0, len); } finally { bufferRental.Return(messageData); }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ObserveLobbies(Guid gameId)
|
public void ObserveLobbies(Guid gameId)
|
||||||
@@ -256,7 +329,7 @@ namespace Lobbies
|
|||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
var len = lobbiesObserve.Serialize(messageData);
|
var len = lobbiesObserve.Serialize(messageData);
|
||||||
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
|
_ = Task.Run(async () => { try { await tcpClient.Send(messageData, 0, len); } finally { bufferRental.Return(messageData); } });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopObservingLobbies()
|
public void StopObservingLobbies()
|
||||||
@@ -265,7 +338,7 @@ namespace Lobbies
|
|||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
var len = lobbiesStopObserve.Serialize(messageData);
|
var len = lobbiesStopObserve.Serialize(messageData);
|
||||||
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
|
_ = Task.Run(async () => { try { await tcpClient.Send(messageData, 0, len); } finally { bufferRental.Return(messageData); } });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void NotifyLobbyNatPunchDone(int natPunchId, string externalIp, int externalPort)
|
public void NotifyLobbyNatPunchDone(int natPunchId, string externalIp, int externalPort)
|
||||||
@@ -274,7 +347,7 @@ namespace Lobbies
|
|||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
var len = lobbyNatPunchDone.Serialize(messageData);
|
var len = lobbyNatPunchDone.Serialize(messageData);
|
||||||
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
|
_ = Task.Run(async () => { try { await tcpClient.Send(messageData, 0, len); } finally { bufferRental.Return(messageData); } });
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task TryDirectConnection(IPAddress[] ipAddressesToTry, int tryPort)
|
public Task TryDirectConnection(IPAddress[] ipAddressesToTry, int tryPort)
|
||||||
@@ -348,26 +421,58 @@ namespace Lobbies
|
|||||||
return new IPAddress[0];
|
return new IPAddress[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueryExternalIpAndPort(SendUdpMessageCallback sendUdpCallback)
|
public bool QueryExternalIpAndPort(SendUdpMessageCallback sendUdpCallback, out string? ip, out int port)
|
||||||
{
|
{
|
||||||
|
ip = null;
|
||||||
|
port = 0;
|
||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
waitForExternalIp.Reset();
|
if (sendUdpCallback == null)
|
||||||
var queryExternalPortAndIp = new QueryExternalPortAndIp() { LobbyClientId = connectionId };
|
throw new ArgumentNullException(nameof(sendUdpCallback));
|
||||||
var len = queryExternalPortAndIp.Serialize(messageData);
|
|
||||||
var ip = GetIPsByName(host!, true, false).First();
|
|
||||||
var tries = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
sendUdpCallback(new IPEndPoint(ip, port), messageData, len);
|
|
||||||
}
|
|
||||||
while (!waitForExternalIp.WaitOne(100) && tries++ < 100);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(host))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (connectionId <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var ips = GetIPsByName(host, true, false);
|
||||||
|
if (ips.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
externalIp = null;
|
||||||
|
externalPort = 0;
|
||||||
|
waitForExternalIp.Reset();
|
||||||
|
|
||||||
|
var queryExternalPortAndIp = new QueryExternalPortAndIp
|
||||||
|
{
|
||||||
|
LobbyClientId = connectionId
|
||||||
|
};
|
||||||
|
|
||||||
|
int len = queryExternalPortAndIp.Serialize(messageData);
|
||||||
|
var remoteEndpoint = new IPEndPoint(ips[0], this.port);
|
||||||
|
|
||||||
|
for (int tries = 0; tries < 100; tries++)
|
||||||
|
{
|
||||||
|
sendUdpCallback(remoteEndpoint, messageData, len);
|
||||||
|
|
||||||
|
if (waitForExternalIp.WaitOne(100))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(externalIp) && externalPort > 0)
|
||||||
|
{
|
||||||
|
ip = externalIp;
|
||||||
|
port = externalPort;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -432,7 +537,7 @@ namespace Lobbies
|
|||||||
var lobbyDelete = LobbyDelete.Deserialize(data.Span);
|
var lobbyDelete = LobbyDelete.Deserialize(data.Span);
|
||||||
if (lobbyDelete != null)
|
if (lobbyDelete != null)
|
||||||
{
|
{
|
||||||
lobbyInformation.Remove(lobbyDelete.Id);
|
lobbyInformation.TryRemove(lobbyDelete.Id, out var _);
|
||||||
events.Enqueue(new LobbyClientEvent { EventType = LobbyClientEventTypes.LobbyDelete, EventData = lobbyDelete });
|
events.Enqueue(new LobbyClientEvent { EventType = LobbyClientEventTypes.LobbyDelete, EventData = lobbyDelete });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -486,19 +591,47 @@ namespace Lobbies
|
|||||||
|
|
||||||
public IEnumerable<IPAddress> GatherLocalIpAddresses()
|
public IEnumerable<IPAddress> GatherLocalIpAddresses()
|
||||||
{
|
{
|
||||||
foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
|
foreach (var netInterface in NetworkInterface.GetAllNetworkInterfaces())
|
||||||
{
|
{
|
||||||
IPInterfaceProperties ipProps = netInterface.GetIPProperties();
|
if (netInterface.OperationalStatus != OperationalStatus.Up)
|
||||||
|
continue;
|
||||||
|
|
||||||
foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses)
|
if (netInterface.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Optional: virtuelle Interfaces rausfiltern (je nach Bedarf)
|
||||||
|
if (netInterface.Description.Contains("Virtual", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
netInterface.Description.Contains("VMware", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
netInterface.Description.Contains("Docker", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var ipProps = netInterface.GetIPProperties();
|
||||||
|
|
||||||
|
foreach (var addr in ipProps.UnicastAddresses)
|
||||||
{
|
{
|
||||||
yield return addr.Address;
|
var ip = addr.Address;
|
||||||
|
|
||||||
|
// Nur IPv4
|
||||||
|
if (ip.AddressFamily != AddressFamily.InterNetwork)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Loopback nochmal sicherheitshalber
|
||||||
|
if (IPAddress.IsLoopback(ip))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Link-local (APIPA) raus (169.254.x.x)
|
||||||
|
if (ip.GetAddressBytes()[0] == 169 && ip.GetAddressBytes()[1] == 254)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
yield return ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
Stop();
|
||||||
|
connectionCancellationRegistration.Dispose();
|
||||||
waitForExternalIp.Dispose();
|
waitForExternalIp.Dispose();
|
||||||
tcpClient.Dispose();
|
tcpClient.Dispose();
|
||||||
udpEchoServer.Dispose();
|
udpEchoServer.Dispose();
|
||||||
|
|||||||
+126
-87
@@ -1,4 +1,6 @@
|
|||||||
using System;
|
using LobbyServerDto;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -13,141 +15,170 @@ namespace Lobbies
|
|||||||
internal delegate void DisconnectedEventArgs(bool clean, string error);
|
internal delegate void DisconnectedEventArgs(bool clean, string error);
|
||||||
internal event DisconnectedEventArgs? Disconnected;
|
internal event DisconnectedEventArgs? Disconnected;
|
||||||
|
|
||||||
|
|
||||||
internal delegate void ConnectedEventArgs();
|
internal delegate void ConnectedEventArgs();
|
||||||
internal event ConnectedEventArgs? Connected;
|
internal event ConnectedEventArgs? Connected;
|
||||||
|
|
||||||
TcpClient? tcpClient;
|
private const int HeaderSize = 4;
|
||||||
NetworkStream? networkStream;
|
private const int MaxMessageSize = 4096;
|
||||||
CancellationTokenSource? cancellationTokenSource = new CancellationTokenSource();
|
private static readonly TimeSpan PingInterval = TimeSpan.FromSeconds(10);
|
||||||
bool running = false;
|
|
||||||
|
private TcpClient? tcpClient;
|
||||||
|
private NetworkStream? networkStream;
|
||||||
|
private CancellationTokenSource? cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
private bool running = false;
|
||||||
|
private readonly BufferRental bufferRental = new BufferRental(MaxMessageSize);
|
||||||
|
private async Task ReadExact(NetworkStream stream, Memory<byte> buffer, int length, CancellationToken token)
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
while (offset < length)
|
||||||
|
{
|
||||||
|
int read = await stream.ReadAsync(buffer.Slice(offset), token);
|
||||||
|
if (read == 0)
|
||||||
|
throw new EndOfStreamException();
|
||||||
|
|
||||||
|
offset += read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task Connect(string host, int port)
|
internal async Task Connect(string host, int port)
|
||||||
{
|
{
|
||||||
bool wasError = false;
|
bool cleanDisconnect = true;
|
||||||
string error = string.Empty;
|
string error = string.Empty;
|
||||||
|
Task? pingTask = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
cancellationTokenSource!.Token.ThrowIfCancellationRequested();
|
if (cancellationTokenSource == null || cancellationTokenSource.IsCancellationRequested)
|
||||||
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var token = cancellationTokenSource.Token;
|
||||||
|
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
running = true;
|
running = true;
|
||||||
tcpClient = new TcpClient();
|
tcpClient = new TcpClient();
|
||||||
using (cancellationTokenSource!.Token.Register(() => { tcpClient.Close(); }))
|
|
||||||
|
using (token.Register(() =>
|
||||||
|
{
|
||||||
|
try { tcpClient.Close(); } catch { }
|
||||||
|
}))
|
||||||
{
|
{
|
||||||
await tcpClient.ConnectAsync(host, port);
|
await tcpClient.ConnectAsync(host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
networkStream = tcpClient.GetStream();
|
networkStream = tcpClient.GetStream();
|
||||||
|
|
||||||
Memory<byte> buffer = new byte[4096];
|
|
||||||
Memory<byte> target = new byte[4096];
|
|
||||||
|
|
||||||
int currentOffset = 0;
|
|
||||||
int currentMessageRemainingLength = 0;
|
|
||||||
int currentMessageLength = 0;
|
|
||||||
bool validMessage = true;
|
|
||||||
int currentReadOffset = 0;
|
|
||||||
bool offsetSizeInt = false;
|
|
||||||
Connected?.Invoke();
|
Connected?.Invoke();
|
||||||
|
|
||||||
while (running)
|
pingTask = Task.Run(() => PingLoop(token), token);
|
||||||
|
|
||||||
|
Memory<byte> buffer = new byte[MaxMessageSize];
|
||||||
|
|
||||||
|
while (running && !token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
int copyOffset = 0;
|
await ReadExact(networkStream, buffer, HeaderSize, token);
|
||||||
int receivedBytes = currentReadOffset;
|
|
||||||
|
|
||||||
if (currentReadOffset < 4)
|
int length = BitConverter.ToInt32(buffer.Span.Slice(0, HeaderSize));
|
||||||
{
|
|
||||||
receivedBytes += await networkStream.ReadAsync(buffer.Slice(currentReadOffset), cancellationTokenSource.Token) + currentReadOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receivedBytes == 0 && running && !cancellationTokenSource.Token.IsCancellationRequested)
|
if (length < 0)
|
||||||
{
|
throw new InvalidDataException("Negative message length received.");
|
||||||
throw new Exception("Connection lost!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receivedBytes > 3 || (currentMessageRemainingLength > 0 && receivedBytes > currentMessageRemainingLength))
|
if (length > MaxMessageSize)
|
||||||
{
|
throw new InvalidDataException($"Message too large: {length} > {MaxMessageSize}.");
|
||||||
currentReadOffset = 0;
|
|
||||||
if (currentMessageLength == 0)
|
|
||||||
{
|
|
||||||
currentMessageRemainingLength = BitConverter.ToInt32(buffer.Span);
|
|
||||||
currentMessageLength = currentMessageRemainingLength;
|
|
||||||
receivedBytes -= 4;
|
|
||||||
copyOffset += 4;
|
|
||||||
offsetSizeInt = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
offsetSizeInt = false;
|
|
||||||
|
|
||||||
var receivedCount = Math.Min(receivedBytes, currentMessageRemainingLength);
|
if (length == 0)
|
||||||
receivedBytes -= receivedCount;
|
continue;
|
||||||
copyOffset += receivedCount;
|
|
||||||
|
|
||||||
if (validMessage && currentOffset + receivedCount > 0)
|
await ReadExact(networkStream, buffer, length, token);
|
||||||
{
|
|
||||||
if (currentOffset + receivedCount < target.Length)
|
|
||||||
buffer.Slice(offsetSizeInt ? 4 : 0, receivedCount).CopyTo(target.Slice(currentOffset));
|
|
||||||
else
|
|
||||||
validMessage = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentOffset += receivedCount;
|
DataReceived?.Invoke(length, buffer.Slice(0, length));
|
||||||
currentMessageRemainingLength -= receivedCount;
|
|
||||||
|
|
||||||
if (currentMessageRemainingLength <= 0)
|
|
||||||
{
|
|
||||||
if (validMessage)
|
|
||||||
DataReceived?.Invoke(currentMessageLength, target);
|
|
||||||
|
|
||||||
if (receivedBytes > 0)
|
|
||||||
{
|
|
||||||
buffer.Slice(copyOffset, receivedBytes).CopyTo(buffer);
|
|
||||||
currentReadOffset += receivedBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentOffset = 0;
|
|
||||||
currentMessageLength = 0;
|
|
||||||
currentMessageRemainingLength = 0;
|
|
||||||
validMessage = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (receivedBytes > 0)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
currentReadOffset += receivedBytes;
|
cleanDisconnect = true;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
cleanDisconnect = false;
|
||||||
error = e.Message;
|
error = e.Message;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
wasError = running;
|
|
||||||
|
|
||||||
running = false;
|
running = false;
|
||||||
|
|
||||||
networkStream?.Dispose();
|
try { cancellationTokenSource?.Cancel(); } catch { }
|
||||||
tcpClient?.Dispose();
|
|
||||||
|
if (pingTask != null)
|
||||||
|
{
|
||||||
|
try { await pingTask; } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
try { networkStream?.Dispose(); } catch { }
|
||||||
|
try { tcpClient?.Close(); } catch { }
|
||||||
|
try { tcpClient?.Dispose(); } catch { }
|
||||||
|
|
||||||
tcpClient = null;
|
|
||||||
networkStream = null;
|
networkStream = null;
|
||||||
|
tcpClient = null;
|
||||||
|
|
||||||
Disconnected?.Invoke(!wasError, error);
|
Disconnected?.Invoke(cleanDisconnect, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PingLoop(CancellationToken token)
|
||||||
|
{
|
||||||
|
while (running && !token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Task.Delay(PingInterval, token);
|
||||||
|
|
||||||
|
if (!running || token.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
|
await SendPing(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendPing(CancellationToken token)
|
||||||
|
{
|
||||||
|
if (!running || networkStream == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await networkStream.WriteAsync(BitConverter.GetBytes(0), 0, 4, token);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task Send(byte[] buffer, int offset, int count)
|
internal async Task Send(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
|
byte[] frame = bufferRental.Rent();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (running && networkStream != null)
|
if (!running || networkStream == null || cancellationTokenSource == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (count < 0 || count > MaxMessageSize)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count));
|
||||||
|
|
||||||
|
if (offset < 0 || offset + count > buffer.Length)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||||
|
|
||||||
|
BitConverter.GetBytes(count).CopyTo(frame, 0);
|
||||||
|
Buffer.BlockCopy(buffer, offset, frame, HeaderSize, count);
|
||||||
|
|
||||||
|
await networkStream.WriteAsync(frame, 0, HeaderSize + count, cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch
|
||||||
{
|
{
|
||||||
await networkStream.WriteAsync(BitConverter.GetBytes(count - offset), 0, 4, cancellationTokenSource!.Token);
|
|
||||||
await networkStream.WriteAsync(buffer, offset, count, cancellationTokenSource!.Token);
|
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bufferRental.Return(frame);
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Stop()
|
internal void Stop()
|
||||||
@@ -158,9 +189,17 @@ namespace Lobbies
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
cancellationTokenSource?.Cancel();
|
running = false;
|
||||||
tcpClient?.Dispose();
|
|
||||||
cancellationTokenSource?.Dispose();
|
try { cancellationTokenSource?.Cancel(); } catch { }
|
||||||
|
try { networkStream?.Dispose(); } catch { }
|
||||||
|
try { tcpClient?.Close(); } catch { }
|
||||||
|
try { tcpClient?.Dispose(); } catch { }
|
||||||
|
try { cancellationTokenSource?.Dispose(); } catch { }
|
||||||
|
|
||||||
|
networkStream = null;
|
||||||
|
tcpClient = null;
|
||||||
|
cancellationTokenSource = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@ using Lobbies;
|
|||||||
using LobbyClientTest;
|
using LobbyClientTest;
|
||||||
using LobbyServerDto;
|
using LobbyServerDto;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.WebSockets;
|
|
||||||
|
|
||||||
Console.WriteLine("Starting lobby client v0.7!");
|
Console.WriteLine("Starting lobby client v0.7!");
|
||||||
var lobbyClient = new LobbyClient();
|
var lobbyClient = new LobbyClient();
|
||||||
@@ -25,8 +24,10 @@ _ = Task.Run(() =>
|
|||||||
{
|
{
|
||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
foreach (var lobbyEvent in lobbyClient.ReadEvents(20))
|
while (lobbyClient.TryReadEvent(out var lobbyEvent))
|
||||||
{
|
{
|
||||||
|
if (lobbyEvent == null) continue;
|
||||||
|
|
||||||
switch (lobbyEvent.EventType)
|
switch (lobbyEvent.EventType)
|
||||||
{
|
{
|
||||||
case LobbyClientEventTypes.LobbyJoinFailed:
|
case LobbyClientEventTypes.LobbyJoinFailed:
|
||||||
@@ -169,7 +170,7 @@ _ = Task.Run(() =>
|
|||||||
lobbyClient.QueryExternalIpAndPort((remoteEndpoint, messageData, messageLength) =>
|
lobbyClient.QueryExternalIpAndPort((remoteEndpoint, messageData, messageLength) =>
|
||||||
{
|
{
|
||||||
fakeGameHost.Send(remoteEndpoint, messageData, messageLength);
|
fakeGameHost.Send(remoteEndpoint, messageData, messageLength);
|
||||||
});
|
}, out var ip, out var port);
|
||||||
|
|
||||||
var ep = new IPEndPoint(IPAddress.Parse(lobbyRequestNatPunch.ClientIp!), lobbyRequestNatPunch.ClientPort);
|
var ep = new IPEndPoint(IPAddress.Parse(lobbyRequestNatPunch.ClientIp!), lobbyRequestNatPunch.ClientPort);
|
||||||
for (int z = 0; z < 32; z++)
|
for (int z = 0; z < 32; z++)
|
||||||
|
|||||||
@@ -13,16 +13,27 @@ namespace LobbyServer
|
|||||||
public delegate void ClientDisconnectedEventArgs(int clientId);
|
public delegate void ClientDisconnectedEventArgs(int clientId);
|
||||||
public event ClientDisconnectedEventArgs? ClientDisconnected;
|
public event ClientDisconnectedEventArgs? ClientDisconnected;
|
||||||
|
|
||||||
|
private const int HeaderSize = 4;
|
||||||
|
private const int MaxMessageSize = 4096;
|
||||||
|
private readonly BufferRental bufferRental = new BufferRental(MaxMessageSize);
|
||||||
|
|
||||||
internal class Client : IDisposable
|
internal class Client : IDisposable
|
||||||
{
|
{
|
||||||
internal CancellationTokenSource? cancellationToken = null;
|
internal CancellationTokenSource? cancellationToken = null;
|
||||||
internal NetworkStream? stream;
|
internal NetworkStream? stream;
|
||||||
internal TcpClient? client;
|
internal TcpClient? client;
|
||||||
|
internal DateTime lastSeenUtc = DateTime.UtcNow;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
cancellationToken?.Dispose();
|
try { stream?.Dispose(); } catch { }
|
||||||
|
try { client?.Close(); } catch { }
|
||||||
|
try { client?.Dispose(); } catch { }
|
||||||
|
try { cancellationToken?.Dispose(); } catch { }
|
||||||
|
|
||||||
cancellationToken = null;
|
cancellationToken = null;
|
||||||
|
stream = null;
|
||||||
|
client = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +49,7 @@ namespace LobbyServer
|
|||||||
cancellationToken.TryReset();
|
cancellationToken.TryReset();
|
||||||
running = true;
|
running = true;
|
||||||
clientIdCounter = 0;
|
clientIdCounter = 0;
|
||||||
|
_ = Task.Run(() => MonitorClients());
|
||||||
_ = Task.Run(() => Listener(port));
|
_ = Task.Run(() => Listener(port));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +67,6 @@ namespace LobbyServer
|
|||||||
catch
|
catch
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
activeClients.Clear ();
|
|
||||||
|
|
||||||
serverClosed.WaitOne();
|
serverClosed.WaitOne();
|
||||||
}
|
}
|
||||||
@@ -93,127 +104,131 @@ namespace LobbyServer
|
|||||||
|
|
||||||
public async Task Send(int clientId, byte[] buffer, int offset, int count)
|
public async Task Send(int clientId, byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
|
byte[] frame = bufferRental.Rent();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (activeClients.TryGetValue(clientId, out var lobbyClient) && lobbyClient.stream != null && lobbyClient.cancellationToken != null)
|
if (activeClients.TryGetValue(clientId, out var lobbyClient) && lobbyClient.stream != null && lobbyClient.cancellationToken != null)
|
||||||
{
|
{
|
||||||
await lobbyClient.stream.WriteAsync(BitConverter.GetBytes(count - offset), 0, 4, lobbyClient.cancellationToken.Token);
|
BitConverter.GetBytes(count).CopyTo(frame, 0);
|
||||||
await lobbyClient.stream.WriteAsync(buffer, offset, count, lobbyClient.cancellationToken.Token);
|
Buffer.BlockCopy(buffer, offset, frame, HeaderSize, count);
|
||||||
|
|
||||||
|
await lobbyClient.stream.WriteAsync(frame, 0, HeaderSize + count, lobbyClient.cancellationToken.Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bufferRental.Return(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReadExact(NetworkStream stream, Memory<byte> buffer, int length, CancellationToken token)
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
while (offset < length)
|
||||||
|
{
|
||||||
|
int read = await stream.ReadAsync(buffer.Slice(offset), token);
|
||||||
|
if (read == 0)
|
||||||
|
throw new EndOfStreamException();
|
||||||
|
|
||||||
|
offset += read;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ClientThread(TcpClient client)
|
private async Task ClientThread(TcpClient client)
|
||||||
{
|
{
|
||||||
int myId = Interlocked.Increment(ref clientIdCounter);
|
int myId = Interlocked.Increment(ref clientIdCounter);
|
||||||
|
Client? lobbyClient = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await using NetworkStream stream = client.GetStream();
|
var stream = client.GetStream();
|
||||||
|
|
||||||
Memory<byte> buffer = new byte[4096];
|
Memory<byte> buffer = new byte[MaxMessageSize];
|
||||||
Memory<byte> target = new byte[4096];
|
|
||||||
|
|
||||||
using var lobbyClient = new Client
|
|
||||||
|
lobbyClient = new Client
|
||||||
{
|
{
|
||||||
cancellationToken = new CancellationTokenSource(),
|
cancellationToken = new CancellationTokenSource(),
|
||||||
stream = stream,
|
stream = stream,
|
||||||
client = client
|
client = client,
|
||||||
|
lastSeenUtc = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|
||||||
activeClients.TryAdd(myId, lobbyClient);
|
activeClients.TryAdd(myId, lobbyClient);
|
||||||
|
|
||||||
int currentOffset = 0;
|
|
||||||
int currentMessageRemainingLength = 0;
|
|
||||||
int currentMessageLength = 0;
|
|
||||||
bool validMessage = true;
|
|
||||||
bool offsetSizeInt = false;
|
|
||||||
int currentReadOffset = 0;
|
|
||||||
|
|
||||||
var lobbyClientConnectionInfo = new LobbyClientConnectionInfo { Id = myId };
|
var lobbyClientConnectionInfo = new LobbyClientConnectionInfo { Id = myId };
|
||||||
byte[] sendBuffer = new byte[128];
|
byte[] sendBuffer = new byte[128];
|
||||||
int sendLen = lobbyClientConnectionInfo.Serialize(sendBuffer);
|
int sendLen = lobbyClientConnectionInfo.Serialize(sendBuffer);
|
||||||
await Send(myId, sendBuffer, 0, sendLen);
|
await Send(myId, sendBuffer, 0, sendLen);
|
||||||
|
|
||||||
while (running)
|
while (running && !lobbyClient.cancellationToken.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
int copyOffset = 0;
|
await ReadExact(stream, buffer, HeaderSize, lobbyClient.cancellationToken.Token);
|
||||||
int receivedBytes = currentReadOffset;
|
lobbyClient.lastSeenUtc = DateTime.UtcNow;
|
||||||
|
|
||||||
if (currentReadOffset < 4)
|
int length = BitConverter.ToInt32(buffer.Span.Slice(0, HeaderSize));
|
||||||
{
|
|
||||||
receivedBytes += await stream.ReadAsync(buffer.Slice(currentReadOffset), lobbyClient.cancellationToken.Token) + currentReadOffset;
|
|
||||||
if (receivedBytes == 0)
|
|
||||||
throw new Exception("Connection lost!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receivedBytes > 3 || (currentMessageRemainingLength > 0 && receivedBytes >= currentMessageRemainingLength))
|
if (length < 0)
|
||||||
{
|
throw new InvalidDataException("Negative message length received.");
|
||||||
currentReadOffset = 0;
|
|
||||||
if (currentMessageLength == 0)
|
|
||||||
{
|
|
||||||
currentMessageRemainingLength = BitConverter.ToInt32(buffer.Span);
|
|
||||||
currentMessageLength = currentMessageRemainingLength;
|
|
||||||
receivedBytes -= 4;
|
|
||||||
copyOffset += 4;
|
|
||||||
offsetSizeInt = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
offsetSizeInt = false;
|
|
||||||
|
|
||||||
var receivedCount = Math.Min(receivedBytes, currentMessageRemainingLength);
|
if (length > MaxMessageSize)
|
||||||
|
throw new InvalidDataException($"Message too large: {length} > {MaxMessageSize}.");
|
||||||
|
|
||||||
receivedBytes -= receivedCount;
|
if (length == 0)
|
||||||
copyOffset += receivedCount;
|
continue;
|
||||||
|
|
||||||
if (validMessage && currentOffset + receivedCount > 0)
|
await ReadExact(stream, buffer, length, lobbyClient.cancellationToken.Token);
|
||||||
{
|
|
||||||
if (currentOffset + receivedCount < target.Length)
|
|
||||||
buffer.Slice(offsetSizeInt ? 4 : 0, receivedCount).CopyTo(target.Slice(currentOffset));
|
|
||||||
else
|
|
||||||
validMessage = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentOffset += receivedCount;
|
DataReceived?.Invoke(myId, length, buffer.Slice(0, length));
|
||||||
currentMessageRemainingLength -= receivedCount;
|
|
||||||
|
|
||||||
if (currentMessageRemainingLength <= 0)
|
|
||||||
{
|
|
||||||
if(validMessage)
|
|
||||||
DataReceived?.Invoke(myId, currentMessageLength, target);
|
|
||||||
|
|
||||||
if (receivedBytes > 0)
|
|
||||||
{
|
|
||||||
buffer.Slice(copyOffset, receivedBytes).CopyTo(buffer);
|
|
||||||
currentReadOffset += receivedBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentOffset = 0;
|
|
||||||
currentMessageLength = 0;
|
|
||||||
currentMessageRemainingLength = 0;
|
|
||||||
validMessage = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(receivedBytes > 0)
|
|
||||||
{
|
|
||||||
currentReadOffset += receivedBytes;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
activeClients.TryRemove(myId, out _);
|
||||||
|
lobbyClient?.Dispose();
|
||||||
ClientDisconnected?.Invoke(myId);
|
ClientDisconnected?.Invoke(myId);
|
||||||
activeClients.TryRemove(myId, out var _);
|
|
||||||
client?.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string? GetClientIp(int cliendId)
|
private async Task MonitorClients()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (activeClients.TryGetValue(cliendId, out var client))
|
while (running)
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
|
foreach (var kv in activeClients)
|
||||||
|
{
|
||||||
|
var id = kv.Key;
|
||||||
|
var client = kv.Value;
|
||||||
|
|
||||||
|
if ((now - client.lastSeenUtc) > TimeSpan.FromSeconds(30))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.cancellationToken?.Cancel();
|
||||||
|
client.client?.Close();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(1000, cancellationToken.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string? GetClientIp(int cliendIp)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (activeClients.TryGetValue(cliendIp, out var client))
|
||||||
{
|
{
|
||||||
return (client.client!.Client.RemoteEndPoint as IPEndPoint)!.Address.ToString();
|
return (client.client!.Client.RemoteEndPoint as IPEndPoint)!.Address.ToString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Net;
|
|
||||||
using LobbyServerDto;
|
using LobbyServerDto;
|
||||||
|
|
||||||
namespace LobbyServer
|
namespace LobbyServer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Small udp server to receive udp packets and report their remote ip and port to a event delegate
|
/// Small udp server to receive udp packets and report their remote ip and port to an event delegate
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class UdpCandidateServer : IDisposable
|
internal class UdpCandidateServer : IDisposable
|
||||||
{
|
{
|
||||||
@@ -36,14 +35,12 @@ namespace LobbyServer
|
|||||||
var receiveResult = await serverSocket.ReceiveAsync(cancellationTokenSource.Token);
|
var receiveResult = await serverSocket.ReceiveAsync(cancellationTokenSource.Token);
|
||||||
if(receiveResult.Buffer.Length > 0)
|
if(receiveResult.Buffer.Length > 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{DateTime.Now}: [UdpCandidateServer] received packet");
|
|
||||||
Memory<byte> receivedData = new Memory<byte>(receiveResult.Buffer);
|
Memory<byte> receivedData = new Memory<byte>(receiveResult.Buffer);
|
||||||
var identifier = LobbyMessageIdentifier.ReadLobbyMessageIdentifier(receivedData.Span);
|
var identifier = LobbyMessageIdentifier.ReadLobbyMessageIdentifier(receivedData.Span);
|
||||||
switch (identifier)
|
switch (identifier)
|
||||||
{
|
{
|
||||||
case QueryExternalPortAndIp.TypeId:
|
case QueryExternalPortAndIp.TypeId:
|
||||||
var queryExternalPortAndIp = QueryExternalPortAndIp.Deserialize(receivedData.Span);
|
var queryExternalPortAndIp = QueryExternalPortAndIp.Deserialize(receivedData.Span);
|
||||||
Console.WriteLine($"{DateTime.Now}: [UdpCandidateServer] received QueryExternalPortAndIp for lobby client id {queryExternalPortAndIp.LobbyClientId} from {receiveResult.RemoteEndPoint.Address.ToString()}:{receiveResult.RemoteEndPoint.Port}");
|
|
||||||
QueryIpAndPort?.Invoke(queryExternalPortAndIp.LobbyClientId, receiveResult.RemoteEndPoint.Address.ToString(), receiveResult.RemoteEndPoint.Port);
|
QueryIpAndPort?.Invoke(queryExternalPortAndIp.LobbyClientId, receiveResult.RemoteEndPoint.Address.ToString(), receiveResult.RemoteEndPoint.Port);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Drawing;
|
|
||||||
|
|
||||||
namespace LobbyServerDto
|
namespace LobbyServerDto
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user