Use more efficient ReceiveAsync/SendAsync for even less memory allocations

This commit is contained in:
UnknownShadow200 2017-07-31 10:57:37 +10:00
parent 6711538f06
commit 51a25d7c44

View File

@ -25,39 +25,48 @@ namespace MCGalaxy.Network {
readonly Socket socket; readonly Socket socket;
byte[] unprocessed = new byte[352]; byte[] unprocessed = new byte[352];
byte[] recvBuffer = new byte[256]; byte[] recvBuffer = new byte[256];
int unprocessedLen; int unprocessedLen;
readonly SocketAsyncEventArgs recvArgs = new SocketAsyncEventArgs();
byte[] sendBuffer = new byte[1536]; byte[] sendBuffer = new byte[1536];
readonly object sendLock = new object(); readonly object sendLock = new object();
readonly Queue<byte[]> sendQueue = new Queue<byte[]>(64); readonly Queue<byte[]> sendQueue = new Queue<byte[]>(64);
volatile bool sendInProgress; volatile bool sendInProgress;
readonly SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
public TcpSocket(Player p, Socket s) { public TcpSocket(Player p, Socket s) {
player = p; socket = s; player = p; socket = s;
recvArgs.SetBuffer(recvBuffer, 0, recvBuffer.Length);
recvArgs.Completed += recvCallback;
recvArgs.UserToken = this;
sendArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);
sendArgs.Completed += sendCallback;
sendArgs.UserToken = this;
} }
public string RemoteIP { public string RemoteIP {
get { return ((IPEndPoint)socket.RemoteEndPoint).Address.ToString(); } get { return ((IPEndPoint)socket.RemoteEndPoint).Address.ToString(); }
} }
public bool LowLatency { public bool LowLatency { set { socket.NoDelay = value; } }
set { socket.NoDelay = value; }
}
static AsyncCallback recvCallback = new AsyncCallback(ReceiveCallback); static EventHandler<SocketAsyncEventArgs> recvCallback = RecvCallback;
public void ReceiveNextAsync() { public void ReceiveNextAsync() {
socket.BeginReceive(recvBuffer, 0, recvBuffer.Length, SocketFlags.None, recvCallback, this); // ReceiveAsync returns false if completed sync
if (!socket.ReceiveAsync(recvArgs)) RecvCallback(null, recvArgs);
} }
static void ReceiveCallback(IAsyncResult result) { static void RecvCallback(object sender, SocketAsyncEventArgs e) {
TcpSocket s = (TcpSocket)result.AsyncState; TcpSocket s = (TcpSocket)e.UserToken;
Player p = s.player; Player p = s.player;
if (p.disconnected) return; if (p.disconnected) return;
try { try {
int recvLen = s.socket.EndReceive(result); int recvLen = e.BytesTransferred;
if (recvLen == 0) { p.Disconnect(); return; } if (recvLen == 0) { p.Disconnect(); return; }
// Packets may not always be fully received in a Receive call // Packets may not always be fully received in a Receive call
@ -84,14 +93,14 @@ namespace MCGalaxy.Network {
Player.connections.Remove(p); Player.connections.Remove(p);
p.RemoveFromPending(); p.RemoveFromPending();
p.disconnected = true; p.disconnected = true;
} catch (Exception e) { } catch (Exception ex) {
Logger.LogError(e); Logger.LogError(ex);
p.Leave("Error!"); p.Leave("Error!");
} }
} }
static AsyncCallback sendCallback = new AsyncCallback(SendCallback); static EventHandler<SocketAsyncEventArgs> sendCallback = SendCallback;
public void Send(byte[] buffer, bool sync = false) { public void Send(byte[] buffer, bool sync = false) {
// Abort if socket has been closed // Abort if socket has been closed
if (player.disconnected || !socket.Connected) return; if (player.disconnected || !socket.Connected) return;
@ -127,14 +136,16 @@ namespace MCGalaxy.Network {
Buffer.BlockCopy(buffer, 0, sendBuffer, 0, buffer.Length); Buffer.BlockCopy(buffer, 0, sendBuffer, 0, buffer.Length);
} }
socket.BeginSend(sendBuffer, 0, buffer.Length, SocketFlags.None, sendCallback, this); sendArgs.SetBuffer(0, buffer.Length);
// SendAsync returns false if completed sync
if (!socket.SendAsync(sendArgs)) SendCallback(null, sendArgs);
} }
static void SendCallback(IAsyncResult result) { static void SendCallback(object sender, SocketAsyncEventArgs e) {
TcpSocket s = (TcpSocket)result.AsyncState; TcpSocket s = (TcpSocket)e.UserToken;
try { try {
// TODO: Need to check if all data was sent or not? // TODO: Need to check if all data was sent or not?
int sent = s.socket.EndSend(result); int sent = e.BytesTransferred;
lock (s.sendLock) { lock (s.sendLock) {
s.sendInProgress = false; s.sendInProgress = false;
if (s.sendQueue.Count > 0) { if (s.sendQueue.Count > 0) {
@ -154,6 +165,8 @@ namespace MCGalaxy.Network {
try { socket.Shutdown(SocketShutdown.Both); } catch { } try { socket.Shutdown(SocketShutdown.Both); } catch { }
try { socket.Close(); } catch { } try { socket.Close(); } catch { }
lock (sendLock) { sendQueue.Clear(); } lock (sendLock) { sendQueue.Clear(); }
try { recvArgs.Dispose(); } catch { }
try { sendArgs.Dispose(); } catch { }
} }
} }
} }