From a5e6e6e80ee878843b413005587d253e0e75042f Mon Sep 17 00:00:00 2001 From: sta Date: Sat, 5 Oct 2013 23:28:43 +0900 Subject: [PATCH] Modified sending and added a new Send method --- websocket-sharp/Ext.cs | 24 ++- websocket-sharp/WebSocket.cs | 373 ++++++++++++++++++++++++----------- 2 files changed, 283 insertions(+), 114 deletions(-) diff --git a/websocket-sharp/Ext.cs b/websocket-sharp/Ext.cs index 549ed78a..1725c4cb 100644 --- a/websocket-sharp/Ext.cs +++ b/websocket-sharp/Ext.cs @@ -165,6 +165,22 @@ namespace WebSocketSharp } } + internal static string CheckIfCanRead (this Stream stream) + { + return stream == null + ? "'stream' must not be null." + : !stream.CanRead + ? "'stream' cannot be read." + : null; + } + + internal static string CheckIfOpen (this WebSocketState state) + { + return state != WebSocketState.OPEN + ? "A WebSocket connection isn't established or has been closed." + : null; + } + internal static string CheckIfStarted (this ServerState state) { return state != ServerState.START @@ -456,7 +472,7 @@ namespace WebSocketSharp var buffer = new byte [length]; var readLen = stream.Read (buffer, 0, length); if (readLen <= 0) - return new byte [] {}; + return new byte []{}; var tmpLen = 0; while (readLen < length) @@ -1148,7 +1164,7 @@ namespace WebSocketSharp public static byte [] ReadBytes (this Stream stream, int length) { return stream == null || length < 1 - ? new byte [] {} + ? new byte []{} : stream.ReadBytesInternal (length); } @@ -1168,7 +1184,7 @@ namespace WebSocketSharp public static byte [] ReadBytes (this Stream stream, long length) { return stream == null || length < 1 - ? new byte [] {} + ? new byte []{} : length > 1024 ? stream.ReadBytesInternal (length, 1024) : stream.ReadBytesInternal ((int) length); @@ -1439,7 +1455,7 @@ namespace WebSocketSharp ? BitConverter.GetBytes ((UInt32)(object) value) : type == typeof (UInt64) ? BitConverter.GetBytes ((UInt64)(object) value) - : new byte [] {}; + : new byte []{}; return buffer.Length <= 1 || order.IsHostOrder () ? buffer diff --git a/websocket-sharp/WebSocket.cs b/websocket-sharp/WebSocket.cs index 13628dd5..9e0699cb 100644 --- a/websocket-sharp/WebSocket.cs +++ b/websocket-sharp/WebSocket.cs @@ -916,8 +916,7 @@ namespace WebSocketSharp private bool processPingFrame (WsFrame frame) { - if (send (WsFrame.CreatePongFrame ( - _client ? Mask.MASK : Mask.UNMASK, frame.PayloadData).ToByteArray ())) + if (send (WsFrame.CreatePongFrame (_client ? Mask.MASK : Mask.UNMASK, frame.PayloadData))) _logger.Trace ("Returned Pong."); return true; @@ -1026,50 +1025,77 @@ namespace WebSocketSharp return false; } - return _stream.WriteFrame (frame); + return _stream.Write (frame.ToByteArray ()); + } + + private void send (Opcode opcode, byte [] data) + { + lock (_forSend) + { + try { + var comped = false; + if (_compression != CompressionMethod.NONE) + { + data = data.Compress (_compression); + comped = true; + } + + send (WsFrame.CreateFrame ( + Fin.FINAL, opcode, _client ? Mask.MASK : Mask.UNMASK, data, comped)); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occured."); + } + } } private void send (Opcode opcode, Stream stream) { - var data = stream; - var compressed = false; - try { - if (_readyState != WebSocketState.OPEN) - { - var msg = "A WebSocket connection isn't established or has been closed."; - _logger.Error (msg); - error (msg); + lock (_forSend) + { + var comp = stream; + var comped = false; + try { + if (_compression != CompressionMethod.NONE) + { + comp = stream.Compress (_compression); + comped = true; + } - return; + sendFragmented (opcode, comp, _client ? Mask.MASK : Mask.UNMASK, comped); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occured."); } + finally { + if (comped) + comp.Dispose (); - if (_compression != CompressionMethod.NONE) - { - data = data.Compress (_compression); - compressed = true; + stream.Dispose (); } + } + } - lock (_forSend) + private void sendAsync (Opcode opcode, byte [] data, Action completed) + { + Action sender = send; + AsyncCallback callback = ar => + { + try { + sender.EndInvoke (ar); + if (completed != null) + completed (); + } + catch (Exception ex) { - var mask = _client ? Mask.MASK : Mask.UNMASK; - var length = data.Length; - if (length <= FragmentLength) - send (WsFrame.CreateFrame ( - Fin.FINAL, opcode, mask, data.ReadBytes ((int) length), compressed)); - else - sendFragmented (opcode, data, mask, compressed); + _logger.Fatal (ex.ToString ()); + error ("An exception has occured."); } - } - catch (Exception ex) { - _logger.Fatal (ex.ToString ()); - error ("An exception has occured."); - } - finally { - if (compressed) - data.Dispose (); + }; - stream.Dispose (); - } + sender.BeginInvoke (opcode, data, callback, null); } private void sendAsync (Opcode opcode, Stream stream, Action completed) @@ -1092,55 +1118,75 @@ namespace WebSocketSharp sender.BeginInvoke (opcode, stream, callback, null); } - private long sendFragmented (Opcode opcode, Stream stream, Mask mask, bool compressed) + private bool sendFragmented (Opcode opcode, Stream stream, Mask mask, bool compressed) { - var length = stream.Length; - var quo = length / FragmentLength; - var rem = length % FragmentLength; - var count = rem == 0 ? quo - 2 : quo - 1; + var len = stream.Length; + if (sendFragmented (opcode, stream, len, mask, compressed) == len) + return true; + + var msg = "Sending fragmented data is interrupted."; + _logger.Error (msg); + error (msg); + close (CloseStatusCode.ABNORMAL, msg, false); - long readLen = 0; - int tmpLen = 0; + return false; + } + + private long sendFragmented ( + Opcode opcode, Stream stream, long length, Mask mask, bool compressed) + { + var quo = length / FragmentLength; + var rem = (int) (length % FragmentLength); + var count = rem == 0 ? quo - 2 : quo - 1; + + long sentLen = 0; + int readLen = 0; byte [] buffer = null; // Not fragmented if (quo == 0) { buffer = new byte [rem]; - tmpLen = stream.Read (buffer, 0, buffer.Length); - if (send (WsFrame.CreateFrame (Fin.FINAL, opcode, mask, buffer, compressed))) - readLen = tmpLen; + readLen = stream.Read (buffer, 0, rem); + if (readLen == rem && + send (WsFrame.CreateFrame (Fin.FINAL, opcode, mask, buffer, compressed))) + sentLen = readLen; - return readLen; + return sentLen; } buffer = new byte [FragmentLength]; // First - tmpLen = stream.Read (buffer, 0, FragmentLength); - if (send (WsFrame.CreateFrame (Fin.MORE, opcode, mask, buffer, compressed))) - readLen = tmpLen; + readLen = stream.Read (buffer, 0, FragmentLength); + if (readLen == FragmentLength && + send (WsFrame.CreateFrame (Fin.MORE, opcode, mask, buffer, compressed))) + sentLen = readLen; else - return 0; + return sentLen; // Mid for (long i = 0; i < count; i++) { - tmpLen = stream.Read (buffer, 0, FragmentLength); - if (send (WsFrame.CreateFrame (Fin.MORE, Opcode.CONT, mask, buffer, compressed))) - readLen += tmpLen; + readLen = stream.Read (buffer, 0, FragmentLength); + if (readLen == FragmentLength && + send (WsFrame.CreateFrame (Fin.MORE, Opcode.CONT, mask, buffer, compressed))) + sentLen += readLen; else - return readLen; + return sentLen; } // Final + var tmpLen = FragmentLength; if (rem != 0) - buffer = new byte [rem]; - tmpLen = stream.Read (buffer, 0, buffer.Length); - if (send (WsFrame.CreateFrame (Fin.FINAL, Opcode.CONT, mask, buffer, compressed))) - readLen += tmpLen; + buffer = new byte [tmpLen = rem]; - return readLen; + readLen = stream.Read (buffer, 0, tmpLen); + if (readLen == tmpLen && + send (WsFrame.CreateFrame (Fin.FINAL, Opcode.CONT, mask, buffer, compressed))) + sentLen += readLen; + + return sentLen; } // As client @@ -1291,51 +1337,51 @@ namespace WebSocketSharp // As server, used to broadcast internal void Send (Opcode opcode, byte [] data, Dictionary cache) { - try { - byte [] cached; - if (!cache.TryGetValue (_compression, out cached)) - { - cached = WsFrame.CreateFrame ( - Fin.FINAL, - opcode, - Mask.UNMASK, - data.Compress (_compression), - _compression != CompressionMethod.NONE).ToByteArray (); - - cache.Add (_compression, cached); - } + lock (_forSend) + { + try { + byte [] cached; + if (!cache.TryGetValue (_compression, out cached)) + { + cached = WsFrame.CreateFrame ( + Fin.FINAL, + opcode, + Mask.UNMASK, + data.Compress (_compression), + _compression != CompressionMethod.NONE).ToByteArray (); + + cache.Add (_compression, cached); + } - lock (_forSend) - { send (cached); } - } - catch (Exception ex) { - _logger.Fatal (ex.ToString ()); - error ("An exception has occured."); + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occured."); + } } } // As server, used to broadcast internal void Send (Opcode opcode, Stream stream, Dictionary cache) { - try { - Stream cached; - if (!cache.TryGetValue (_compression, out cached)) - { - cached = stream.Compress (_compression); - cache.Add (_compression, cached); - } + lock (_forSend) + { + try { + Stream cached; + if (!cache.TryGetValue (_compression, out cached)) + { + cached = stream.Compress (_compression); + cache.Add (_compression, cached); + } - lock (_forSend) - { cached.Position = 0; sendFragmented (opcode, cached, Mask.UNMASK, _compression != CompressionMethod.NONE); } - } - catch (Exception ex) { - _logger.Fatal (ex.ToString ()); - error ("An exception has occured."); + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + error ("An exception has occured."); + } } } @@ -1544,17 +1590,19 @@ namespace WebSocketSharp /// public void Send (byte[] data) { - if (data == null) + var msg = _readyState.CheckIfOpen () ?? data.CheckIfValidSendData (); + if (msg != null) { - var msg = "'data' must not be null."; _logger.Error (msg); error (msg); return; } - var stream = new MemoryStream (data); - send (Opcode.BINARY, stream); + if (data.LongLength <= FragmentLength) + send (Opcode.BINARY, data); + else + send (Opcode.BINARY, new MemoryStream (data)); } /// @@ -1565,17 +1613,20 @@ namespace WebSocketSharp /// public void Send (string data) { - if (data == null) + var msg = _readyState.CheckIfOpen () ?? data.CheckIfValidSendData (); + if (msg != null) { - var msg = "'data' must not be null."; _logger.Error (msg); error (msg); return; } - var stream = new MemoryStream (Encoding.UTF8.GetBytes (data)); - send (Opcode.TEXT, stream); + var rawData = Encoding.UTF8.GetBytes (data); + if (rawData.LongLength <= FragmentLength) + send (Opcode.TEXT, rawData); + else + send (Opcode.TEXT, new MemoryStream (rawData)); } /// @@ -1586,9 +1637,11 @@ namespace WebSocketSharp /// public void Send (FileInfo file) { - if (file == null) + var msg = _readyState.CheckIfOpen () ?? + (file == null ? "'file' must not be null." : null); + + if (msg != null) { - var msg = "'file' must not be null."; _logger.Error (msg); error (msg); @@ -1598,6 +1651,50 @@ namespace WebSocketSharp send (Opcode.BINARY, file.OpenRead ()); } + /// + /// Sends a binary data from the specified using the WebSocket connection. + /// + /// + /// A object from which contains a binary data to send. + /// + /// + /// An that contains the number of bytes to send. + /// + /// + /// true if is disposed after a binary data read; + /// otherwise, false. + /// + public void Send (Stream stream, int length, bool dispose) + { + byte [] data = null; + int readLen = 0; + var msg = _readyState.CheckIfOpen () ?? + stream.CheckIfCanRead () ?? + (length < 1 ? "'length' must be greater than 0." : null) ?? + ((readLen = (data = stream.ReadBytesInternal (length)).Length) == 0 + ? "A data cannot be read from 'stream'." : null); + + if (msg != null) + { + _logger.Error (msg); + error (msg); + + return; + } + + if (readLen != length) + _logger.Warn (String.Format ( + "A data with 'length' cannot be read from 'stream'.\nexpected: {0} actual: {1}", length, readLen)); + + if (dispose) + stream.Dispose (); + + if (readLen <= FragmentLength) + send (Opcode.BINARY, data); + else + send (Opcode.BINARY, new MemoryStream (data)); + } + /// /// Sends a binary asynchronously using the WebSocket connection. /// @@ -1610,17 +1707,19 @@ namespace WebSocketSharp /// public void SendAsync (byte [] data, Action completed) { - if (data == null) + var msg = _readyState.CheckIfOpen () ?? data.CheckIfValidSendData (); + if (msg != null) { - var msg = "'data' must not be null."; _logger.Error (msg); error (msg); return; } - var stream = new MemoryStream (data); - sendAsync (Opcode.BINARY, stream, completed); + if (data.LongLength <= FragmentLength) + sendAsync (Opcode.BINARY, data, completed); + else + sendAsync (Opcode.BINARY, new MemoryStream (data), completed); } /// @@ -1635,17 +1734,20 @@ namespace WebSocketSharp /// public void SendAsync (string data, Action completed) { - if (data == null) + var msg = _readyState.CheckIfOpen () ?? data.CheckIfValidSendData (); + if (msg != null) { - var msg = "'data' must not be null."; _logger.Error (msg); error (msg); return; } - var stream = new MemoryStream (Encoding.UTF8.GetBytes (data)); - sendAsync (Opcode.TEXT, stream, completed); + var rawData = Encoding.UTF8.GetBytes (data); + if (rawData.LongLength <= FragmentLength) + sendAsync (Opcode.TEXT, rawData, completed); + else + sendAsync (Opcode.TEXT, new MemoryStream (rawData), completed); } /// @@ -1660,9 +1762,11 @@ namespace WebSocketSharp /// public void SendAsync (FileInfo file, Action completed) { - if (file == null) + var msg = _readyState.CheckIfOpen () ?? + (file == null ? "'file' must not be null." : null); + + if (msg != null) { - var msg = "'file' must not be null."; _logger.Error (msg); error (msg); @@ -1672,6 +1776,55 @@ namespace WebSocketSharp sendAsync (Opcode.BINARY, file.OpenRead (), completed); } + /// + /// Sends a binary data asynchronously from the specified + /// using the WebSocket connection. + /// + /// + /// A object from which contains a binary data to send. + /// + /// + /// An that contains the number of bytes to send. + /// + /// + /// true if is disposed after a binary data read; + /// otherwise, false. + /// + /// + /// An delegate that references the method(s) called when + /// the asynchronous operation completes. + /// + public void SendAsync (Stream stream, int length, bool dispose, Action completed) + { + byte [] data = null; + int readLen = 0; + var msg = _readyState.CheckIfOpen () ?? + stream.CheckIfCanRead () ?? + (length < 1 ? "'length' must be greater than 0." : null) ?? + ((readLen = (data = stream.ReadBytesInternal (length)).Length) == 0 + ? "A data cannot be read from 'stream'." : null); + + if (msg != null) + { + _logger.Error (msg); + error (msg); + + return; + } + + if (readLen != length) + _logger.Warn (String.Format ( + "A data with 'length' cannot be read from 'stream'.\nexpected: {0} actual: {1}", length, readLen)); + + if (dispose) + stream.Dispose (); + + if (readLen <= FragmentLength) + sendAsync (Opcode.BINARY, data, completed); + else + sendAsync (Opcode.BINARY, new MemoryStream (data), completed); + } + /// /// Sets a used in the WebSocket opening handshake. ///