diff --git a/.vs/websocket-sharp/v16/Server/sqlite3/db.lock b/.vs/websocket-sharp/v16/Server/sqlite3/db.lock new file mode 100644 index 000000000..e69de29bb diff --git a/.vs/websocket-sharp/v16/Server/sqlite3/storage.ide b/.vs/websocket-sharp/v16/Server/sqlite3/storage.ide new file mode 100644 index 000000000..b34dca128 Binary files /dev/null and b/.vs/websocket-sharp/v16/Server/sqlite3/storage.ide differ diff --git a/Example/Example.csproj b/Example/Example.csproj index 38c5b4200..9b4dea019 100644 --- a/Example/Example.csproj +++ b/Example/Example.csproj @@ -1,5 +1,5 @@ - + Debug AnyCPU @@ -10,6 +10,11 @@ Example example v3.5 + + + + + 3.5 true diff --git a/Example1/Example1.csproj b/Example1/Example1.csproj index 81c52eff2..b2ae026e8 100644 --- a/Example1/Example1.csproj +++ b/Example1/Example1.csproj @@ -1,5 +1,5 @@ - + Debug AnyCPU @@ -10,6 +10,11 @@ Example example1 v3.5 + + + + + 3.5 true diff --git a/Example2/Example2.csproj b/Example2/Example2.csproj index 685a1ef6d..5531ca3a6 100644 --- a/Example2/Example2.csproj +++ b/Example2/Example2.csproj @@ -1,5 +1,5 @@ - + Debug AnyCPU @@ -10,6 +10,11 @@ Example2 example2 v3.5 + + + + + 3.5 true diff --git a/Example3/Example3.csproj b/Example3/Example3.csproj index ce4fe265c..0fa630233 100644 --- a/Example3/Example3.csproj +++ b/Example3/Example3.csproj @@ -1,5 +1,5 @@ - + Debug AnyCPU @@ -10,6 +10,11 @@ Example3 example3 v3.5 + + + + + 3.5 true diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..222b0f385 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +ROOT:=$(shell pwd) +PROJECT=websocket-sharp +BUILDDIR=build + +CONFIGURATION=Debug +VERSION=$(shell cat VERSION) +REVISION=$(shell git rev-parse --short HEAD) + +build: clean app + +clean: + echo $(ROOT) + rm -rf $(ROOT)/build + rm -rf $(ROOT)/*/bin/ + rm -rf $(ROOT)/*/obj/ + +app: + msbuild $(ROOT)/$(PROJECT).sln /t:$(PROJECT) /p:Configuration="$(CONFIGURATION)" /p:Platform="Any CPU" /p:BuildProjectReferences=false diff --git a/websocket-sharp.sln b/websocket-sharp.sln index 3c20e06a0..dd0e0f06c 100644 --- a/websocket-sharp.sln +++ b/websocket-sharp.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28803.156 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "websocket-sharp", "websocket-sharp\websocket-sharp.csproj", "{B357BAC7-529E-4D81-A0D2-71041B19C8DE}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{52805AEC-EFB1-4F42-BB8E-3ED4E692C568}" @@ -13,28 +15,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example3", "Example3\Exampl EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU Debug_Ubuntu|Any CPU = Debug_Ubuntu|Any CPU + Debug|Any CPU = Debug|Any CPU Release_Ubuntu|Any CPU = Release_Ubuntu|Any CPU + Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Release|Any CPU.Build.0 = Release|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug|Any CPU.Build.0 = Debug|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release|Any CPU.ActiveCfg = Release|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release|Any CPU.Build.0 = Release|Any CPU {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -43,6 +29,22 @@ Global {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.Build.0 = Release|Any CPU + {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU + {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU + {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU + {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU + {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release|Any CPU.Build.0 = Release|Any CPU + {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU + {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU + {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {390E2568-57B7-4D17-91E5-C29336368CCF}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU + {390E2568-57B7-4D17-91E5-C29336368CCF}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU + {390E2568-57B7-4D17-91E5-C29336368CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {390E2568-57B7-4D17-91E5-C29336368CCF}.Release|Any CPU.Build.0 = Release|Any CPU {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -60,6 +62,12 @@ Global {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release|Any CPU.ActiveCfg = Release|Any CPU {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B41BCBD5-A9D2-4829-8E87-56AAD2CD7923} + EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = websocket-sharp\websocket-sharp.csproj Policies = $0 diff --git a/websocket-sharp/ThrottledStream.cs b/websocket-sharp/ThrottledStream.cs new file mode 100644 index 000000000..c2ef7f412 --- /dev/null +++ b/websocket-sharp/ThrottledStream.cs @@ -0,0 +1,137 @@ +using System; +using System.IO; +using System.Threading; + +namespace WebSocketSharp { + + public class ThrottledStream : Stream { + + /// + /// Actual base stream the data is passed to + /// + private Stream _objStream; + + /// + /// Maximum number of bytes per second that's being allowed + /// 0 = infinite + /// + private long _lngMaxBytesPerSecond = 0; + + /// + /// Global byte counter used to calculate the current speed (in combination with _lngStartTime) + /// + private long _lngByteCount; + + /// + /// Start time the current speed is being calculated on (in combination with _lngByteCount) + /// + private long _lngStartTime; + + #region Constructors + /// + /// Creates the stream without any bandwidth throttling + /// + /// + public ThrottledStream(Stream pStream) : this(pStream, 0) { } + + /// + /// Creates the stream with the given maximum bytes per second + /// + /// + /// + public ThrottledStream(Stream pStream, long pBytesPerSecond) { + _objStream = pStream; + _lngMaxBytesPerSecond = pBytesPerSecond; + _lngByteCount = 0; + _lngStartTime = Environment.TickCount; + } + #endregion + + #region Public Properties + /// + /// Maximum number of bytes per second allowed to be sent + /// + public long MaxBytesPerSecond { + get { return _lngMaxBytesPerSecond; } + set { + if (value < 0) { _lngMaxBytesPerSecond = 0; } + _lngMaxBytesPerSecond = value; + Reset(); // -- reset current counter + } + } + + /// + /// Current bytes per second being sent over the stream + /// + public long CurrentBytesPerSecond { + get { return (_lngByteCount * 1000L) / (Environment.TickCount - _lngStartTime); } + } + #endregion + + #region Stream Overrides + public override bool CanRead { get { return _objStream.CanRead; } } + public override bool CanSeek { get { return _objStream.CanSeek; } } + public override bool CanWrite { get { return _objStream.CanWrite; } } + public override long Length { get { return _objStream.Length; } } + public override void Flush() { _objStream.Flush(); } + public override long Position { + get { return _objStream.Position; } + set { _objStream.Position = value; } + } + public override long Seek(long pOffset, SeekOrigin pOrigin) { + return _objStream.Seek(pOffset, pOrigin); + } + public override void SetLength(long value) { + _objStream.SetLength(value); + } + public override string ToString() { + return _objStream.ToString(); + } + public override int Read(byte[] pBuffer, int pOffset, int pCount) { + WaitIfNeeded(pCount); + return _objStream.Read(pBuffer, pOffset, pCount); + } + public override void Write(byte[] buffer, int offset, int count) { + WaitIfNeeded(count); + _objStream.Write(buffer, offset, count); + } + #endregion + + /// + /// Slows down sending the bytes if needed + /// + /// + private void WaitIfNeeded(int pByteCount) { + if(_lngMaxBytesPerSecond == 0) { return; } // -- no limit + if (pByteCount == 0) { return; } // -- nothing to write, so no waiting + + //update global byte counter + _lngByteCount += pByteCount; + + var lngTimePassed = Environment.TickCount - _lngStartTime; + if (lngTimePassed > 0) { + var lngCurrentSpeed = _lngByteCount * 1000L / lngTimePassed; + if (lngCurrentSpeed > _lngMaxBytesPerSecond) { // -- do we need to wait? + var intMilisecondsToSleep = ((_lngByteCount * 1000L / _lngMaxBytesPerSecond) - lngTimePassed); + if (intMilisecondsToSleep > 1) { + try { + Thread.Sleep((int)intMilisecondsToSleep); + } catch (Exception) { } // can happen when threads get killed/aborted + Reset(); + } + } + } + } + + /// + /// Reset the timer being used to throttle the speed + /// + protected void Reset() { + //To better shape the stream, keep 10 second history + if ((Environment.TickCount - _lngStartTime) > 10000) { + _lngByteCount = 0; + _lngStartTime = Environment.TickCount; + } + } + } +} \ No newline at end of file diff --git a/websocket-sharp/WebSocket.cs b/websocket-sharp/WebSocket.cs index 93ed5bf4e..041abf487 100644 --- a/websocket-sharp/WebSocket.cs +++ b/websocket-sharp/WebSocket.cs @@ -114,7 +114,7 @@ public class WebSocket : IDisposable private int _retryCountForConnect; private bool _secure; private ClientSslConfiguration _sslConfig; - private Stream _stream; + private ThrottledStream _stream; private TcpClient _tcpClient; private Uri _uri; private const string _version = "13"; @@ -141,7 +141,7 @@ public class WebSocket : IDisposable /// Int32.MaxValue - 14 inclusive. /// /// - internal static readonly int FragmentLength; + public static int FragmentLength { get; set; } /// /// Represents the random number generator used internally. @@ -174,7 +174,7 @@ internal WebSocket (HttpListenerWebSocketContext context, string protocol) _logger = context.Log; _message = messages; _secure = context.IsSecureConnection; - _stream = context.Stream; + _stream = new ThrottledStream(context.Stream, MaxBytesPerSecond); _waitTime = TimeSpan.FromSeconds (1); init (); @@ -190,7 +190,7 @@ internal WebSocket (TcpListenerWebSocketContext context, string protocol) _logger = context.Log; _message = messages; _secure = context.IsSecureConnection; - _stream = context.Stream; + _stream = new ThrottledStream(context.Stream, MaxBytesPerSecond); _waitTime = TimeSpan.FromSeconds (1); init (); @@ -774,6 +774,24 @@ public TimeSpan WaitTime { } } + /// + /// Gets or sets the maxiumum bytes per second + /// + /// + /// long representing the maximum number of bytes/second + /// 0 or lower is infinite + /// + public long MaxBytesPerSecond { get; set; } = 0; + + public long CurrentBytesPerSecond { + get { + if (_stream != null) { + return _stream.CurrentBytesPerSecond; + } + return 0; + } + } + #endregion #region Public Events @@ -2094,7 +2112,7 @@ private void sendProxyConnectRequest () if (res.HasConnectionClose) { releaseClientResources (); _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); + _stream = new ThrottledStream(_tcpClient.GetStream (), MaxBytesPerSecond); } var authRes = new AuthenticationResponse (authChal, _proxyCredentials, 0); @@ -2116,12 +2134,12 @@ private void setClientStream () { if (_proxyUri != null) { _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); + _stream = new ThrottledStream(_tcpClient.GetStream (), MaxBytesPerSecond); sendProxyConnectRequest (); } else { _tcpClient = new TcpClient (_uri.DnsSafeHost, _uri.Port); - _stream = _tcpClient.GetStream (); + _stream = new ThrottledStream(_tcpClient.GetStream (), MaxBytesPerSecond); } if (_secure) { @@ -2144,7 +2162,7 @@ private void setClientStream () conf.EnabledSslProtocols, conf.CheckCertificateRevocation); - _stream = sslStream; + _stream = new ThrottledStream(sslStream, MaxBytesPerSecond); } catch (Exception ex) { throw new WebSocketException (CloseStatusCode.TlsHandshakeFailure, ex); diff --git a/websocket-sharp/websocket-sharp.csproj b/websocket-sharp/websocket-sharp.csproj index 0860c0313..27c4e6db1 100644 --- a/websocket-sharp/websocket-sharp.csproj +++ b/websocket-sharp/websocket-sharp.csproj @@ -1,5 +1,5 @@ - + Debug AnyCPU @@ -12,6 +12,11 @@ v3.5 true websocket-sharp.snk + + + + + 3.5 true @@ -106,6 +111,7 @@ + @@ -146,4 +152,4 @@ - \ No newline at end of file +