fix: Fixes networking (#822)

* Fixes wepoll to be POSIX compliant.
This commit is contained in:
Kamron Batman 2021-10-09 11:55:31 -07:00 committed by GitHub
parent 05ff3f2ad9
commit 81486772f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 60 additions and 161 deletions

View file

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using Moq;
using Server.Network;
namespace Server.Tests.Network
@ -11,18 +9,8 @@ namespace Server.Tests.Network
public static Span<byte> Compile(this Packet p) =>
p.Compile(false, out var length).AsSpan(0, length);
public static NetState CreateTestNetState()
{
var socket = new Mock<ISocket>();
socket
.Setup(s => s.SendAsync(It.IsAny<IList<ArraySegment<byte>>>(), SocketFlags.None))
.ReturnsAsync(() => 0);
socket
.Setup(s => s.ReceiveAsync(It.IsAny<IList<ArraySegment<byte>>>(), SocketFlags.None))
.ReturnsAsync(() => 0);
return new NetState(socket.Object);
}
public static NetState CreateTestNetState() => new(
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
);
}
}

View file

@ -9,7 +9,7 @@ namespace Server.Tests.Network
[Fact]
public void TestGlobalLightLevel()
{
byte lightLevel = 5;
const byte lightLevel = 5;
var expected = new GlobalLightLevel(lightLevel).Compile();
var ns = PacketTestUtilities.CreateTestNetState();

View file

@ -48,7 +48,7 @@ namespace Server.Network
private const int MenuCap = 512;
private const int PacketPerSecondThreshold = 3000;
private static NetState[] _polledStates = new NetState[2048];
private static GCHandle[] _polledStates = new GCHandle[2048];
private static readonly IPollGroup _pollGroup = PollGroup.Create();
private static readonly Queue<NetState> FlushPending = new(2048);
private static readonly Queue<NetState> FlushedPartials = new(2048);
@ -101,7 +101,7 @@ namespace Server.Network
Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1.5), CheckAllAlive);
}
public NetState(ISocket connection)
public NetState(Socket connection)
{
Connection = connection;
Seeded = false;
@ -130,7 +130,7 @@ namespace Server.Network
try
{
_pollGroup.Add(this);
_pollGroup.Add(connection, _handle);
}
catch (Exception ex)
{
@ -171,7 +171,7 @@ namespace Server.Network
public Pipe<byte> SendPipe { get; }
public ISocket Connection { get; }
public Socket Connection { get; }
public bool CompressionEnabled { get; set; }
@ -885,8 +885,8 @@ namespace Server.Network
{
for (int i = 0; i < count; i++)
{
_polledStates[i].HandleReceive();
_polledStates[i] = null;
(_polledStates[i].Target as NetState)?.HandleReceive();
_polledStates[i] = default;
}
}
@ -1032,7 +1032,7 @@ namespace Server.Network
TcpServer.Instances.Remove(this);
try
{
_pollGroup.Remove(this);
_pollGroup.Remove(Connection);
}
catch (Exception ex)
{

View file

@ -1,77 +0,0 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: NetworkSocket.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Server.Network
{
public class NetworkSocket : ISocket
{
private readonly Socket _connection;
public Socket Connection
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _connection;
}
public IntPtr Handle
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _connection.Handle;
}
public EndPoint LocalEndPoint
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _connection.LocalEndPoint;
}
public EndPoint RemoteEndPoint
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _connection.RemoteEndPoint;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NetworkSocket(Socket connection) => _connection = connection;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Task<int> SendAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags) =>
_connection.SendAsync(buffers, flags);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Send(IList<ArraySegment<byte>> buffers, SocketFlags flags) => _connection.Send(buffers, flags);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Task<int> ReceiveAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags) =>
_connection.ReceiveAsync(buffers, flags);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Receive(IList<ArraySegment<byte>> buffers, SocketFlags flags) =>
_connection.Receive(buffers, flags);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Shutdown(SocketShutdown how) => _connection.Shutdown(how);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Close() => _connection.Close();
}
}

View file

@ -14,6 +14,7 @@
*************************************************************************/
using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace Server.Network
@ -53,23 +54,20 @@ namespace Server.Network
EPOLL_CTL_MOD = 3,
}
[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Explicit, Size = 8)]
private struct epoll_data
{
[FieldOffset(0)]
public int fd;
[FieldOffset(0)]
public IntPtr ptr;
[FieldOffset(0)]
public uint u32;
[FieldOffset(0)]
public ulong u64;
}
[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Explicit, Pack = 4)]
private struct epoll_event
{
[FieldOffset(0)]
@ -109,10 +107,12 @@ namespace Server.Network
}
private readonly int _epHndle;
private readonly bool _isWindows;
public EPollGroup()
{
_epHndle = Core.IsWindows ? Windows.epoll_create1(epoll_flags.NONE) : Linux.epoll_create1(epoll_flags.NONE);
_isWindows = Core.IsWindows;
_epHndle = _isWindows ? Windows.epoll_create1(epoll_flags.NONE) : Linux.epoll_create1(epoll_flags.NONE);
if (_epHndle == 0)
{
@ -122,7 +122,7 @@ namespace Server.Network
public void Dispose()
{
if (Core.IsWindows)
if (_isWindows)
{
Windows.epoll_close(_epHndle);
}
@ -132,18 +132,18 @@ namespace Server.Network
}
}
public void Add(NetState state)
public void Add(Socket socket, GCHandle handle)
{
var ev = new epoll_event
{
events = epoll_events.EPOLLIN | epoll_events.EPOLLERR
};
ev.data.ptr = (IntPtr)state._handle;
ev.data.ptr = (IntPtr)handle;
var rc = Core.IsWindows ?
Windows.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_ADD, (int)state.Connection.Handle, ref ev) :
Linux.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_ADD, (int)state.Connection.Handle, ref ev);
var rc = _isWindows ?
Windows.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_ADD, (int)socket.Handle, ref ev) :
Linux.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_ADD, (int)socket.Handle, ref ev);
if (rc != 0)
@ -152,17 +152,13 @@ namespace Server.Network
}
}
public void Remove(NetState state)
public void Remove(Socket socket)
{
var ev = new epoll_event
{
events = epoll_events.EPOLLIN | epoll_events.EPOLLERR,
};
ev.data.ptr = (IntPtr)state._handle;
var ev = new epoll_event { events = epoll_events.EPOLLIN | epoll_events.EPOLLERR };
var rc = Core.IsWindows ?
Windows.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_DEL, (int)state.Connection.Handle, ref ev) :
Linux.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_DEL, (int)state.Connection.Handle, ref ev);
var rc = _isWindows ?
Windows.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_DEL, (int)socket.Handle, ref ev) :
Linux.epoll_ctl(_epHndle, epoll_op.EPOLL_CTL_DEL, (int)socket.Handle, ref ev);
if (rc != 0)
{
@ -172,36 +168,29 @@ namespace Server.Network
private epoll_event[] _events = new epoll_event[2048];
public int Poll(ref NetState[] states)
public int Poll(ref GCHandle[] handles)
{
if (states.Length > _events.Length)
if (handles.Length > _events.Length)
{
var newLength = Math.Max(states.Length, _events.Length + (_events.Length >> 2));
var newLength = Math.Max(handles.Length, _events.Length + (_events.Length >> 2));
_events = new epoll_event[newLength];
}
var rc = Core.IsWindows ?
Windows.epoll_wait(_epHndle, _events, states.Length, 0) :
Linux.epoll_wait(_epHndle, _events, states.Length, 0);
var rc = _isWindows ?
Windows.epoll_wait(_epHndle, _events, handles.Length, 0) :
Linux.epoll_wait(_epHndle, _events, handles.Length, 0);
if (rc <= 0)
{
return rc;
}
int count = 0;
for (int i = 0; i < rc; i++)
{
if (((GCHandle)_events[i].data.ptr).Target is not NetState state)
{
continue;
}
states[count++] = state;
handles[i] = (GCHandle)_events[i].data.ptr;
}
return count;
return rc;
}
}

View file

@ -15,6 +15,7 @@
using System;
using System.IO;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace Server.Network
@ -181,14 +182,14 @@ namespace Server.Network
Marshal.FreeHGlobal(_zeroTimeoutPtr);
}
public void Add(NetState state)
public void Add(Socket socket, GCHandle handle)
{
var rc = BSD.kevent(
_kqueueHndle,
state.Connection.Handle,
socket.Handle,
kqueue_filter.READ | kqueue_filter.WRITE,
kqueue_flags.ADD | kqueue_flags.CLEAR,
udata: (IntPtr)state._handle
udata: (IntPtr)handle
);
if (rc != 0)
@ -197,14 +198,13 @@ namespace Server.Network
}
}
public void Remove(NetState state)
public void Remove(Socket socket)
{
var rc = BSD.kevent(
_kqueueHndle,
state.Connection.Handle,
socket.Handle,
kqueue_filter.READ | kqueue_filter.WRITE,
kqueue_flags.DELETE,
udata: (IntPtr)state._handle
kqueue_flags.DELETE
);
if (rc != 0)
@ -215,11 +215,11 @@ namespace Server.Network
private kevent[] _events = new kevent[2048];
public int Poll(ref NetState[] states)
public int Poll(ref GCHandle[] handles)
{
if (states.Length > _events.Length)
if (handles.Length > _events.Length)
{
var newLength = Math.Max(states.Length, _events.Length + (_events.Length >> 2));
var newLength = Math.Max(handles.Length, _events.Length + (_events.Length >> 2));
_events = new kevent[newLength];
}
@ -230,19 +230,12 @@ namespace Server.Network
return rc;
}
int count = 0;
for (int i = 0; i < rc; i++)
{
if (((GCHandle)_events[i].udata).Target is not NetState state)
{
continue;
}
states[count++] = state;
handles[i] = (GCHandle)_events[i].udata;
}
return count;
return rc;
}
}
}

View file

@ -14,14 +14,16 @@
*************************************************************************/
using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace Server.Network
{
public interface IPollGroup : IDisposable
{
void Add(NetState state);
void Remove(NetState state);
int Poll(ref NetState[] states);
void Add(Socket sock, GCHandle handle);
void Remove(Socket sock);
int Poll(ref GCHandle[] handles);
}
public static class PollGroup

View file

@ -196,7 +196,7 @@ namespace Server.Network
}
else
{
var ns = new NetState(new NetworkSocket(socket));
var ns = new NetState(socket);
_connectedQueue.Enqueue(ns);
}
}

Binary file not shown.

View file

@ -68,7 +68,11 @@ namespace Server.Misc
{
var ns = e.State;
var ipep = (IPEndPoint)ns.Connection.LocalEndPoint;
var ipep = (IPEndPoint)ns.Connection?.LocalEndPoint;
if (ipep == null)
{
return;
}
var localAddress = ipep.Address;
var localPort = ipep.Port;
@ -76,7 +80,7 @@ namespace Server.Misc
if (IsPrivateNetwork(localAddress))
{
ipep = (IPEndPoint)ns.Connection.RemoteEndPoint;
if (!IsPrivateNetwork(ipep.Address) && _publicAddress != null)
if (ipep == null || !IsPrivateNetwork(ipep.Address) && _publicAddress != null)
{
localAddress = _publicAddress;
}