diff --git a/MCGalaxy/Network/Player.Networking.cs b/MCGalaxy/Network/Player.Networking.cs
index 7597c5668..a55d553f7 100644
--- a/MCGalaxy/Network/Player.Networking.cs
+++ b/MCGalaxy/Network/Player.Networking.cs
@@ -294,7 +294,7 @@ namespace MCGalaxy {
internal void CloseSocket() {
Socket.Close();
- RemoveFromPending();
+ RemoveFromPending();
}
}
}
diff --git a/MCGalaxy/Network/Socket/Interfaces.cs b/MCGalaxy/Network/Socket/Interfaces.cs
index dff319427..9ef183eaa 100644
--- a/MCGalaxy/Network/Socket/Interfaces.cs
+++ b/MCGalaxy/Network/Socket/Interfaces.cs
@@ -36,7 +36,7 @@ namespace MCGalaxy.Network {
void Send(byte[] buffer, bool sync = false);
/// Sends a block of low-priority data, either synchronously or asynchronously.
- void SendLowPriority(byte[] buffer, bool sync = false);
+ void SendLowPriority(byte[] buffer);
/// Closes this network socket.
void Close();
diff --git a/MCGalaxy/Network/Socket/TcpSocket.cs b/MCGalaxy/Network/Socket/TcpSocket.cs
index f7883e27f..f7cba3554 100644
--- a/MCGalaxy/Network/Socket/TcpSocket.cs
+++ b/MCGalaxy/Network/Socket/TcpSocket.cs
@@ -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;
@@ -24,9 +25,14 @@ namespace MCGalaxy.Network {
readonly Socket socket;
byte[] unprocessed = new byte[352];
- byte[] recvBuffer = new byte[256];
+ byte[] recvBuffer = new byte[256];
int unprocessedLen;
+ byte[] sendBuffer = new byte[1536];
+ readonly object sendLock = new object();
+ readonly Queue sendQueue = new Queue(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(); }
}
}
}
diff --git a/MCGalaxy/Player/Player.Login.cs b/MCGalaxy/Player/Player.Login.cs
index 7d300a12f..863750efc 100644
--- a/MCGalaxy/Player/Player.Login.cs
+++ b/MCGalaxy/Player/Player.Login.cs
@@ -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() {