From 386643129bccb47c4b904377a046285144f389b0 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 27 Dec 2012 01:09:07 +0800 Subject: [PATCH 01/62] removed the files which will not be released in v1.5 --- Facility/HttpBase/HttpReceiveFilterBase.cs | 115 --------------------- Facility/HttpBase/HttpRequestInfoBase.cs | 71 ------------- Facility/HttpBase/MimeHeaderHelper.cs | 85 --------------- Facility/SuperSocket.Facility.Net35.csproj | 3 - Facility/SuperSocket.Facility.Net40.csproj | 3 - Facility/SuperSocket.Facility.Net45.csproj | 3 - 6 files changed, 280 deletions(-) delete mode 100644 Facility/HttpBase/HttpReceiveFilterBase.cs delete mode 100644 Facility/HttpBase/HttpRequestInfoBase.cs delete mode 100644 Facility/HttpBase/MimeHeaderHelper.cs diff --git a/Facility/HttpBase/HttpReceiveFilterBase.cs b/Facility/HttpBase/HttpReceiveFilterBase.cs deleted file mode 100644 index feb1f7469..000000000 --- a/Facility/HttpBase/HttpReceiveFilterBase.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Linq; -using System.Text; -using SuperSocket.Common; -using SuperSocket.SocketBase; -using SuperSocket.SocketBase.Protocol; - -namespace SuperSocket.Facility.HttpBase -{ - /// - /// HttpReceiveFilterBase - /// - /// The type of the request info. - public abstract class HttpReceiveFilterBase : TerminatorReceiveFilter - where TRequestInfo : IRequestInfo - { - /// - /// Http header terminator - /// - private static readonly byte[] NewLine = Encoding.ASCII.GetBytes("\r\n\r\n"); - - /// - /// indicate whether the header has been parsed - /// - private bool m_HeaderParsed = false; - - /// - /// Gets the header items. - /// - protected NameValueCollection HeaderItems { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - protected HttpReceiveFilterBase(IAppSession session) - : base(session, NewLine) - { - - } - - /// - /// Filters the specified session. - /// - /// The read buffer. - /// The offset. - /// The length. - /// if set to true [to be copied]. - /// The rest. - /// - public override TRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest) - { - if (!m_HeaderParsed) - { - return base.Filter(readBuffer, offset, length, toBeCopied, out rest); - } - else - { - return FilterRequestBody(readBuffer, offset, length, toBeCopied, out rest); - } - } - - /// - /// Filters the request body. - /// - /// The read buffer. - /// The offset. - /// The length. - /// if set to true [to be copied]. - /// The rest data size. - /// - protected abstract TRequestInfo FilterRequestBody(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest); - - /// - /// Resolves the specified data. - /// - /// The data. - /// The offset. - /// The length. - /// - protected override TRequestInfo ProcessMatchedRequest(byte[] data, int offset, int length) - { - string header = Encoding.UTF8.GetString(data, offset, length); - - var headerItems = new NameValueCollection(); - MimeHeaderHelper.ParseHttpHeader(header, headerItems); - HeaderItems = headerItems; - - OnHeaderParsed(headerItems); - - return NullRequestInfo; - } - - /// - /// Called when [header parsed]. - /// - /// The header. - protected virtual void OnHeaderParsed(NameValueCollection header) - { - - } - - /// - /// Resets this instance to inital state. - /// - public override void Reset() - { - m_HeaderParsed = false; - HeaderItems = null; - base.Reset(); - } - } -} diff --git a/Facility/HttpBase/HttpRequestInfoBase.cs b/Facility/HttpBase/HttpRequestInfoBase.cs deleted file mode 100644 index 2e72ed886..000000000 --- a/Facility/HttpBase/HttpRequestInfoBase.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using SuperSocket.SocketBase.Protocol; -using System.Collections.Specialized; - -namespace SuperSocket.Facility.HttpBase -{ - /// - /// IHttpRequestInfo - /// - public interface IHttpRequestInfo : IRequestInfo - { - /// - /// Gets the http header. - /// - NameValueCollection Header { get; } - } - - /// - /// HttpRequestInfoBase - /// - public abstract class HttpRequestInfoBase : IHttpRequestInfo - { - /// - /// Gets the key of this request. - /// - public string Key { get; private set; } - - /// - /// Gets the http header. - /// - public NameValueCollection Header { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The header. - protected HttpRequestInfoBase(string key, NameValueCollection header) - { - Key = key; - Header = header; - } - } - - /// - /// HttpRequestInfoBase - /// - /// The type of the request body. - public abstract class HttpRequestInfoBase : HttpRequestInfoBase - { - /// - /// Gets the body. - /// - public TRequestBody Body { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The header. - /// The body. - protected HttpRequestInfoBase(string key, NameValueCollection header, TRequestBody body) - : base(key, header) - { - Body = body; - } - } -} diff --git a/Facility/HttpBase/MimeHeaderHelper.cs b/Facility/HttpBase/MimeHeaderHelper.cs deleted file mode 100644 index 63be598bc..000000000 --- a/Facility/HttpBase/MimeHeaderHelper.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Linq; -using System.Text; - -namespace SuperSocket.Facility.HttpBase -{ - /// - /// MimeHeader Helper class - /// - public static class MimeHeaderHelper - { - private const string Tab = "\t"; - private const char Colon = ':'; - private const string Space = " "; - private const string ValueSeparator = ", "; - - /// - /// Parses the HTTP header. - /// - /// The header data. - /// The header. - public static void ParseHttpHeader(string headerData, NameValueCollection header) - { - string line; - string firstLine = string.Empty; - string prevKey = string.Empty; - - var reader = new StringReader(headerData); - - while (!string.IsNullOrEmpty(line = reader.ReadLine())) - { - if (string.IsNullOrEmpty(firstLine)) - { - firstLine = line; - continue; - } - - if (line.StartsWith(Tab) && !string.IsNullOrEmpty(prevKey)) - { - string currentValue = header[prevKey]; - header[prevKey] = currentValue + line.Trim(); - continue; - } - - int pos = line.IndexOf(Colon); - - if (pos <= 0) - continue; - - string key = line.Substring(0, pos); - - if (!string.IsNullOrEmpty(key)) - key = key.Trim(); - - var valueOffset = pos + 1; - - if (line.Length <= valueOffset) //No value in this line - continue; - - string value = line.Substring(valueOffset); - if (!string.IsNullOrEmpty(value) && value.StartsWith(Space) && value.Length > 1) - value = value.Substring(1); - - if (string.IsNullOrEmpty(key)) - continue; - - string oldValue = header[key]; - - if (string.IsNullOrEmpty(oldValue)) - { - header.Add(key, value); - } - else - { - header[key] = oldValue + ValueSeparator + value; - } - - prevKey = key; - } - } - } -} diff --git a/Facility/SuperSocket.Facility.Net35.csproj b/Facility/SuperSocket.Facility.Net35.csproj index 3da787a8d..194c35d55 100644 --- a/Facility/SuperSocket.Facility.Net35.csproj +++ b/Facility/SuperSocket.Facility.Net35.csproj @@ -55,9 +55,6 @@ GlobalAssemblyInfo.cs - - - diff --git a/Facility/SuperSocket.Facility.Net40.csproj b/Facility/SuperSocket.Facility.Net40.csproj index e3c962ee8..2d95084f6 100644 --- a/Facility/SuperSocket.Facility.Net40.csproj +++ b/Facility/SuperSocket.Facility.Net40.csproj @@ -51,9 +51,6 @@ GlobalAssemblyInfo.cs - - - diff --git a/Facility/SuperSocket.Facility.Net45.csproj b/Facility/SuperSocket.Facility.Net45.csproj index 067a87a45..12e76901e 100644 --- a/Facility/SuperSocket.Facility.Net45.csproj +++ b/Facility/SuperSocket.Facility.Net45.csproj @@ -54,9 +54,6 @@ GlobalAssemblyInfo.cs - - - From d606acba15ee1f9c0fe407343f56d1568985059d Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 27 Dec 2012 01:09:33 +0800 Subject: [PATCH 02/62] updated the push script --- Push.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Push.bat b/Push.bat index 655830d92..0cca31270 100644 --- a/Push.bat +++ b/Push.bat @@ -1,3 +1,3 @@ -git push origin -git push github +git push origin v1.5:v1.5 +git push github v1.5:v1.5 pause \ No newline at end of file From d68bc03445c0528bc2c9f9ad8ecec815e2236fc3 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 27 Dec 2012 01:09:49 +0800 Subject: [PATCH 03/62] updated the assembly version --- Solution Items/GlobalAssemblyInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Solution Items/GlobalAssemblyInfo.cs b/Solution Items/GlobalAssemblyInfo.cs index 25576a039..82d7a5916 100644 --- a/Solution Items/GlobalAssemblyInfo.cs +++ b/Solution Items/GlobalAssemblyInfo.cs @@ -8,8 +8,8 @@ // Build Number // Revision // -[assembly: AssemblyVersion("1.5.0.6")] -[assembly: AssemblyFileVersion("1.5.0.6")] +[assembly: AssemblyVersion("1.5.0.7")] +[assembly: AssemblyFileVersion("1.5.0.7")] [assembly: AssemblyCompany("SuperSocket")] [assembly: AssemblyProduct("SuperSocket")] [assembly: AssemblyCopyright("Copyright © supersocket.codeplex.com 2012")] From ffbcd43cd91c3d5b55022e440dc1451a61a7e969 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 27 Dec 2012 18:07:25 +0800 Subject: [PATCH 04/62] updated the copyright year in the assembly info --- Solution Items/GlobalAssemblyInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Solution Items/GlobalAssemblyInfo.cs b/Solution Items/GlobalAssemblyInfo.cs index 82d7a5916..eb5dd8474 100644 --- a/Solution Items/GlobalAssemblyInfo.cs +++ b/Solution Items/GlobalAssemblyInfo.cs @@ -12,6 +12,6 @@ [assembly: AssemblyFileVersion("1.5.0.7")] [assembly: AssemblyCompany("SuperSocket")] [assembly: AssemblyProduct("SuperSocket")] -[assembly: AssemblyCopyright("Copyright © supersocket.codeplex.com 2012")] +[assembly: AssemblyCopyright("Copyright © supersocket.codeplex.com 2010-2013")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] \ No newline at end of file From 0b5bb78c1f93d3213361b8f476fa729f72f4975f Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 27 Dec 2012 23:37:40 +0800 Subject: [PATCH 05/62] removed the useless code in the test cases --- Test/SocketServerTest.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Test/SocketServerTest.cs b/Test/SocketServerTest.cs index cc9e4f1ed..4024987de 100644 --- a/Test/SocketServerTest.cs +++ b/Test/SocketServerTest.cs @@ -706,10 +706,6 @@ public void TestFastSending() EndPoint serverAddress = new IPEndPoint(IPAddress.Parse("127.0.0.1"), m_Config.Port); - string[] source = SEND.GetStringSource(); - - string[] received = new string[source.Length]; - using (Socket socket = CreateClientSocket()) { socket.Connect(serverAddress); From 3c1c9d4c28013316e214c959e0571d2b05e0a171 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Fri, 28 Dec 2012 00:40:34 +0800 Subject: [PATCH 06/62] added the missed TestFixture attribute in some test cases --- Test/Protocol/CountSpliterProtocolTest.cs | 2 ++ Test/Protocol/TerminatorProtocolTest.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Test/Protocol/CountSpliterProtocolTest.cs b/Test/Protocol/CountSpliterProtocolTest.cs index 24d2411cc..05e04c805 100644 --- a/Test/Protocol/CountSpliterProtocolTest.cs +++ b/Test/Protocol/CountSpliterProtocolTest.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using NUnit.Framework; using SuperSocket.Facility.Protocol; using SuperSocket.SocketBase.Protocol; namespace SuperSocket.Test.Protocol { + [TestFixture] public class CountSpliterProtocolTest : ProtocolTestBase { class TestReceiveFilter : CountSpliterReceiveFilter diff --git a/Test/Protocol/TerminatorProtocolTest.cs b/Test/Protocol/TerminatorProtocolTest.cs index c96e66e60..37e5da6ab 100644 --- a/Test/Protocol/TerminatorProtocolTest.cs +++ b/Test/Protocol/TerminatorProtocolTest.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using NUnit.Framework; using SuperSocket.SocketBase.Protocol; namespace SuperSocket.Test.Protocol { + [TestFixture] public class TerminatorProtocolTest : ProtocolTestBase { private readonly Encoding m_Encoding = new ASCIIEncoding(); From fa92f40286a8a04c40f4893c6d35058ff5616032 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Sun, 30 Dec 2012 16:04:09 +0800 Subject: [PATCH 07/62] improved the test case TestMaxConnection --- Test/SocketServerTest.cs | 62 +++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/Test/SocketServerTest.cs b/Test/SocketServerTest.cs index 4024987de..5fe1f043c 100644 --- a/Test/SocketServerTest.cs +++ b/Test/SocketServerTest.cs @@ -6,6 +6,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; +using System.Threading.Tasks; using NUnit.Framework; using SuperSocket.Common; using SuperSocket.SocketBase; @@ -241,6 +242,36 @@ public void TestSessionConnectedState() } } + private bool TryConnect(EndPoint serverAddress) + { + var task = Task.Factory.StartNew(() => + { + using (Socket trySocket = CreateClientSocket()) + { + trySocket.Connect(serverAddress); + var innerSocketStream = GetSocketStream(trySocket); + innerSocketStream.ReadTimeout = 500; + + using (var tryReader = new StreamReader(innerSocketStream, m_Encoding, true)) + { + return tryReader.ReadLine() != null; + } + } + }); + + try + { + if (!task.Wait(1500)) + return false; + } + catch + { + return false; + } + + return task.Result; + } + private bool TestMaxConnectionNumber(int maxConnectionNumber) { var defaultConfig = DefaultServerConfig; @@ -277,31 +308,22 @@ private bool TestMaxConnectionNumber(int maxConnectionNumber) sockets.Add(socket); } - try - { - using (Socket trySocket = CreateClientSocket()) - { - trySocket.Connect(serverAddress); - var innerSocketStream = new NetworkStream(trySocket); - innerSocketStream.ReadTimeout = 500; + Assert.AreEqual(maxConnectionNumber, server.SessionCount); - using (StreamReader tryReader = new StreamReader(innerSocketStream, m_Encoding, true)) - { - string welcome = tryReader.ReadLine(); - Console.WriteLine(welcome); - return true; - } - } - } - catch (Exception) - { - return true; - } + Assert.IsFalse(TryConnect(serverAddress)); + + sockets[0].SafeClose(); + + Thread.Sleep(500); + + Assert.AreEqual(maxConnectionNumber - 1, server.SessionCount); + + return TryConnect(serverAddress); } catch (Exception e) { Console.WriteLine(e.Message + " " + e.StackTrace); - return false; + throw e; } finally { From 12229a8d014502393a9fb59831322426786d110e Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Wed, 2 Jan 2013 23:18:46 +0800 Subject: [PATCH 08/62] removed the useless file RunServer.bat --- SocketService/RunServer.bat | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 SocketService/RunServer.bat diff --git a/SocketService/RunServer.bat b/SocketService/RunServer.bat deleted file mode 100644 index 8099737ec..000000000 --- a/SocketService/RunServer.bat +++ /dev/null @@ -1,2 +0,0 @@ -SuperSocket.SocketService.exe -c -pause \ No newline at end of file From c878a4c441e39ba864008e46f2cc812b34eeddc3 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 3 Jan 2013 19:12:40 +0800 Subject: [PATCH 09/62] updated README.TXT --- README.TXT | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.TXT b/README.TXT index 27c35d788..b9621d5f4 100644 --- a/README.TXT +++ b/README.TXT @@ -3,7 +3,7 @@ SuperSocket SuperSocket is a light weight extensible socket application framework. You can use it to build a command based server side socket application easily without thinking about how to use socket, -how to maintain the socket connections and how socket works(synchronize/asynchronize). +how to maintain the socket connections and how socket works. It is a pure C# project which is designed to be extended, so it is easy to be integrated to your existing system. As long as your systems (like forum/CRM/MIS/HRM/ERP) are developed in .NET language, @@ -13,7 +13,7 @@ you must be able to use SuperSocket to build your socket application as a part o Project homepage: http://supersocket.codeplex.com/ Git URL: https://git01.codeplex.com/supersocket Author email address: kerry-jiang@hotmail.com - +Releases download: http://supersocket.codeplex.com/releases/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at From 08ea506f9068757b9a88b73db35a1b6c77f48233 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Tue, 8 Jan 2013 18:06:52 +0800 Subject: [PATCH 10/62] updated the assembly version to 1.5.1 --- Solution Items/GlobalAssemblyInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Solution Items/GlobalAssemblyInfo.cs b/Solution Items/GlobalAssemblyInfo.cs index eb5dd8474..1a62a24a8 100644 --- a/Solution Items/GlobalAssemblyInfo.cs +++ b/Solution Items/GlobalAssemblyInfo.cs @@ -8,8 +8,8 @@ // Build Number // Revision // -[assembly: AssemblyVersion("1.5.0.7")] -[assembly: AssemblyFileVersion("1.5.0.7")] +[assembly: AssemblyVersion("1.5.1.0")] +[assembly: AssemblyFileVersion("1.5.1.0")] [assembly: AssemblyCompany("SuperSocket")] [assembly: AssemblyProduct("SuperSocket")] [assembly: AssemblyCopyright("Copyright © supersocket.codeplex.com 2010-2013")] From eb93c1503933d9dc0910cf80955330c027fd166c Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 3 Jan 2013 19:12:27 +0800 Subject: [PATCH 11/62] updated README.TXT --- README.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.TXT b/README.TXT index b9621d5f4..b1939afe1 100644 --- a/README.TXT +++ b/README.TXT @@ -25,4 +25,4 @@ Unless required by applicable law or agreed to in writing, software distributed See the License for the specific language governing permissions and limitations under the License. -Copyright 2012 Kerry Jiang (kerry-jiang@hotmail.com) \ No newline at end of file +Copyright 2010-2013 Kerry Jiang (kerry-jiang@hotmail.com) \ No newline at end of file From 3bfe6feb8870622c30af4bbca64968b1ebcafe39 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Tue, 8 Jan 2013 18:02:04 +0800 Subject: [PATCH 12/62] improved the code to allow accessing socket session from appSession when create new receive filter in receive filter factory --- SocketBase/AppServerBase.cs | 2 +- SocketBase/AppSession.cs | 8 ++++---- SocketBase/IAppSession.cs | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/SocketBase/AppServerBase.cs b/SocketBase/AppServerBase.cs index 439808b28..4188032d7 100644 --- a/SocketBase/AppServerBase.cs +++ b/SocketBase/AppServerBase.cs @@ -1240,7 +1240,7 @@ IAppSession IAppServer.CreateAppSession(ISocketSession socketSession) if (!RegisterSession(socketSession.SessionID, appSession)) return NullAppSession; - appSession.Initialize(this, socketSession, ReceiveFilterFactory.CreateFilter(this, appSession, socketSession.RemoteEndPoint)); + appSession.Initialize(this, socketSession); socketSession.Closed += OnSocketSessionClosed; if (Config.LogBasicSessionActivity && Logger.IsInfoEnabled) diff --git a/SocketBase/AppSession.cs b/SocketBase/AppSession.cs index 5e3e01b5b..efc31c61b 100644 --- a/SocketBase/AppSession.cs +++ b/SocketBase/AppSession.cs @@ -181,14 +181,14 @@ public AppSession() /// /// The app server. /// The socket session. - /// The receive filter. - public virtual void Initialize(IAppServer appServer, ISocketSession socketSession, IReceiveFilter receiveFilter) + public virtual void Initialize(IAppServer appServer, ISocketSession socketSession) { - AppServer = (AppServerBase)appServer; + var castedAppServer = (AppServerBase)appServer; + AppServer = castedAppServer; SocketSession = socketSession; SessionID = socketSession.SessionID; m_Connected = true; - m_ReceiveFilter = receiveFilter; + m_ReceiveFilter = castedAppServer.ReceiveFilterFactory.CreateFilter(appServer, this, socketSession.RemoteEndPoint); OnInit(); } diff --git a/SocketBase/IAppSession.cs b/SocketBase/IAppSession.cs index 11543479c..a08a757c5 100644 --- a/SocketBase/IAppSession.cs +++ b/SocketBase/IAppSession.cs @@ -130,7 +130,6 @@ public interface IAppSession : IAppSession /// /// The server. /// The socket session. - /// The Receive filter. - void Initialize(IAppServer server, ISocketSession socketSession, IReceiveFilter requestFilter); + void Initialize(IAppServer server, ISocketSession socketSession); } } From ae1886a6c46b5f06911833d2fd424bbb049d3c14 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Tue, 8 Jan 2013 18:07:46 +0800 Subject: [PATCH 13/62] added test cases for FixedHeaderReceiveFilter --- Test/Protocol/FixedHeaderProtocolTest.cs | 47 ++++++++++++++++++++++++ Test/SuperSocket.Test.NET35.csproj | 1 + Test/SuperSocket.Test.Net40.csproj | 1 + Test/SuperSocket.Test.Net45.csproj | 1 + 4 files changed, 50 insertions(+) create mode 100644 Test/Protocol/FixedHeaderProtocolTest.cs diff --git a/Test/Protocol/FixedHeaderProtocolTest.cs b/Test/Protocol/FixedHeaderProtocolTest.cs new file mode 100644 index 000000000..93d86747d --- /dev/null +++ b/Test/Protocol/FixedHeaderProtocolTest.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using SuperSocket.Facility.Protocol; +using SuperSocket.SocketBase.Protocol; + +namespace SuperSocket.Test.Protocol +{ + [TestFixture] + public class FixedHeaderProtocolTest : ProtocolTestBase + { + class TestReceiveFilter : FixedHeaderReceiveFilter + { + public TestReceiveFilter() + : base(8) + { + + } + + protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length) + { + var strLen = Encoding.ASCII.GetString(header, offset + 4, 4); + return int.Parse(strLen.TrimStart('0')); + } + + protected override StringRequestInfo ResolveRequestInfo(ArraySegment header, byte[] bodyBuffer, int offset, int length) + { + var body = Encoding.ASCII.GetString(bodyBuffer, offset, length); + return new StringRequestInfo(Encoding.ASCII.GetString(header.Array, header.Offset, 4), body, new string[] { body }); + } + } + + private readonly Encoding m_Encoding = new ASCIIEncoding(); + + protected override IReceiveFilterFactory CurrentReceiveFilterFactory + { + get { return new DefaultReceiveFilterFactory(); } + } + + protected override string CreateRequest(string sourceLine) + { + return string.Format("ECHO{0}{1}", sourceLine.Length.ToString().PadLeft(4), sourceLine); + } + } +} diff --git a/Test/SuperSocket.Test.NET35.csproj b/Test/SuperSocket.Test.NET35.csproj index ffd49c69c..5bc19b167 100644 --- a/Test/SuperSocket.Test.NET35.csproj +++ b/Test/SuperSocket.Test.NET35.csproj @@ -94,6 +94,7 @@ + diff --git a/Test/SuperSocket.Test.Net40.csproj b/Test/SuperSocket.Test.Net40.csproj index 7b30331be..7c7331538 100644 --- a/Test/SuperSocket.Test.Net40.csproj +++ b/Test/SuperSocket.Test.Net40.csproj @@ -81,6 +81,7 @@ + diff --git a/Test/SuperSocket.Test.Net45.csproj b/Test/SuperSocket.Test.Net45.csproj index 763f61d13..3ef660f21 100644 --- a/Test/SuperSocket.Test.Net45.csproj +++ b/Test/SuperSocket.Test.Net45.csproj @@ -83,6 +83,7 @@ + From 256df9adb2385a89c2413f9b279a8167268a1cb5 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 10 Jan 2013 18:22:10 +0800 Subject: [PATCH 14/62] fixed stopping listening issue --- SocketEngine/SocketServerBase.cs | 11 ----------- SocketEngine/TcpAsyncSocketListener.cs | 9 +++++++-- SocketEngine/UdpSocketListener.cs | 9 +++++++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/SocketEngine/SocketServerBase.cs b/SocketEngine/SocketServerBase.cs index a076c6b4d..e8edc8593 100644 --- a/SocketEngine/SocketServerBase.cs +++ b/SocketEngine/SocketServerBase.cs @@ -110,17 +110,6 @@ void OnListenerError(ISocketListener listener, Exception e) if(!logger.IsErrorEnabled) return; - if (e is ObjectDisposedException || e is NullReferenceException) - return; - - var socketException = e as SocketException; - - if (socketException != null) - { - if (socketException.ErrorCode == 995 || socketException.ErrorCode == 10004 || socketException.ErrorCode == 10038) - return; - } - logger.ErrorFormat(string.Format("Listener ({0}) error: {1}", listener.EndPoint, e.Message), e); } diff --git a/SocketEngine/TcpAsyncSocketListener.cs b/SocketEngine/TcpAsyncSocketListener.cs index 719e21b6e..985a05946 100644 --- a/SocketEngine/TcpAsyncSocketListener.cs +++ b/SocketEngine/TcpAsyncSocketListener.cs @@ -68,8 +68,13 @@ void ProcessAccept(SocketAsyncEventArgs e) { if (e.SocketError != SocketError.Success) { - OnError(new SocketException((int)e.SocketError)); - return; + var errorCode = (int)e.SocketError; + + //The listen socket was closed + if (errorCode == 995 || errorCode == 10004 || errorCode == 10038) + return; + + OnError(new SocketException(errorCode)); } OnNewClientAccepted(e.AcceptSocket, null); diff --git a/SocketEngine/UdpSocketListener.cs b/SocketEngine/UdpSocketListener.cs index 889bf4d5a..94e30a7f1 100644 --- a/SocketEngine/UdpSocketListener.cs +++ b/SocketEngine/UdpSocketListener.cs @@ -69,8 +69,13 @@ void eventArgs_Completed(object sender, SocketAsyncEventArgs e) { if (e.SocketError != SocketError.Success) { - OnError(new SocketException((int)e.SocketError)); - return; + var errorCode = (int)e.SocketError; + + //The listen socket was closed + if (errorCode == 995 || errorCode == 10004 || errorCode == 10038) + return; + + OnError(new SocketException(errorCode)); } if (e.LastOperation == SocketAsyncOperation.ReceiveFrom) From d839f40212f2603c85ec09ddd287453a3e840a19 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Fri, 11 Jan 2013 17:36:06 +0800 Subject: [PATCH 15/62] dont't fire OnAcceptNewClient event if a socket error received --- SocketEngine/TcpAsyncSocketListener.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SocketEngine/TcpAsyncSocketListener.cs b/SocketEngine/TcpAsyncSocketListener.cs index 985a05946..b8239aaa2 100644 --- a/SocketEngine/TcpAsyncSocketListener.cs +++ b/SocketEngine/TcpAsyncSocketListener.cs @@ -76,8 +76,10 @@ void ProcessAccept(SocketAsyncEventArgs e) OnError(new SocketException(errorCode)); } - - OnNewClientAccepted(e.AcceptSocket, null); + else + { + OnNewClientAccepted(e.AcceptSocket, null); + } e.AcceptSocket = null; From 5101ce3e59fced5b98755961f1cf4618ed08d1fc Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Mon, 14 Jan 2013 18:30:18 +0800 Subject: [PATCH 16/62] made the ServerPush sample workable --- QuickStart/ServerPush/PushServer.cs | 2 +- QuickStart/ServerPush/ServerPush.csproj | 15 ++++++++++-- .../SuperSocket.SocketService.exe.config | 23 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 QuickStart/ServerPush/SuperSocket.SocketService.exe.config diff --git a/QuickStart/ServerPush/PushServer.cs b/QuickStart/ServerPush/PushServer.cs index 1d44d1bed..a5f1523fc 100644 --- a/QuickStart/ServerPush/PushServer.cs +++ b/QuickStart/ServerPush/PushServer.cs @@ -11,7 +11,7 @@ public class PushServer : AppServer { private Timer m_PushTimer; - private int m_Interval = 60 * 1000 * 5; //5 minutes + private int m_Interval = 60 * 1000; //1 minute protected override void OnStartup() { diff --git a/QuickStart/ServerPush/ServerPush.csproj b/QuickStart/ServerPush/ServerPush.csproj index c4153745e..da1679858 100644 --- a/QuickStart/ServerPush/ServerPush.csproj +++ b/QuickStart/ServerPush/ServerPush.csproj @@ -34,8 +34,6 @@ - - @@ -50,6 +48,19 @@ {40b77789-ea11-4c05-8f52-86711d7bcaaf} SuperSocket.SocketBase.Net40 + + {153fef72-191c-43d9-be71-2b351c7ac760} + SuperSocket.SocketEngine.Net40 + + + {b9113694-7226-4152-938d-3172b11571a1} + SuperSocket.SocketService.Net40 + + + + + PreserveNewest + W{(rx;?Q{EfgJM95~Mw^+NsCPm|;yr7aM!m)RR<%ao z2KHMk8GV>Sa(-lOR*2;M$l4)G20rXwBtEiUR(T}#$JWOx@2l>0=Es(OC-wJX3XS0t zE5c}bz~5si&|rnS_b3scTH_TOio8#)N`=aM7%6|WY7{!Vhaaeg(T6EC_5;=ml@~+3 z1J)XqM_T&K+NScFk@uOkU7<^R+-7`ky`j)GJr+5iTl-Z#(%=`?VU@QUd0$w8yW|*1 zpI=%2@#X6DTk~Eg~p|IP5H)Jtx?aEZ>?<#9qQ3D05pFvoa65#wWb(*EAZR z;@IyqBHubWC18J{^M;C4`)?YJPf4=__mE_=WXR5CB%{gH5v-^hAK(>QI`FsCojuBtkL+Cu${6cDOrx4$4JI*d3Fzt#;4@l z$9qb4v4`uti0EpM)2PnuYR5DhDk64^L;36xqS8Jnw-gdJY9DP>5D>cYClxw2Hgi!xj2H>h-WI721P( zJ?sl4!fZxFPrFH@p+HxvlBCaG_Bx%1PlnqY+o3J(&;zO-t(9K(%LJ<_F?bEh#NnQ{A?I{ukruHfk$Jz6AUId7K zsZx4w7IsInMqi=gAmlfW1YRED{6kH#F)k2HN(osUF$qw}IpB6E*sC zV2C}Q(bIz3Ji(r&^R^f#+N~O0nljX0r_og@!|a`&dZ*eW?st1wkuuz#pwU$+BkVOA z-I!8pf2q;hlrsDL-?;TQq>Qt-GrBP_Fz>FE)9tqvI+4*Kg@!ZAd4THO7#PE7uta!I zxfS)MsJyfD9!NREu2E=Q2B9kzn$39|6slviT_U`vd<-S`YV>r<1lxX)`x7hkUjT|Q zlAdUy-Am@-UF%&b6YY@-k<=6I^A%c}_byPALO;&?0O)FkZpiy1&<2Ip=6wb9h(Z@; z{uSs|g?^rQ80dgPTk?$5iMIWa)WkD+exQg%cxxE~I#Hp0dD*Fx>Y4URg|hQ|r%tsuD-_8;F15nmq0k_pO8aevhUS-~PP6~4(aEW2 z*#!?voej?)2{c%v@u_Fql?shR-r07IM&nb@u~#T`R{mt5jT)VuI^BL=p;-O}K>IbC zlX|WlctrNsm|qK2q|hb#O{wSE!z996%ZpOax2LGQTk_k0<|*_>{uQY+>^6mrf@@PR zuvaToSglMB|Q&a!tZG^${IYSiAV(A0uEQ)k=XFp?Hhjm=R?_HDP1QmgF< zBbgVodujAVYRn!c5iI%Z)H(LYvL4>l-<2}gwjWdD3$~@swId2WhB3^uM=JCJ#t^s9 zQRpL}`S!&M9m>Bpb%DK7p>&L|#(qGdC81TRwf4&j4ZvvY>_0J*a@O02WnN%^uLn}< z?V`t7mH@4sdV9D+f9mzO)OvfCLYch@EmNouqm2rc^)}M#?d=i?vYvW-uSO@QHrV!d zIa;!w2D_I=<5L&fV;D)v7TOav8Y&jrGd0RiYqVP=3T*3LBAVgQI{Nw zyoWT(PHVPb_0(HzAMn&$Y}-!={3m-~YzH;kVl1``HHwH9`-kmNv>j^H=$Nz?`(}-{ z7_IhQ9(1Ao2qSq;y~uu5Av&l2$bL_uK%eeum)M_c)HiLZeORNxY0K=Be@jx|C<1*> zNxRZML!(h?KelISbVk}W_F9e3O1sX!N26J3H`qHgT99_5Z9M6=*^+j%ov%?_+FE;{ zM$6M~vqx+6leBgABt}w$ci891yg*5xwP|eQpPOK%+a;*4wXn>TR$;&}fUX z!Tz&EVtSwZ(td9D-9eHq2YN8=7xp}bs{1^Vw#i=0NJgrg?fYaN$1j`hhufhi+o9iS zG*oQ1seSh^hwfI0{&FZH?$L<;awv%oX6#DaYQN2`$X)wh`(s8o1}?$a@3s9;QLER9 zpY(YHsDP22_j~QZ3emi8vqv&oDc1FQJMDgZqsqI#&xdIb+Xpn-6?oMCt4436J!S`< zrXFq-52NH`_5_V~1-9E~YxG9i6ZSlXUh457&}NNx1)j8jqtP2_JM5hbZAY6s?BFx9 zhwXiK1)jF^HF_iM8M}`{WUbHGCur0+?OA)IMuXFyw=dJ^l(d)aRT_;-+huRm=!~@2 z>_2LBR@$5P-!+<*_LiNwQ_2$PvmosqyF#OuwD;|}Mr~;y*q3OuJnbWUl}0~F``BKm z(73eE(mu8KYxHf}=eBrO_AoBZ7yQaD(kLT%&>pE#B=}c*mPW`slTQoW? z_)q(Jjm8BH-vNbQ=rbi~`U;+t{k_oVyr9oFQlsji-#1UAj9|cbrACop(6>>e;$Vhv zr$!~gEZ-rGP7CJv`u@)CVO%iJSE{t(wzNLJA852Zt*>u_Lf50se!kfnwWSU4wP>_F zZIJI;g+4-?$NM&D)Rs2H_mD=*(@ylgtk4&IE)Nd%eW=kj!C}6`8r={)#TR}-%K2B6 zJk{4%qicdAd?PfvAvn@ksnC@Lmj_FIF^#SXmiZQGbVG2g?+S&C;>&~Me5*COCV0AU zlSVfLC-@#xC{VmQIMMgAM(cu8d>?A`i{Mn>VU6wzR`|j%N~r_I4+PKh_0{Of;5oh# z3aw0kE->9US)+r_`My~i?FwArTd2_s!I{2GHQF7V?YlvvJ;7?<28GIweJ&95J*3e= zXP)n6jdlg*`##j@h2R3;VU2bN>wMvtq|{}{?g=*d`YP0=__@GB-^m&sbeeppYqTq{ z$akJX#VEPhw@{;l&V{}!G};xo*tb@pVa5A`Kl0t9(PzP>z9%&LI=IaDibjWn%YC0~ z*!5e&WjSdG_`L5E)7h3Jxq){+* zv+r4jW}wYmd~a#=S@1UBXBvGSyuJi%I+o4h4 z(5t?^8kL0J@Ez7@Sm-TZ!7H-&t=S_&dweHqG%oa>Z;D2fLi>F+8dZiq^j)dZjL@gP z4H{L4KJ#tYXnyDm-`g5BhYtD@uN6u^HXQ2W?AEAjsL=VVMm<7B zPQhzZ&h%sZhI%+BYg7{I<3h(fk7_h2G{kvFqsq{U&ObGp z5gO(c?RNXC#%G;MHJTqf&6%xHbEwq0Ors^CGG~KEFNDTAPieF}bh`7QMtedN9slcY z5BoxsoxU0!2vs=c8XXLs<;>UU(a<^0QjMMto$svHXpb|~xksVfiXRO{ohLMUIuvtW z(P)n|&-p;1U!vrE=deakhw7Z{H>700EZ*ZRbowiFZ*e%3aK>uXHMH27tx=EAh0YR< zMuaYQZq{gAsLi=wqe-F5oLw6A4PEYhu2D(oDkt?#+55f4!$Ma(eKe{JUF(d}Xh!II z=K_tYL#vz%HJTr~$+=OZ=Flz9y&5eE-Riuk(Z0|+=TnUigf=+NAKcy#hJNYv(CCHG zCTE03yF+(7=W4Vkw8dGZ(Kn%cohvo6)9-g~)+m(zptD(_hoH}got+wW4L$Dc*QiJ6 zw~lyA%JNY0h|mrvqS3g}PG^`#lS039DmCgGdckSXs3i1z=W30Hg%@4in6uj-q(j407oT$-~(1*?xjrN5;c4{;_5IW#osnNmE7tRKa zUI=~VY}aUa=xgU~jrN2NIfpd*CiJb7y2tI^PXC7!(I}K|_y;NUIP_`xPuHkx$nT${ zQIAln|5A-cgo6HCH5wPn^gpE0q)@hhw?=(Kx&A+CR1)gqPk%?s@;K}>;_t6fW$0M{ zSdC_cdiZB+R2}N=U!u|cP_h4JjhaKp`R~_gNobINmqzdH!=W>KmHjZ_=nF6!ovrXjrJ)zgeTo z&>a6xjb?=A`}b>99jf(<_XPgCQM_0@KUD9JXw)1^_=jntDx6-cQ`>e?CgR zyXaMk#GQj8;#NO>(=L5mjDhcSqi>Hc0i|z9Bf6gXo0x`~v&EB^d`TZ>{yFK?!VFM+ zBbFy^^(W3n$GFtQH;HvD<1H`y^0t4pTq$`jw=Zjr=CtH*U`dWfC!wS%rf~VGOsS+P zWICEbY1yV6o#e~*y*iO;DsQOr-=EW2w#A*=p;<8TeRp(QvlY}9_k;TI{bn6|?A)4^ zsx$Q;M{TBq;@jRhO&t1;GNQ+FTF%-Z3W(F4InpX*u1w1^-uaS!_3KKtI?L0Uh9e8v{!#A#7LMyJ9~(dtJ`IX*mE<&WEX9%QB`Nd2lK%tLwejtdWR0J5 zn}200*S?%Pd@-1wuT)M;i%mz`65Dzub$C4I(l=rm!fVyUhbQFnvp6>?p(W;nV+)d` zy|foGpJbDKL)^-B-vu?r2c6PVTCZJo=6G|Z)mek6eJ}qQ<_rZj#TZT}r8U}FrrfP$ zJCb_ak(|OjYNrQi`(8SGD!DFlJ>!+~e;+Dq9gTjU7XGVcqz~~@Zwua0OFtlKQzq#_ zQfaH-O{Gp`dFhwDEy%QY?><`EyR&$AAIbM>UdrROfA6@yJAE|$|9f-Vxv%e|?amh1 znL{m`_(sr^*={4@44ZaJB@R6HizKU$2JoI6W2A5XG%=6G$%o0jna zQ4`<(N_9K0FF9%{gUtP}a=Z~;=k_~uI+y=$jyKmkI&a!r=D#c5Inu++U}&-!)DYfu zQe&2&eaBY&PDj4ilY7(usr7$X8{{$Ec*Z+l9r=bxa=hvG^OY+t~|-|7JD0;S48`| zUY#GgJid{=L*&!Bh0Yvz?8!FUm-*i;vz$lihm&bns~tJ+NRxbbeyN3jRV&7RtcGne znRaV+@29S}-)<}6`NE@!%eshp9cdH&>1l3- z_xeiifZuOVyn{zA?QNS!1D$QMGe_={-~CqTXquFj3hc<(`|Q_mFGH>oNYT zoPSS;$r<_IH(z9LCPy{xcejpwgMX_*mZaYgI&!U!d~ZoNb|U?|wj`q|IU|(*eq}oL z;#zn|{{Kwx-)BYq-z@VSti62%mE;(L(vtq)D)UMwd6FDMP+HRV@^miqR;PB7979mr zJ-2!-YBbIlvSpboOSbRpf3BTm%dUkdIj+oJPeqcu*4vTa{^Te(z|KzwgtT(|%9?_uBdXE6&^ge`@_l8%Yg# z&uHzVChvKTY{pyCoBmI&|7gRj0dM+0w(%crcs1Zn|Hn4|qYbYHyy^dnZ6x=y_O&|l zy)l>}_VJnPvmq9KL-ri}n}^>_y-0j)T#SFE27O0SIny&i7dg|JMwt_5x{zrr)1^$W zVtO6ZpMh>MZU>FvS7{}^y5HM@5`%utVv}(TJ{GXaIF$c(`X=M-ymVue@qY1pNN?+R zkm)x}MaEX+oqnMV;%8@U67(yjXNm#+&&hbgIHmt>#f}qf5NEjf4R{rX7#@) zAPzxcwY4HYn0%e@DI>F z1FWot=J5jppeGMV2OTva8}#%61)voJ3bRVgKyeSy^9B%|Tv(jdYAzTsAnRoCPXRq| z0MW^XKV*Jc)`R#a=c%AHzYmJ~0p}pSWWX%WjU#;xbIw1G`0EBVWKBoirO4epU^(a( z&@6CPbNRJdXCl3sbGLHt)~xr8$%RjH?#?Wl-=U(!+&18Kw6<-)+h}dufcMeDwgDfZ z?zRC3P=4EhOESAdp1)@;g>EC5FQQvT&cL?=TSd=-w;42pc9>{dc#}~wun6f>2lht# z{o+YslI?uZEudGKR}5?ffBnFV!em!pq+Vx|g=`hu2VM%!u7N*B?jHuO1pRIHFTziV zKMs5l^v?qy50iC0AHLlryMm1k`XcpqvwF~9z)1``95yh^X7=soGNiY%oMhGgvP%W6 zivj5C1&78P5f5_in_};vTzt{z*MlNCZ;EdR^~$04Pe7W=ya!rmzQ<+WacAudLlQfC>`;oN62M;B#m6$1tr z<#w^2%J_YTVJsOiHaEp89Xu5jwgI|i!0g;sar)pNj(b|oSO#UpL;d`0{cqHuqU^lb@$+Xxg{w7N$$yzCn8Fb zPR%wB6hTp*qh&F>b%M9XNX%OdXqt*v%5`TqB{FNs0SDVB? z#Qej|7Z&k*S*yh$F3RiY651)>k=c_JM& zTYQ1suAJLbq#jch8tr_UyB_qIJS%;)GbL|5=rMUua(W-9t1?DA_vGyZ{Y#$6e9IW0 zX=PHnC+IPGqd}+SJ;|JXoVKz!7c?_}G^eYWUIyAfe?92@{3n^SkEs>r@}SG|M@yRh zmT@ruvh2~$;|1$MKP=b>IwjA_A=;DaXr@)5p)Qw!{v~f8Q!970)7m8^_bp>nK~JRD zb*Tb9CU0NvPwcf~S>!Hjt=P|WR^PXbpJum#Zp|+G0i{QRZq2U#A(dG+lIVs~qB}-$ zt8)g#SF^hAfMC~e~ zy{d_p#fa`;+G_!GYKT_XGM{O61ErTOB)XxI`3a^?%x@-Iwuq>`nCSM!q`keMTeJ5w z$8IHluM3H`UHFy}DOd-3a=~`ca|^^pZyC)6BSEh%D7%Qt%)00VKU%w(Yb_xvE};>% zUB;SRMx3(cME9>C+IAh4U&nMiQ~P@2ue+Z3+nMfV+Uo}5FJoG?vL`HmB-2?;+nBCn zx}9m!OzHn5YTrWqk+%?k7SlGS zz19$a8Phg=C<(QgZdgnF?VKKYE7xM$#&pN6l)K|LqHXIaciB2hujBM~rXz3XT1=~N z=aMpg2c>(hXU=-=k?D4(`>Z*T-X*$Y zFVX$)5w-Ub?X{n1*#|_cKP0;BBcdBVCc5JjqWeE3Y9Aol>ocNdpA)VAg6Og@iEj9c z=#D=TrT?ZgJ(uZuOwVUJgXsn0qx1vmfsDe8-Wh{3hGvY&D9boKV{%4C#swL%jGBx^ z8EqLq$yk|jbH*<+9>{n+lQK`s9F;jcvo^CSb6Mt^%sVsh z$=sRwa^~L5k2C+2S&~(rwJ7WAtUI!Pll4N@S6Sik!0@neX}BUhI~)%;hi?hr9o`mx zD*Q_L&G1LzV0Kn^x9q{$Kgb@QJt}*A_N45}?CIGTWLIa;&#upI%5KeGl6_hBRoTyF zznr~0`>pJ~*&k&8G5f3Rzh?hE+saAF>7P@YGcjjI&VroQoVJ`Ra<0u;lXFMT{W)Lf z9FyBW_tf0d+`8P$a(|k;D)-^s6Z3wUH$QK2-rBtNd3WYLoA*-Qt9ftc?a%u(@5{V1 z^Uuz|ApfrXE%^`U@5p~4|MmR$@*gZXvCC;)CU!Zy%d9R9U7qRke3#d{{Gp45JI7S~ zomQG~M3(T20_@dYaGp8_oUS4yA|hQJ3r>GGz3dJd+n>bD2`{eOKsBz*4;&gG6 zm?Tcd8E=BP0I8YcT%7gJ6LE3AsKu|-H{e(38%4Ef5;1WhaxMbrV$h}Ha&alXEo`~C z0{>rwUpK#2TrGZz)JpsX^eX%|_*!wZ*eGrhn-H7a1GE)i9)6#=TWrI;k4#?zIwAd1 z&?!u(F&&t37@YGm!uTlG?HN-+XJ-BybQRNEGIx5?yOF*(ljy*V^GqSOhKWAF^eLv# zG2O-Vb*6ime!%oIrr$Chm=Qx=x3vqAZp2+O5o^?#0>O$J{a{fK3q~`xU+2q{GSzDEPwNHqFZ4QF#Uk(XH36kx-y++d1u$>QEO+{Sq{-{paV0AA1Nd{xbPuxe12-Vv~WAp zzb?$gUz0Z;OWKh0bzTvrtC%ieTG0KyfDk>plT`h>e~9#{-Kor(Ol943yHoqKnRB%A zzwSY`p6ZdF;`S(8c&P`~{XQ)mt;gb?$+5_pm6CggQn`Xk#ksZ`t08^^YKjNpRZa0e{3>qJKuz&CaVqFx_+ChE{1CLKQ3~42 z7!BIn7z5hJ7zbL6(;fb5#yA7CzcCT?IAb#C0Nibv;&`J1bg(fEw8S_Ybcit>^aSHP z&=ZXrphJzBpg%C8pl29WpfimaB$>r@wlNo+DAOt-Z zy3V);^mgN?pm!M8fvz`h0Nr4$0)5c93G@TwXP_S%w}Ac?CstE@YupBU80UXe{M}d& z`VZshp#L;}2`bD@poV!DsA=8}YMJ+d+U8bJpLrjs-~2Ubck?%(z03zedz%kK8+|~b zZSzs2`!elsJ`PSlrpK92fIk4#6a&pCL8InVpuaMo0slVpS)}h}`mXsqaQ1?lxLGzrLH(vs0AJc5>_ekehuYl%SuYu-SuY=}WZ-N$BZ-I8P_JAH^y$jmSdJnY7+K1M< zgW~Untq+jy35rozA0geFX|eSQIDJ43{Ibm-k?za1zx5e7$1xpfeF6Rerh}}nz&{=o zR%9JSx&#zfWPOeF381DJX8jfPWa}HyQ>?#%o@)Iabh!0T(9O>nN^JRGEh@o zVf6#O1-}JviZ#|i(6#urcUUQY*B$nW-%y8r;`hp7pVmpB>#dVPH(IBHZn91Tz0>+3 z=v`JR=w@p)=-t*B&|g{OK<}~2LAO|EfNr%WfY|y>d zbkO&#^FZIXW`KTb%>@0U6$Sm=ssjDOih%~~xuD0`al^t5540>^v`q13dS+(7%p36he4g{S94j|1_r=`Zx$k$e+vrnLKK$QSjIBzXlHSsfILZI; zO({V_?Rm?Nz&ind(?XwOvfy!51&A2WMZ|cnm<693#lPA3SA~Bu#Ex_D zZ!Z3=6gMCeJcXKo7k-Z0KTkbpsAp6?SE=Vs>bXfh?^4el>iM*KKChmys;AG8?fca; zO+81Ol0Qa0XQ<~=^}Jj?uTsxz)bl#^Ji(InPgc(v>KRqfn0m(5vsOLdRL^W%*3VVX z=yJgiL@D)o%1 z=Unytk$PUDo=es9r|Nl~dfuR(Mv7`*J$>pKQqK(a%u>&8>e*dAd#Y!NdY+)3L-8DJ zFU%N%e}nKZjDI=!Hz%+#!^Xcnu`uIeJbxg%WAE*byU*^}ce}&dx?|_BgyYp#no4Vu#!t&J_NZ(J~_x~4{yPmLw&YZk>u*ViSQ8l&;LCNXIm6&tIIC9@JD znHJOP60xSqv**XEn#8DhU9_>)lUZ6Ulgs0)ioV4 zyP|nE#(%`D$&J;q#+X+Dqw8xMqK)xHyJ3!Nj76Jbjg|AF-obmglN&n@eL^ge=uqBe zbuL_5)da;a0{KdlbUO4ATiBcr##YZ$5tRsV^iYnc+)&@({1v9XQywVp*ji=E!pK*%pbb)*r{v3LaJ0lw5rod=b%|or%sa| zHJDUimjq;RI;SF5Ge>$$NUOOtaT=61!Ek3cHziw;L(~PNqtp;?Ql}?3>SS`fV_F&- zF>mmXqoN7#@=aza=UahUj@HC4BzsNP7w6R093=-0#;ZIfX-RruWn;6qow3oHgcnbW zPVyAi%F5DzUwwp7Fq0MO)js&#^W2(IzOJH;_rLK{L0!t_fbgzD|_YH)G@z z>g(o8Tv=ZO$A)!{-7h9Ig5|ibxU@RjKubY#VvQ~?i_M8P*I@1ai-NOCIyW}99_z!c zhXU*`<4vt@+b-_d=TZ1a>cVY`w`sQ>xoLN7h1NuMyb6v#seLqA5o?;(FgD)Oggsf$ zPseRvNWTnU2M30R>e-PciznBVDl#@+gROu|m&FoQjqwKB){_;=F~x8cV_K?WoFgh@ zElus6lB|we<6|}GPL$VG)ihVfP=y}Jrl!LC6O}r}Jl;mh-C3`EMTUX!c>0N1eVzjAho{;K~ zRqNu(rewV&?jUMKGq#LmZf9{xGv0hX<|K1!4B-^4v9!86?j2}-?QF!3@ab?~$;Xq?B! zUZcFznxd)K(WP2Qa5fwWiP}U}ePd00wy0=LASw~+v@oWwxmHZXiiKeFR^jm=iZ^KNZoE7<)-r^9jwZF z>2<|SB^+&%qP}6~sCW~vhq`JHn%qcWLj7Fmu?F#uAlVR7@RLFpPM}oj9NOo;<6JkW z^D%Ict26Ilsc~)?jn>5GG>OLe+<8r6j`w`Ghz=YxX9^0W!~`ldl?#>Av|~%A5tS!6 zbQIO~(l=G{o(UOg1kxLkBNo#V@YF|`^+DrebrjVmXTRM}2~gf?FYLsjQu=%;QwPP}<#)cTHgXf#e z?T{TPII0yN6w#=rY4RB&wh&}4r4akc?HfDcqWT5UTWO-Tt_m}Q#UnU98b{#SbaERN zn;WlF#m3ZCt5khM0%Ezxw}hy{f}7HumOsykrO>FLuEi82B%4^2H8s}mfZqX@m&N&p05=St zvB(esqpOKG$VHP-ej`y9pED=cI1_;mA0u5ngm5fc>tfFL;31e#_Y#5p4I1dc7$Vpi zB^zi5hwxY?)sKcmoTP)X+aNEScpdYn)nHa=+*4xMLtsIaE{nEG=6LKZGBq(?hogi{ zSHv)l)h@pVCh1{R#;X?4-3$euSVU^YN;Sj~0#mHRzD_E{n;})=$%jI``Eaj#2&$1J z$6K&dCaEw1hf#qYNDI*JCTLu&i4`c1*SZ^}!FJkXmAUF-RY^6uDst5`G44@~R15Z) z7|!^yxZHQO$`eOA#Gnvjz*x2SU?WNq!qJr%979#S7Ed*?Y$n+JC&cSw6Jm99;onKV_8jl_ zLj~n70|D#!kS?J<7D(tuhEi_XLVb07P8_z>ag&k^PsyY;jH!v$!kTf@&`!oN6DE{T zsVJ`ym~K)9qLs!NrlSc*I-J|aB&wo(N7K|;Uo(1M6t<6o?0n1NF=4!=32C++`R&aH zg<~y()}AU&Nq1y0L%Gq4vGKa0UBW@qv4zcH zbhNI%jy7AEG^WnIEAb@N9SH>-y4D;Vj1nTzJi9uM!Rfxr>*mx8TCjCZ;0cZ|bSCdR z<#06WZe?;EUCQXn@mNBdNL92M8*nn-A&KQkle~^tMNGUA&%w=>XsTz^awCXI65@u0 z*>pRi@?lQyL6AAh3aL{S@i4m_#@3kZpR(z4f%E8EffIb8z$siM@KfH_a#CKCcrPxP z-QhAro~{u#$y-R_xjOZx)y*i%l{QU?VFN@|BCk<(!dqlwJxY+FPM(8p*S#C`BzZj} z)PnJ!-$%MWF#kEE( zfr(KWsNvp&s>;lus;VGk4$%+<@a?E723kk)3mE8Kf-i(oN8Bd6m^m^C>-J7(*=|<6P~3iR!v); z%=9uy{}hh8707pbG3B?5cG&q-=<3#RTY&p?d(o9VJQ*c%=~)elY_44*9@J7YE*p#e{G-d6%n>@-n{WyT{S< zVpR)dN7#o{d7fIijgk$y2O_SJWUH%4fmuQ{z}J%MltJ&3)Op(73S{`#E^KOYrNSWv z8m*%^M+J#>Dm!p!QaRo+R8WA5+noyB#IuyDIX$ZWj7s z2kT)e=PD&r8xk!ySEOXx%|ysr-=sGz1UT;A#ToLhgjDEc+=yf zb=5VVE6K7kV@sz^sGK>WeA1Yi6UIy$S2!0t? z1rBB6lSBM zC`28LOB?6nf`|?fvNyiukOE2DKsV}I{fJ!NBPoaOBsGwpOkH>I$xca`><+;?-KV%3 zPIl9Y!>#J(-KOPJHH+IrH42<^sM(mZY~3tdQYS7q?Q-1^3R`y7JT8xW06C_3!tFNh z9gUn7?oj&Ljw(AMS8K@~soSx)w96yavfpBXs6>0@UG|i#^+Xk=(9+geZcnn>lU7>n zC{MB{)zIkD$}!_6Pc0u)!PcT)n2_t78pE5QI$Q?Dq{8s@N(Sh@U7;fSJmYEMy8O|y46NjoSXKRad}M$8DZ!Ny3|gn)2+JuqgmuEYF`7l4CiQWW6bmRMlDY-XKK5A zSAVfN>a#ugkb!zr!wMUR8KGBf)kG4>w=q>5e?gN1z7>BbheS1{rbZXz_|+s29nyu$ zG~OEF(i1@>sYI_YMwJ+Ftch+xF&mhv_PH1xFVgm0l*Z9sy^-%!2U{b3OaT|gM{Bur zt4D9qZ7sp?k=RmXl61}^kS~+;3YCy^Jvm;k4BF+}A`-L|mkUkgP;n;2M}P3Sp(fZ% zteWr6>5@a>eb>Auz6WT8?a6mV%KI{|KxuVWbtevrcV8;eH1bP#zOlgsVeKWW@nkA% z=6iCav0pUu9g%!dB(Kk9TD64mgu^9ROd=sD@}f%m#j3z6#j8wp*5y}n*hctJiJLU~ zhyoo-dH3ht#ZA%e0~k1Q5{E`im;bn+)1N|;yHR^a@=(ubd~UH5&yC6Gwg88fgy*wF zvdtu~Bj(A(i0OQ-f-z32dknsva4#~{4F&Vuw5!(SZ2?7tbQgi^g__ngV(fvEuf;qW z{IcHtu!^q1pS-BRzLKmndO`&S$u;q)_hq5m5-MOp%sgNp63Oo+bKLc-clSPgMq8TZ;`A9mETDwCpB_}jFEnGGcX8^7L=FK<+Jh$I9oHlr98q6i&bQ$2L#o(6jc#tF zyGwyL`U0L(z zO$##Vq}|Qhbw(~Gx#gi*>5Jdk%MjdsZ(A=vsfy1F%&W(%x~O_>hBr{SQiB$8ns6_5 zro>_kaJI(hV)5aEnoVjod;A(4~Ps}@&g8VK+TKjb3K*y>PUh&&$2!^>NHYWuWDA- z%N8U9Eiwa7a(HuabBWO-K$|B@XD3J{-g{U;xF;ANzry49)#~!q^GSKr2HO2#^XipHt$Q-WGNU(> z7&h6td|RQn+wz3GT9Esny#JSPMAc%XnINx#h(ubJnq>I`l3GQMdJE>xzwE8u)qQKb zcMN0{^F#&wYm?w(qVg#umW<&@RzkD|DNbd*6!mm`f`c0VT-{!is1#J-eE8VY*iDYt?fP{B=H$AIJ~Zk z)l^TOGY2txa`>dbiU!C>Ho-4L**N&UF{QAA_`NZu-4~;h?|C^&4Ecg|BD+M_r%HsV zkNhNm=(J4j@9-edr}`vB6k8%jOB0K?Vx)O>p-gNQ_UZR)REnBXGsZRP)S(OLPY_W_ zC4nCAG;#nM;U$jxjI9jBanO^2FKxs4$Vpuj-J#ZqG1YPGXrt&8$K)|}t&SO31aO#9 z`jc<6(jpk7ELn$iIa;GWF>5@A*{By2BIIpHWDEzd z7T;6QgcuE@>Q1dw`*Cn6CxLV`+KG#Wc%pum)C^{awUQ9xIO0&P7)uyu%@rX{8g$E% z2IoSrF>WWWoFbF~FN1VdjWRHJ(p|qvs3k|@ z)l?L1(AY@7G5qav5&pzIS+W7O@p_$GA$_#h@)7FxKJv&r$Z{0@v@b<-*9!fRTv&&o zB%{(=)^NzxZhU?!4e9fXcqE-_Sje+5pJ$B5Jk{aQ5FeWGwsKr&-cjqHS&EucH%-urY*~((v_UPqwq1){(!Z-$x$a~sS__L%9#(kC z*{DGyqIoQW=IeP*=K#ymy6r^KFKL$Mh2*P48`M6fNcYlaf;=in}G(Yt*~@1~S= z0is*`QsmTd|D)Umqv<>y&LJ294|RAinwVV6(NVV zYJ_cFkDMbVKggb#<H>Q`M|(x#iA5?RuVF*HRjQDNR0zY-lbqPsNoA(@r7!jgPBH|&SnZ*1HX#hHl_bJ1ylI&E6T5iR0q7D{;fV_7p&t%fGNw3_#v>2E44@ny* zi=x@5b>{9OQfr;tAlrjC?MXqI4EQMsXy1$S5p(~&F;;R9_=FdpB-LEjIq9l%G{Cz` z7m`_qS`?Jgp-BdWqFAmo@+9qZWI#*4itJw?kS7b4I6()lf9rTxgkvIUr?c$hgh}X2 z26UYzp&Yr&lTtvc-n4>?AQg>oE?O=UeU+~ppsX6-f)bj595;XyX`|5(*&kL(#zdBe z_CcyG3dvT;|1@%7*0PfC$KhH1YV+K*P=j(mSo^aH%Omu3`48mU2pDjh^&~b zl@4vR8bmRTw+2Z1D>}T;9!fJsEz(|x4~}3qspUDSA$_N-{n{gzC*S0@N*@16HT76( zE742G9gZRbceQlhsXFg^a+Q*|C4VRFn(T_!4ISR}o`~LP?Z{o4JheR1Qytn5Yq*ZI zp6}j!rAH<^c6FMJYN=ncJ@G^G#qIaiBeyD72FXqlko4M!jk9D6FQ5c0Xq3+A6<0_-*BdM+7sYlhW+CZ%bkQPG*~TSy}=l#@QuTlBnb;~B6H|D4wt*c z!#N~GElEFef#*`ijr>R>ba^5#RYpN~wAQ3YRhKGhZhe$WaXpeqBdH`3g~Op+xx0`b zpjmNGfsi?^Sd}Xl#__GN2l{`Yn27%eqIa69@%aA~)Tsrf*&PI()Bi^POm+D@T1*oY zVCB;=BV%xuorzUa3Ooj1fIAsHx>T;9-|xzviLb+*3_JhA0! zi=tNY1a!6}3lX^$pz>0gq6irQh*Edtop(aI7^I6exhuGFW5+X|=vE48N+D|{Mm~n+ zqtR8Wafq(&Sf{ZMXgA&y=)BVTW}tIiM{}lT<@3o;jWt@%oUrWWZS&rlSF~6JeskgZ zXLUJo?^Sk@2nS6kVmj%j6R@1{EL_VPc9CI3d_|@a0W}O}rV}&WA=vgj(k5k@;q(22 z@%@3B#|p60BLQKCgY>^640^y+!%K&uQg~@~z+Yt2BVhFv>At>{f&PNva#734ir51G z*#;34#D}kf4EN)1`q@r+=?0RvFSXV;+$9bDhWqA*2O7r;H(i3X%pU5pP6i7jF(VPm z7!dAfqvyWp__P3ZymUjjA4W_v(del&17*iJN>C|dj zpvY%LOw%MSg~zKZ<2_Z7mQ|qH0(2W8m6V3ZkSZhLQV39ufAf%Iq6sMa5X%!J)su*3 zz{w4lOTM67^xAgFFPTA-A&B}DN$duatAPjIpd@MVNP@JhtcI{u7FeXLlp>pEI1&j2 ziY!W{r>99)_{{K`0XID@JS`B!cu^os>3}252%aQF0}0SVsTPPcoyPzQSvRu88<`Vu zFahb|pp99MqzDUBlo?*yLa9*5O$CFLN(C=#M%D}@14yQ&sYGh3N~ENyL?D2K4~nAs zM)QSGv1J7Cq|rnoR18w5rw2@va!7IrkPg!W69$)F81M_TFn|nvJ1xouQecsp>6u9R zg&m-pmQ!f)a9$;&Pbc#NrMY+&-k@0Y* zBOzM2iL5O=f$0>cl}yipsvy8>)pXlxhHG57hT%FFu4A~tg&SzN!^8dPF_M>2co-XB z+eTguZOi22O8K~2K32%b8}jjXdJ2{UQ~{;1f0)H|JktqGr!cJ~9o7&zk@0Y*G&lo` zr?23S;eD{er8f#ds-?rQR5&p!ytIJbLohcShs9$uYVmI&JG^wH3VCX#!;7(Rg5!dr z1j)oLGZ^GYD3odoBS6NR8UhKD6@^kw2hKFbbV8g?G-3XeXIx%m3^#`4nPC)tQt^<=wv8&GL)J;SvN^3OgxFnvae&A){z%& zA+mzG@ZQ)d*bZ07((2J-!%Ulbp|plu`3aB0&wk;4{DkM>HaD<_R@l%}GszIwGEfTZ z9!>-l3q62Uo1Tt!k4d8hae1k_G(gjx9-_V{QhO7z?BV*u)8S$GnVyO!h=E4n7?57i7VxKQuy>Blse6>Meo{)o&LhReN31 zRaad&5880^D9L$9aNdu?KXUUZ$$3cdv~_3NO7mb5!Qc%8d$eI>&7c{{YFi3zV<%eL z;_#C#KN1dLVcKAm8>Ir_^TTp;z%xA+S;RnI%3#AZ5DJ6?se=tmrqbcb;6I7UwedB> zc#`hTbl!n4-4aGOM)=a|@H8F)oFz)|$SKJqKteK*!?O@2Uv0p>^6VByY#>A^0x3-B zbWA8xnAPd<)e$ViOLJ(Ls9Z$50QwhT@tBc=wZ|a5^h=BnX8a}G8b8x9T)=eq2}v1; z*o?m`g|G!;a|*C2 z!o2{brl!iQ5IusyppTrvFh@Wt>;MF2`5B;xOt}L94wG(Rm@hyDxY&566PQk6T4|$t z4UrQW4`(_OibSA56gvYbb_P)F44~KXmFjzB{j&jXymbPCf-8~hp~Co&$+ zbR;p#NDwkBHhZGsX{^Gw)im<915^}gq6cUyvD6*RT2|Y2WMi;*zks#CaA|GQQ!RN| zJ6j{3a&=Pz=AWs}KQn3mnO^fpdu;w_-b^Q(H4!mGNUQ;vMsGOFwpG|J&^(x{!)-rv z6W2ic!Vs;;!Vv6+p0JyM6~K2%!=5(s5KB?jNP&+*5w%e82nsvgJsdzM0EnbqK&q8| zSz@}k1Or)ul4%$amM}L#!<6X&b|X&7Ryh~rL90lT@+ra2k+XOuBZaA@A+vHJ68mdb z2Kz#ioLC`dg@RC7ZdTh`V3OW+K?)j?e4dJF$YsYsGmPe^u{)hk?v(loBgNU%BM=&s zO2uiqBI!O1!r<*9K$A$@1rj6y^ow~{i>MS{2~Da1`nQ(Pp7o0di% zjUrd@Nls3Z9w>oul4pv#q#2`$lS87hVp5VYjf$u|>L|hk022wq!Jz6G3F;V83OBB} zXq>nRTEM07N{Pcso)++=k&_~&*-vAQA*Lr6K+S?i+SZ9)IGwc#eEYW$4xQ3~r&g4f z2&`-a|G#v4+T%mdsJ$k5@<)%1+>`d+{2>pmT{Pkv`|qo+dvIW5d{4@6pT6Lkvxe`x zVe>D$WIuNMX}j+T&8>TV&og}vjXnI~Jn^tqZbfhIdgs)Pnax*?ed)N<|MJkqB_Z?W z>wo^znUB>@{doBaUz_93m^`-d^0cb%Yf{#qbV|?HOV0Y-+WpLZSO4Jm-MekJaVkZq zjNK(*`^|KdzRVu>it(qX)9iv`fHplMR3N~<0KaEprH9-0(CGchG%;U(3#%r8D9~@x zgYYc)V$|%%c6d6pq4Y{Rfy!+}>J%^pX>-e(LCG{CsYFtU1c;av9)X}V9IhQQO+@I# z4-%guf2=aU9b~=_ELzb3SlMub^gvWa9c0ZwKcTD{seWJ9j1aX#gE7;fj5Pe`_k$$a zbgoEEP4W9ule`oZO-V^XQP3ot&Kx+O`h5Y-3m`Hv0$DTALE!&s@BCilsG>MNGx;@{ zBx|$&Dw>rWQAi=KNl3v$swrtpElr!&kT#%{rCr;QrHM(~C@5r#ffo8u-y$gVL7@*V z2>K#a@X<#feDFcVz9|&!KT!4aJ$EAW=X` zaHa`eNhi|`^yBK!UNBnx4%9p7jn#)te`BP?g)KJKL1YkCu1XK%7KRe1t7tCj7CF5Z ztLLEB;97c`k=PF2!iD8bknUh?B8=Z{!`TfbAR zH)R?SFwf=E7O`WKg!M~MxCr-0)S7>@6V4o6xf}UL!5qXMC}eV_@Q{T^ES$1%T6`)B zM(uda!f_>BRB%MWlmeqXrEKON3P0%&gFd`m-KI<304CA)+yEId^T*~+?q+VK7@K}| zfDB`B)56chd`yAWY6@!gL(95q;dKjdSooubKcRj482sri)Dt=+%pYe093@F_s=F9{ z-0uOH^et{`*=k9dXqnka11x<9v`iM)fM5#Beupz6R^aw|bY!Y?)67jDf z9qbm-3_6l_;fM(@Q?K*`HHNLHm;ghcQ_+4T-@2k02X|DFytNW`g{vy*>TKUq* z_;D1Or?6x#dad{kC9YSCt&>_^$WjIdRic~HBPOBs1Xu5|ks`bqrN>do z`Qsg~B`&maIU^}S#TVKNZTa*gHdYG|>y&Q!Q?XSrx`h*=WO~hG`YC$4l`C}^Oh-LD zNn>H9_Rtqww)du=$GURs1Lc9Hl%WJ`(zxEy_(<=tF+M=!PVSMVf^HN*T)l+-p6}=~ zok2ovOoDq(*Ij8T?x{uS9U*+>EJC5%W~#LW`|;+DV$|EEg_WHsXDQ244rX2le=BK7fl&NHqoSp%XGVP}6Z=%rSEaI1AWD z$G0yI45JXnW~hRBKyONaBQxTzgN<+Mq|V?zqq$cSpU_ zqM8SAZ`MyH26)?$X^>@iMs7(C7gp!J3miTU#7c8=bq@N%;Au}-^lk_fo6qqITo^oC z&BVp13BfaA@Z^@#HD|0k9frq=c{NlY-4!&HF@GsJKm?ERM#L&R*%@J-OYtC&;M+BNIb#u2Hl&v$atlPb;)sIbH*O}>PmOwug)DdkxK88rsS<0|W zrJBUn*zzrPNjARS2AQT*@YafsFOq^*MBe-J>38jZvCsyWzyK~5U*6cf3-2;Moh&)j z9v13dBgNf(hlhrT@hzbN{YQDw&P6x3>`D&d@E*%<;caK>#Rc4cgVnDYIA3l!ExUn< zU2bG(Xyhe=Jx-2)mASH;lG|CaCuvSM>JH;4zU)pG-O=*O{M^#~pqrdKbG}rrxQU{h zURfA)`zqyk%X4mD(akI@A&$!nmz(h$QoSQ?>O3yXi*5pmJ_i|s<4(w@dfA~|x{z_n=Q&|B9X)k&skF#be>eQ-%s+ow$lkqg_!{*uiaU!o z1mD-uZD#V^pQZhk$|M8NKT^6}ws+Tv;;$D2H@-=l#WDmk1nLk7gB=|0Lr9>jb=MprUCG$U2=Ua(R!kdj#=kFjQ7Ga&Ok zzG!8k2@h}1Dl(kg=edN|Dc#DydgSNkub+ zK&1zRoJF|NS{a_ob4x}Nb;hL7IexDM-F37x(DLXWC}qT^&j@^XSAWB_XqYco7y97| z$_WW?-X>GaN?prjz*TFP%;-oix3;Z_{7KdkTyX%^_NWYWXLfEPj4<)zv4Ga`NP%L%P8q2 zZFig}55>TsN~R|B;O#I1kpZ~sl@XuXAi8O&7A$`ssWR}80hz-{M7cBM$w*fi%E10I zsoQJc(8l?Wz-NkjC7J49)hx7yp68~T)uI!YCPZ}}GKKPnSKf<^>nT*vZ|Ms!)iO(z zftSvd$t9mnnUjg6xXHjvCPO?qsgBwkP7AF@8J#luIYt?^RHH(@UmF)X@)EvXIT{RD z$bf|KJd5nfq`dh!34$?``K`47l>A4@{kWB_nU%@B&HK=8x4Z$E1sMVv0vQ4s0vQ4s z0vQ4s0vQ7T2Lyf%nLP)=1H41BPliB-K!!kuK!!kuK!!kuK!!kuK!!kuK!!kuz<(2g FzX1!`>QMjy From 03cd725ed15303410dd541b248e53689e3f9d5d2 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 23 May 2013 19:04:41 +0800 Subject: [PATCH 53/62] added Udp test cases for the built-in receive filters --- Test/Protocol/ProtocolTestBase.cs | 36 ++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/Test/Protocol/ProtocolTestBase.cs b/Test/Protocol/ProtocolTestBase.cs index 4d11951d6..832d0280f 100644 --- a/Test/Protocol/ProtocolTestBase.cs +++ b/Test/Protocol/ProtocolTestBase.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using NUnit.Framework; +using SuperSocket.SocketBase; using SuperSocket.SocketBase.Config; using SuperSocket.SocketBase.Logging; using SuperSocket.SocketBase.Protocol; @@ -20,25 +21,33 @@ public abstract class ProtocolTestBase protected abstract IReceiveFilterFactory CurrentReceiveFilterFactory { get; } - [TestFixtureSetUp] + [SetUp] public void Setup() { - m_Server = CreateServer(CurrentReceiveFilterFactory); + var mode = TestContext.CurrentContext.Test.Name.Contains("Udp") ? SocketMode.Udp : SocketMode.Tcp; + m_Server = CreateServer(CurrentReceiveFilterFactory, mode); } - [TestFixtureTearDown] + [TearDown] public void TearDown() { m_Server.Stop(); } private TestServer CreateServer(IReceiveFilterFactory receiveFilterFactory) + { + return CreateServer(receiveFilterFactory, SocketMode.Tcp); + } + + private TestServer CreateServer(IReceiveFilterFactory receiveFilterFactory, SocketMode mode) { var appServer = new TestServer(); var serverConfig = new ServerConfig(); serverConfig.Ip = "127.0.0.1"; serverConfig.Port = 2012; + serverConfig.Mode = mode; + serverConfig.DisableSessionSnapshot = true; var setupResult = appServer.Setup(serverConfig, null, receiveFilterFactory, new ConsoleLogFactory(), null, null); @@ -204,5 +213,26 @@ public void TestBreakRequest() } } } + + + [Test] + public void TestUdpRequest() + { + EndPoint serverAddress = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2012); + var client = new Socket(serverAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + + var line = Guid.NewGuid().ToString(); + + var data = Encoding.ASCII.GetBytes(CreateRequest(line)); + + client.SendTo(data, serverAddress); + + var receiveData = new byte[data.Length]; + + var len = client.ReceiveFrom(receiveData, ref serverAddress); + + Assert.AreEqual(line.Length, len); + Assert.AreEqual(line, Encoding.ASCII.GetString(receiveData, 0, len)); + } } } From 809f457284b008494ae9571e51b10655231b6782 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Fri, 24 May 2013 12:04:30 +0800 Subject: [PATCH 54/62] fixed a bug which can cause the closed event will be fired multiple times --- SocketEngine/SocketSession.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SocketEngine/SocketSession.cs b/SocketEngine/SocketSession.cs index 7eb6934e3..a7d65f694 100644 --- a/SocketEngine/SocketSession.cs +++ b/SocketEngine/SocketSession.cs @@ -20,7 +20,7 @@ static class SocketState { public const int Normal = 0;//0000 0000 public const int InClosing = 16;//0001 0000 >= 16 - public static int Closed = 16777216;//256 * 256 * 256; 0x01 0x00 0x00 0x00 + public const int Closed = 16777216;//256 * 256 * 256; 0x01 0x00 0x00 0x00 public const int InSending = 1;//0000 0001 > 1 public const int InReceiving = 2;//0000 0010 > 2 public const int InSendingReceivingMask = -4;// ~(InSending | InReceiving); 0xf0 0xff 0xff 0xff @@ -162,7 +162,9 @@ protected virtual void StartSession() /// protected virtual void OnClosed(CloseReason reason) { - AddStateFlag(SocketState.Closed); + //Already closed + if (!TryAddStateFlag(SocketState.Closed)) + return; //Before changing m_SendingQueue, must check m_IsClosed while (true) From 31cbeb2f259e7bfe8d145443be8e21be14ddd722 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Sun, 26 May 2013 21:45:18 +0800 Subject: [PATCH 55/62] added more test cases for ReceiveFilters --- Test/Protocol/ProtocolTestBase.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Test/Protocol/ProtocolTestBase.cs b/Test/Protocol/ProtocolTestBase.cs index 832d0280f..beb7b1088 100644 --- a/Test/Protocol/ProtocolTestBase.cs +++ b/Test/Protocol/ProtocolTestBase.cs @@ -91,6 +91,31 @@ public void TestNormalRequest() } } + [Test] + public void TestMiddleBreak() + { + for (var i = 0; i < 100; i++) + { + using (var socket = CreateClient()) + { + var socketStream = new NetworkStream(socket); + using (var reader = new StreamReader(socketStream, Encoding.ASCII, true)) + using (var writer = new ConsoleWriter(socketStream, Encoding.ASCII, 1024 * 8)) + { + reader.ReadLine(); + + var line = Guid.NewGuid().ToString(); + var sendingLine = CreateRequest(line); + writer.Write(sendingLine.Substring(0, sendingLine.Length / 2)); + writer.Flush(); + + var s = m_Server.GetAllSessions().FirstOrDefault(); + s.Close(); + } + } + } + } + [Test] public void TestFragmentRequest() { From 58a885d91458ab5f1b03a81aae5ee8677148197e Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Wed, 10 Jul 2013 23:27:47 +0800 Subject: [PATCH 56/62] upgraded the assembly version to 1.5.4 --- Solution Items/GlobalAssemblyInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Solution Items/GlobalAssemblyInfo.cs b/Solution Items/GlobalAssemblyInfo.cs index 5837341e2..b60c8abf9 100644 --- a/Solution Items/GlobalAssemblyInfo.cs +++ b/Solution Items/GlobalAssemblyInfo.cs @@ -8,8 +8,8 @@ // Build Number // Revision // -[assembly: AssemblyVersion("1.5.3.0")] -[assembly: AssemblyFileVersion("1.5.3.0")] +[assembly: AssemblyVersion("1.5.4.0")] +[assembly: AssemblyFileVersion("1.5.4.0")] [assembly: AssemblyCompany("SuperSocket")] [assembly: AssemblyProduct("SuperSocket")] [assembly: AssemblyCopyright("Copyright © supersocket.codeplex.com 2010-2013")] From 765204ff173305741e170c627a03cf3619b27fb8 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Mon, 15 Jul 2013 00:43:39 +0800 Subject: [PATCH 57/62] fixed a bug that when you close a session, the data is being sent won't be sent; ignored the socket error 10060; --- SocketEngine/SocketSession.cs | 89 ++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/SocketEngine/SocketSession.cs b/SocketEngine/SocketSession.cs index a7d65f694..dd11fa552 100644 --- a/SocketEngine/SocketSession.cs +++ b/SocketEngine/SocketSession.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -36,13 +36,13 @@ abstract partial class SocketSession : ISocketSession protected readonly object SyncRoot = new object(); //0x00 0x00 0x00 0x00 - //1st byte: Closed(Y/N) + //1st byte: Closed(Y/N) - 0x01 //2nd byte: N/A //3th byte: CloseReason //Last byte: 0000 0000 - normal state - //bit 7: closed - //bit 6: in sending - //bit 5: in receiving + //0000 0001: in sending + //0000 0010: in receiving + //0001 0000: in closing private int m_State = 0; private void AddStateFlag(int stateValue) @@ -276,12 +276,12 @@ private void StartSend(SendingQueue queue, int sendingTrackID, bool initial) if (currentQueue != queue || sendingTrackID != currentQueue.TrackID) { //Has been sent - OnSendEnd(false); + OnSendEnd(); return; } } - if (IsInClosingOrClosed) + if (IsInClosingOrClosed && m_Client == null) { OnSendEnd(true); return; @@ -334,12 +334,32 @@ private void StartSend(SendingQueue queue, int sendingTrackID, bool initial) Send(queue); } + private void OnSendEnd() + { + OnSendEnd(IsInClosingOrClosed); + } + private void OnSendEnd(bool isInClosingOrClosed) { RemoveStateFlag(SocketState.InSending); if (isInClosingOrClosed) { + var client = m_Client; + //The socket has not been closed, close it now + if (client != null) + { + //No data to be sent + if (m_SendingQueue.Count == 0) + { + //Not can close it now + InternalClose(client, GetCloseReasonFromState(), false); + return; + } + + return; + } + if (ValidateNotInSendingReceiving()) { FireCloseEvent(); @@ -352,17 +372,24 @@ protected virtual void OnSendingCompleted(SendingQueue queue) queue.Clear(); m_SendingQueuePool.Push(queue); + var newQueue = m_SendingQueue; + if (IsInClosingOrClosed) { + //has data is being sent and the socket isn't closed + if (newQueue.Count > 0 && m_Client != null) + { + StartSend(newQueue, newQueue.TrackID, false); + return; + } + OnSendEnd(true); return; } - var newQueue = m_SendingQueue; - if (newQueue.Count == 0) { - OnSendEnd(false); + OnSendEnd(); if (newQueue.Count > 0) { @@ -431,9 +458,24 @@ public virtual void Close(CloseReason reason) if (client == null) return; + //Some data is in sending + if (CheckState(SocketState.InSending)) + { + //Set closing reason only, don't close the socket directly + AddStateFlag(GetCloseReasonValue(reason)); + return; + } + + InternalClose(client, reason, true); + } + + private void InternalClose(Socket client, CloseReason reason, bool setCloseReason) + { if (Interlocked.CompareExchange(ref m_Client, null, client) == client) { - AddStateFlag(((int)reason + 1) * m_CloseReasonMagic); + if (setCloseReason) + AddStateFlag(GetCloseReasonValue(reason)); + client.SafeClose(); if (ValidateNotInSendingReceiving()) @@ -447,7 +489,7 @@ protected void OnSendError(SendingQueue queue, CloseReason closeReason) { queue.Clear(); m_SendingQueuePool.Push(queue); - OnSendEnd(false); + OnSendEnd(); ValidateClosed(closeReason); } @@ -481,9 +523,19 @@ private bool ValidateNotInSendingReceiving() private const int m_CloseReasonMagic = 256; + private int GetCloseReasonValue(CloseReason reason) + { + return ((int)reason + 1) * m_CloseReasonMagic; + } + + private CloseReason GetCloseReasonFromState() + { + return (CloseReason)(m_State / m_CloseReasonMagic - 1); + } + private void FireCloseEvent() { - OnClosed((CloseReason)(m_State / m_CloseReasonMagic - 1)); + OnClosed(GetCloseReasonFromState()); } private void ValidateClosed(CloseReason closeReason) @@ -508,11 +560,12 @@ private void ValidateClosed(CloseReason closeReason) protected virtual bool IsIgnorableSocketError(int socketErrorCode) { - if (socketErrorCode == 10004 - || socketErrorCode == 10053 - || socketErrorCode == 10054 - || socketErrorCode == 10058 - || socketErrorCode == 995 + if (socketErrorCode == 10004 //Interrupted + || socketErrorCode == 10053 //ConnectionAborted + || socketErrorCode == 10054 //ConnectionReset + || socketErrorCode == 10058 //Shutdown + || socketErrorCode == 10060 //TimedOut + || socketErrorCode == 995 //OperationAborted || socketErrorCode == -1073741299) { return true; From 9692202275d387b1f4fdf2e8fa65637a6684a379 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Wed, 17 Jul 2013 00:16:04 +0800 Subject: [PATCH 58/62] fixed the default clear idle session parameters in the config model class --- SocketBase/Config/ServerConfig.cs | 12 ++++++++++++ SocketEngine/Configuration/Server.cs | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/SocketBase/Config/ServerConfig.cs b/SocketBase/Config/ServerConfig.cs index feb7e463b..ac881c607 100644 --- a/SocketBase/Config/ServerConfig.cs +++ b/SocketBase/Config/ServerConfig.cs @@ -36,6 +36,16 @@ public class ServerConfig : IServerConfig /// public const int DefaultMaxRequestLength = 1024; + /// + /// Default clear idle session interval + /// + public const int DefaultClearIdleSessionInterval = 120; + + /// + /// Default idle session timeout + /// + public const int DefaultIdleSessionTimeOut = 300; + /// /// Initializes a new instance of the class. /// @@ -75,6 +85,8 @@ public ServerConfig() ListenBacklog = 100; ReceiveBufferSize = DefaultReceiveBufferSize; SendingQueueSize = DefaultSendingQueueSize; + ClearIdleSessionInterval = DefaultClearIdleSessionInterval; + IdleSessionTimeOut = DefaultIdleSessionTimeOut; } #region IServerConfig Members diff --git a/SocketEngine/Configuration/Server.cs b/SocketEngine/Configuration/Server.cs index 1aa746f4f..4ee29d5c5 100644 --- a/SocketEngine/Configuration/Server.cs +++ b/SocketEngine/Configuration/Server.cs @@ -193,7 +193,7 @@ public bool ClearIdleSession /// Gets the clear idle session interval, in seconds. /// /// The clear idle session interval. - [ConfigurationProperty("clearIdleSessionInterval", IsRequired = false, DefaultValue = 120)] + [ConfigurationProperty("clearIdleSessionInterval", IsRequired = false, DefaultValue = ServerConfig.DefaultClearIdleSessionInterval)] public int ClearIdleSessionInterval { get { return (int)this["clearIdleSessionInterval"]; } @@ -204,7 +204,7 @@ public int ClearIdleSessionInterval /// Gets the idle session timeout time length, in seconds. /// /// The idle session time out. - [ConfigurationProperty("idleSessionTimeOut", IsRequired = false, DefaultValue = 300)] + [ConfigurationProperty("idleSessionTimeOut", IsRequired = false, DefaultValue = ServerConfig.DefaultIdleSessionTimeOut)] public int IdleSessionTimeOut { get { return (int)this["idleSessionTimeOut"]; } From 08b74378e498ea9df2b6c6a33b60a532412e71e5 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 15 Aug 2013 23:34:27 +0800 Subject: [PATCH 59/62] fixed serializing exception for CommandAssemblyConfig and ListenerConfig when isolation level is set --- SocketBase/Config/CommandAssemblyConfig.cs | 1 + SocketBase/Config/ServerConfig.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/SocketBase/Config/CommandAssemblyConfig.cs b/SocketBase/Config/CommandAssemblyConfig.cs index 1b61b79a8..01b24f83c 100644 --- a/SocketBase/Config/CommandAssemblyConfig.cs +++ b/SocketBase/Config/CommandAssemblyConfig.cs @@ -8,6 +8,7 @@ namespace SuperSocket.SocketBase.Config /// /// Command assembly config /// + [Serializable] public class CommandAssemblyConfig : ICommandAssemblyConfig { /// diff --git a/SocketBase/Config/ServerConfig.cs b/SocketBase/Config/ServerConfig.cs index ac881c607..17a90dc60 100644 --- a/SocketBase/Config/ServerConfig.cs +++ b/SocketBase/Config/ServerConfig.cs @@ -62,12 +62,12 @@ public ServerConfig(IServerConfig serverConfig) if (serverConfig.Listeners != null && serverConfig.Listeners.Any()) { - this.Listeners = serverConfig.Listeners.Select(l => l.CopyPropertiesTo(new ListenerConfig())); + this.Listeners = serverConfig.Listeners.Select(l => l.CopyPropertiesTo(new ListenerConfig())).OfType().ToArray(); } if (serverConfig.CommandAssemblies != null && serverConfig.CommandAssemblies.Any()) { - this.CommandAssemblies = serverConfig.CommandAssemblies.Select(c => c.CopyPropertiesTo(new CommandAssemblyConfig())); + this.CommandAssemblies = serverConfig.CommandAssemblies.Select(c => c.CopyPropertiesTo(new CommandAssemblyConfig())).OfType().ToArray(); } } From a8219331f219922e5756dda36ae9550988c52fb4 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Mon, 19 Aug 2013 20:21:20 +0800 Subject: [PATCH 60/62] fixed a bug that default security option doesn't work for listeners --- SocketEngine/Configuration/Listener.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SocketEngine/Configuration/Listener.cs b/SocketEngine/Configuration/Listener.cs index 4cca34759..cb7f2f265 100644 --- a/SocketEngine/Configuration/Listener.cs +++ b/SocketEngine/Configuration/Listener.cs @@ -43,7 +43,7 @@ public int Backlog /// /// Gets the security option, None/Default/Tls/Ssl/... /// - [ConfigurationProperty("security", IsRequired = false, DefaultValue = "None")] + [ConfigurationProperty("security", IsRequired = false)] public string Security { get From e9b3ebf99b0ba884854b688e3628cb1ac5bdbe47 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Thu, 22 Aug 2013 20:56:32 +0800 Subject: [PATCH 61/62] fixed a security loading error if the listener's security has not been set --- SocketBase/AppServerBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SocketBase/AppServerBase.cs b/SocketBase/AppServerBase.cs index 8ad5cbc60..ef0e11b16 100644 --- a/SocketBase/AppServerBase.cs +++ b/SocketBase/AppServerBase.cs @@ -911,7 +911,7 @@ private bool SetupListeners(IServerConfig config) { SslProtocols configProtocol; - if (string.IsNullOrEmpty(l.Security) && BasicSecurity != SslProtocols.None) + if (string.IsNullOrEmpty(l.Security)) { configProtocol = BasicSecurity; } From 07807f0c504a77b13ea3860b30dcfd6e0bdd5ad5 Mon Sep 17 00:00:00 2001 From: Kerry Jiang Date: Sat, 31 Aug 2013 18:22:02 +0800 Subject: [PATCH 62/62] fixed the remoting server lifetime issue in the AppDomain isolation mode --- SocketEngine/MarshalAppServer.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/SocketEngine/MarshalAppServer.cs b/SocketEngine/MarshalAppServer.cs index bff29c808..b458543a5 100644 --- a/SocketEngine/MarshalAppServer.cs +++ b/SocketEngine/MarshalAppServer.cs @@ -91,5 +91,19 @@ ServerSummary IWorkItem.CollectServerSummary(NodeSummary nodeSummary) { return m_AppServer.CollectServerSummary(nodeSummary); } + + /// + /// Obtains a lifetime service object to control the lifetime policy for this instance. + /// + /// + /// An object of type used to control the lifetime policy for this instance. This is the current lifetime service object for this instance if one exists; otherwise, a new lifetime service object initialized to the value of the property. + /// + /// + /// + /// + public override object InitializeLifetimeService() + { + return null; + } } }