Optimise sending to avoid unnecessary pinning

This commit is contained in:
UnknownShadow200 2017-07-28 15:20:26 +10:00
parent e0875e0924
commit 0a6b5d3ee0
4 changed files with 74 additions and 73 deletions

View File

@ -36,7 +36,7 @@ namespace MCGalaxy.Network {
void Send(byte[] buffer, bool sync = false);
/// <summary> Sends a block of low-priority data, either synchronously or asynchronously. </summary>
void SendLowPriority(byte[] buffer, bool sync = false);
void SendLowPriority(byte[] buffer);
/// <summary> Closes this network socket. </summary>
void Close();

View File

@ -13,6 +13,7 @@ or implied. See the Licenses for the specific language governing
permissions and limitations under the Licenses.
*/
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
@ -27,6 +28,11 @@ namespace MCGalaxy.Network {
byte[] recvBuffer = new byte[256];
int unprocessedLen;
byte[] sendBuffer = new byte[1536];
readonly object sendLock = new object();
readonly Queue<byte[]> sendQueue = new Queue<byte[]>(64);
volatile bool sendInProgress;
public TcpSocket(Player p, Socket s) {
player = p; socket = s;
}
@ -39,6 +45,7 @@ namespace MCGalaxy.Network {
set { socket.NoDelay = value; }
}
static AsyncCallback recvCallback = new AsyncCallback(ReceiveCallback);
public void ReceiveNextAsync() {
socket.BeginReceive(recvBuffer, 0, recvBuffer.Length, SocketFlags.None, recvCallback, this);
@ -73,8 +80,7 @@ namespace MCGalaxy.Network {
} catch (SocketException) {
p.Disconnect();
} catch (ObjectDisposedException) {
// Player is no longer connected, socket was closed
// Mark this as disconnected and remove them from active connection list
// Socket was closed by another thread, mark as disconnected
Player.connections.Remove(p);
p.RemoveFromPending();
p.disconnected = true;
@ -91,68 +97,63 @@ namespace MCGalaxy.Network {
if (player.disconnected || !socket.Connected) return;
try {
if (sync)
if (sync) {
socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
else
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, sendCallback, this);
buffer = null;
} catch (SocketException e) {
buffer = null;
return;
}
lock (sendLock) {
if (sendInProgress) { sendQueue.Enqueue(buffer); }
else { DoSendAsync(buffer); }
}
} catch (SocketException) {
player.Disconnect();
#if DEBUG
Logger.LogError(e);
#endif
} catch (ObjectDisposedException) {
// socket was already closed by another thread.
buffer = null;
// Socket was already closed by another thread
}
}
// TODO: do this seprately
public void SendLowPriority(byte[] buffer, bool sync = false) {
Send(buffer, sync);
public void SendLowPriority(byte[] buffer) { Send(buffer, false); }
void DoSendAsync(byte[] buffer) {
sendInProgress = true;
// BlockCopy has some overhead, not worth using for very small data
if (buffer.Length <= 16) {
for (int i = 0; i < buffer.Length; i++) {
sendBuffer[i] = buffer[i];
}
} else {
Buffer.BlockCopy(buffer, 0, sendBuffer, 0, buffer.Length);
}
socket.BeginSend(sendBuffer, 0, buffer.Length, SocketFlags.None, sendCallback, this);
}
static void SendCallback(IAsyncResult result) {
// TODO: call EndSend, need to check if all data was sent or not!
/*TcpSocket s = (TcpSocket)result.AsyncState;
TcpSocket s = (TcpSocket)result.AsyncState;
try {
// TODO: Need to check if all data was sent or not?
int sent = s.socket.EndSend(result);
} catch (SocketException e) {
lock (s.sendLock) {
s.sendInProgress = false;
if (s.sendQueue.Count > 0) {
s.DoSendAsync(s.sendQueue.Dequeue());
}
}
} catch (SocketException) {
s.player.Disconnect();
#if DEBUG
Logger.LogError(e);
#endif
} catch (ObjectDisposedException) {
// socket was already closed by another thread.
}*/
// Socket was already closed by another thread
}
}
public void Close() {
// Try to close the socket. Sometimes socket is already closed, so just hide this.
#if !DEBUG
try { socket.Shutdown(SocketShutdown.Both); } catch { }
try { socket.Close(); } catch { }
#else
try {
socket.Shutdown(SocketShutdown.Both);
Logger.Log(LogType.Debug, "Socket was shutdown for " + name ?? ip);
} catch (Exception e) {
Exception ex = new Exception("Failed to shutdown socket for " + name ?? ip, e);
Logger.LogError(ex);
}
try {
socket.Close();
Logger.Log(LogType.Debug, "Socket was closed for " + name ?? ip);
} catch (Exception e) {
Exception ex = new Exception("Failed to close socket for " + name ?? ip, e);
Logger.LogError(ex);
}
#endif
lock (sendLock) { sendQueue.Clear(); }
}
}
}

View File

@ -54,42 +54,42 @@ namespace MCGalaxy {
}
void SendCpeExtensions() {
Send(Packet.ExtInfo(26), true);
Send(Packet.ExtInfo(26));
Send(Packet.ExtEntry(CpeExt.EnvMapAppearance, 1), true); // fix for classicube client, doesn't reply if only send EnvMapAppearance with version 2
Send(Packet.ExtEntry(CpeExt.ClickDistance, 1), true);
Send(Packet.ExtEntry(CpeExt.CustomBlocks, 1), true);
Send(Packet.ExtEntry(CpeExt.EnvMapAppearance, 1)); // fix for classicube client, doesn't reply if only send EnvMapAppearance with version 2
Send(Packet.ExtEntry(CpeExt.ClickDistance, 1));
Send(Packet.ExtEntry(CpeExt.CustomBlocks, 1));
Send(Packet.ExtEntry(CpeExt.HeldBlock, 1), true);
Send(Packet.ExtEntry(CpeExt.TextHotkey, 1), true);
Send(Packet.ExtEntry(CpeExt.EnvColors, 1), true);
Send(Packet.ExtEntry(CpeExt.HeldBlock, 1));
Send(Packet.ExtEntry(CpeExt.TextHotkey, 1));
Send(Packet.ExtEntry(CpeExt.EnvColors, 1));
Send(Packet.ExtEntry(CpeExt.SelectionCuboid, 1), true);
Send(Packet.ExtEntry(CpeExt.BlockPermissions, 1), true);
Send(Packet.ExtEntry(CpeExt.ChangeModel, 1), true);
Send(Packet.ExtEntry(CpeExt.SelectionCuboid, 1));
Send(Packet.ExtEntry(CpeExt.BlockPermissions, 1));
Send(Packet.ExtEntry(CpeExt.ChangeModel, 1));
Send(Packet.ExtEntry(CpeExt.EnvMapAppearance, 2), true);
Send(Packet.ExtEntry(CpeExt.EnvWeatherType, 1), true);
Send(Packet.ExtEntry(CpeExt.HackControl, 1), true);
Send(Packet.ExtEntry(CpeExt.EnvMapAppearance, 2));
Send(Packet.ExtEntry(CpeExt.EnvWeatherType, 1));
Send(Packet.ExtEntry(CpeExt.HackControl, 1));
Send(Packet.ExtEntry(CpeExt.EmoteFix, 1), true);
Send(Packet.ExtEntry(CpeExt.FullCP437, 1), true);
Send(Packet.ExtEntry(CpeExt.LongerMessages, 1), true);
Send(Packet.ExtEntry(CpeExt.EmoteFix, 1));
Send(Packet.ExtEntry(CpeExt.FullCP437, 1));
Send(Packet.ExtEntry(CpeExt.LongerMessages, 1));
Send(Packet.ExtEntry(CpeExt.BlockDefinitions, 1), true);
Send(Packet.ExtEntry(CpeExt.BlockDefinitionsExt, 2), true);
Send(Packet.ExtEntry(CpeExt.TextColors, 1), true);
Send(Packet.ExtEntry(CpeExt.BlockDefinitions, 1));
Send(Packet.ExtEntry(CpeExt.BlockDefinitionsExt, 2));
Send(Packet.ExtEntry(CpeExt.TextColors, 1));
Send(Packet.ExtEntry(CpeExt.BulkBlockUpdate, 1), true);
Send(Packet.ExtEntry(CpeExt.MessageTypes, 1), true);
Send(Packet.ExtEntry(CpeExt.ExtPlayerList, 2), true);
Send(Packet.ExtEntry(CpeExt.BulkBlockUpdate, 1));
Send(Packet.ExtEntry(CpeExt.MessageTypes, 1));
Send(Packet.ExtEntry(CpeExt.ExtPlayerList, 2));
Send(Packet.ExtEntry(CpeExt.EnvMapAspect, 1), true);
Send(Packet.ExtEntry(CpeExt.PlayerClick, 1), true);
Send(Packet.ExtEntry(CpeExt.EntityProperty, 1), true);
Send(Packet.ExtEntry(CpeExt.EnvMapAspect, 1));
Send(Packet.ExtEntry(CpeExt.PlayerClick, 1));
Send(Packet.ExtEntry(CpeExt.EntityProperty, 1));
Send(Packet.ExtEntry(CpeExt.ExtEntityPositions, 1), true);
Send(Packet.ExtEntry(CpeExt.TwoWayPing, 1), true);
Send(Packet.ExtEntry(CpeExt.ExtEntityPositions, 1));
Send(Packet.ExtEntry(CpeExt.TwoWayPing, 1));
}
void CompleteLoginProcess() {