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() {