SupperSocket深入浅出(一)
花了几天时间了解了SupperSocket工作原理,各各类之间的工作关系。SupperSocket大部资料网上都有,但写的都不适合初学者。
今天花点时间写下这几天的学习成果,一方面是为了将来更好的回顾知识点,另一方面想给初学者提供一份参考资料。考虑到笔功有限,
如果下面有什么信息不正确或写的不好,请大家多多包容!
首先我贴一简单的代码。后面我会详细的说明工作原理和他们之间如何调用!下面的代码我也是从网上找的,相当简单。
我今天主要是讲解下SupperSocket的理解!
public class TelnetServer : AppServer<TelnetSession> { protected override bool Setup(IRootConfig rootConfig, IServerConfig config) { return base.Setup(rootConfig, config); } protected override void OnStartup() { base.OnStartup(); } protected override void OnStopped() { base.OnStopped(); } }
public class TelnetSession : AppSession<TelnetSession> { protected override void OnSessionStarted() { //this.Send("Welcome to SuperSocket Telnet Server\r\n"); byte[] bytes = Encoding.ASCII.GetBytes("Welcome\r\n to SuperSocket\r\n Telnet Server\r\n"); this.Send(bytes, 0, bytes.Length); } protected override void HandleUnknownRequest(StringRequestInfo requestInfo) { this.Send("Unknow request"); } protected override void HandleException(Exception e) { this.Send("Application error: {0}", e.Message); } protected override void OnSessionClosed(CloseReason reason) { //add you logics which will be executed after the session is closed base.OnSessionClosed(reason); } }
public class ECHO : CommandBase<TelnetSession, StringRequestInfo> { public override void ExecuteCommand(TelnetSession session, StringRequestInfo requestInfo) { session.Send(requestInfo.Body); } }
static void Main(string[] args) { Console.WriteLine("Press any key to start the server!"); Console.ReadKey(); Console.WriteLine(); var appServer = new TelnetServer(); //Setup the appServer if (!appServer.Setup(2012)) //Setup with listening port { Console.WriteLine("Failed to setup!"); Console.ReadKey(); return; } Console.WriteLine(); //Try to start the appServer if (!appServer.Start()) { Console.WriteLine("Failed to start!"); Console.ReadKey(); return; } Console.WriteLine("The server started successfully, press key 'q' to stop it!"); while (Console.ReadKey().KeyChar != 'q') { Console.WriteLine(); continue; } //Stop the appServer appServer.Stop(); Console.WriteLine("The server was stopped!"); Console.ReadKey(); }
服务端代码就上面四部分,看起来很简单吧,但是大家真的看懂了吗,他们的工作原理是怎样的。命令 ECHO 这个类根本没有构造对象,他是怎样运行的?你越看越疑惑吧!
后面我会说明。
我还是先把客户端代码贴出来。代码简化了,如果大家不知道EasyClietn 可以使用Nuget搜索SuperSocket.ClientEngine、SuperSocket.ProtoBase
因为版本有很多,大家最好是用Nuget
private void button1_Click(object sender, EventArgs e) { string strText = "add 1 1\r\n"; if (client != null && client.IsConnected ) { Byte[] smk = new Byte[strText.Length]; for (int i = 0; i < strText.Length; i++) { Byte ss = Convert.ToByte(strText[i]); smk[i] = ss; } byte[] b = Encoding.ASCII.GetBytes("ECHO 1 1\r\n"); client.Send(smk.ToArray()); //EasyClient<MyPackageInfo> client } }
byte[] b = Encoding.ASCII.GetBytes("ECHO 1 1\r\n"); client.Send(smk.ToArray());
给服务端发送了二进制的“ECHO 1 1\r\n" ,在这里给大家一个问题,为什么后面要加\r\n 换行符。大家带着问题继续往下看。
现在给了大家两个问题?,现在我们来解决问题。
服务端是如何接收到消息。其它大家可以不要具体体解,因为SupperStocket封闭了TCP 和 UDP 层。
SuperSocket.SocketEngine.AsyncStreamSocketSession 这个类是工作最底运类
从这个类可以知道数据的接收来源。
这个类是由
SuperSocket.SocketEngine.AsyncStreamSocketSession 《 SuperSocket.SocketEngine.AsyncSocketServer 《 SocketServerFactory 《 ProviderKey
《 ProviderFactoryInfo 《 AppDomainAppServer 《 DefaultBootstrap
这是最底层类,获取数据和发达送数据,这个大部分我们不要了解,因为这些都被AppSession封装起来了。
大家想了解的可以看下这个类。
class AsyncStreamSocketSession : SocketSession, IAsyncSocketSessionBase, INegotiateSocketSession { private byte[] m_ReadBuffer; private int m_Offset; private int m_Length; private bool m_IsReset; public AsyncStreamSocketSession(Socket client, SslProtocols security, SocketAsyncEventArgsProxy socketAsyncProxy) : this(client, security, socketAsyncProxy, false) { } public AsyncStreamSocketSession(Socket client, SslProtocols security, SocketAsyncEventArgsProxy socketAsyncProxy, bool isReset) : base(client) { SecureProtocol = security; SocketAsyncProxy = socketAsyncProxy; var e = socketAsyncProxy.SocketEventArgs; m_ReadBuffer = e.Buffer; m_Offset = e.Offset; m_Length = e.Count; m_IsReset = isReset; } /// <summary> /// Starts this session communication. /// </summary> public override void Start() { //Hasn't started, but already closed if (IsClosed) return; OnSessionStarting(); } private void OnSessionStarting() { try { OnReceiveStarted(); m_Stream.BeginRead(m_ReadBuffer, m_Offset, m_Length, OnStreamEndRead, m_Stream); } catch (Exception e) { LogError(e); OnReceiveTerminated(CloseReason.SocketError); return; } if (!m_IsReset) StartSession(); } private void OnStreamEndRead(IAsyncResult result) { var stream = result.AsyncState as Stream; int thisRead = 0; try { thisRead = stream.EndRead(result); } catch (Exception e) { LogError(e); OnReceiveTerminated(CloseReason.SocketError); return; } if (thisRead <= 0) { OnReceiveTerminated(CloseReason.ClientClosing); return; } OnReceiveEnded(); int offsetDelta; try { offsetDelta = AppSession.ProcessRequest(m_ReadBuffer, m_Offset, thisRead, true); } catch (Exception ex) { LogError("Protocol error", ex); this.Close(CloseReason.ProtocolError); return; } try { if (offsetDelta < 0 || offsetDelta >= Config.ReceiveBufferSize) throw new ArgumentException(string.Format("Illigal offsetDelta: {0}", offsetDelta), "offsetDelta"); m_Offset = SocketAsyncProxy.OrigOffset + offsetDelta; m_Length = Config.ReceiveBufferSize - offsetDelta; OnReceiveStarted(); m_Stream.BeginRead(m_ReadBuffer, m_Offset, m_Length, OnStreamEndRead, m_Stream); } catch (Exception exc) { LogError(exc); OnReceiveTerminated(CloseReason.SocketError); return; } } private Stream m_Stream; private SslStream CreateSslStream(ICertificateConfig certConfig) { //Enable client certificate function only if ClientCertificateRequired is true in the configuration if(!certConfig.ClientCertificateRequired) return new SslStream(new NetworkStream(Client), false); //Subscribe the client validation callback return new SslStream(new NetworkStream(Client), false, ValidateClientCertificate); } private bool ValidateClientCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { var session = AppSession; //Invoke the AppServer's method ValidateClientCertificate var clientCertificateValidator = session.AppServer as IRemoteCertificateValidator; if (clientCertificateValidator != null) return clientCertificateValidator.Validate(session, sender, certificate, chain, sslPolicyErrors); //Return the native validation result return sslPolicyErrors == SslPolicyErrors.None; } private IAsyncResult BeginInitStream(AsyncCallback asyncCallback) { IAsyncResult result = null; var certConfig = AppSession.Config.Certificate; var secureProtocol = SecureProtocol; switch (secureProtocol) { case (SslProtocols.None): m_Stream = new NetworkStream(Client); break; case (SslProtocols.Default): case (SslProtocols.Tls): case (SslProtocols.Ssl3): SslStream sslStream = CreateSslStream(certConfig); result = sslStream.BeginAuthenticateAsServer(AppSession.AppServer.Certificate, certConfig.ClientCertificateRequired, SslProtocols.Default, false, asyncCallback, sslStream); break; case (SslProtocols.Ssl2): SslStream ssl2Stream = CreateSslStream(certConfig); result = ssl2Stream.BeginAuthenticateAsServer(AppSession.AppServer.Certificate, certConfig.ClientCertificateRequired, SslProtocols.Ssl2, false, asyncCallback, ssl2Stream); break; default: var unknownSslStream = CreateSslStream(certConfig); result = unknownSslStream.BeginAuthenticateAsServer(AppSession.AppServer.Certificate, certConfig.ClientCertificateRequired, secureProtocol, false, asyncCallback, unknownSslStream); break; } return result; } private void OnBeginInitStreamOnSessionConnected(IAsyncResult result) { OnBeginInitStream(result, true); } private void OnBeginInitStream(IAsyncResult result) { OnBeginInitStream(result, false); } private void OnBeginInitStream(IAsyncResult result, bool connect) { var sslStream = result.AsyncState as SslStream; try { sslStream.EndAuthenticateAsServer(result); } catch (IOException exc) { LogError(exc); if (!connect)//Session was already registered this.Close(CloseReason.SocketError); OnNegotiateCompleted(false); return; } catch (Exception e) { LogError(e); if (!connect)//Session was already registered this.Close(CloseReason.SocketError); OnNegotiateCompleted(false); return; } m_Stream = sslStream; OnNegotiateCompleted(true); } protected override void SendSync(SendingQueue queue) { try { for (var i = 0; i < queue.Count; i++) { var item = queue[i]; m_Stream.Write(item.Array, item.Offset, item.Count); } OnSendingCompleted(queue); } catch (Exception e) { LogError(e); OnSendError(queue, CloseReason.SocketError); return; } } protected override void OnSendingCompleted(SendingQueue queue) { try { m_Stream.Flush(); } catch (Exception e) { LogError(e); OnSendError(queue, CloseReason.SocketError); return; } base.OnSendingCompleted(queue); } protected override void SendAsync(SendingQueue queue) { try { var item = queue[queue.Position]; m_Stream.BeginWrite(item.Array, item.Offset, item.Count, OnEndWrite, queue); } catch (Exception e) { LogError(e); OnSendError(queue, CloseReason.SocketError); } } private void OnEndWrite(IAsyncResult result) { var queue = result.AsyncState as SendingQueue; try { m_Stream.EndWrite(result); } catch (Exception e) { LogError(e); OnSendError(queue, CloseReason.SocketError); return; } var nextPos = queue.Position + 1; //Has more data to send if (nextPos < queue.Count) { queue.Position = nextPos; SendAsync(queue); return; } OnSendingCompleted(queue); } public override void ApplySecureProtocol() { var asyncResult = BeginInitStream(OnBeginInitStream); if (asyncResult != null) asyncResult.AsyncWaitHandle.WaitOne(); } public SocketAsyncEventArgsProxy SocketAsyncProxy { get; private set; } ILog ILoggerProvider.Logger { get { return AppSession.Logger; } } public override int OrigReceiveOffset { get { return SocketAsyncProxy.OrigOffset; } } private bool m_NegotiateResult = false; void INegotiateSocketSession.Negotiate() { IAsyncResult asyncResult; try { asyncResult = BeginInitStream(OnBeginInitStreamOnSessionConnected); } catch (Exception e) { LogError(e); OnNegotiateCompleted(false); return; } if (asyncResult == null) { OnNegotiateCompleted(true); return; } } bool INegotiateSocketSession.Result { get { return m_NegotiateResult; } } private EventHandler m_NegotiateCompleted; event EventHandler INegotiateSocketSession.NegotiateCompleted { add { m_NegotiateCompleted += value; } remove { m_NegotiateCompleted -= value; } } private void OnNegotiateCompleted(bool negotiateResult) { m_NegotiateResult = negotiateResult; //One time event handler var handler = Interlocked.Exchange<EventHandler>(ref m_NegotiateCompleted, null); if (handler == null) return; handler(this, EventArgs.Empty); } }
SuperSocket.SocketEngine.AsyncStreamSocketSession 《 SuperSocket.SocketEngine.AsyncSocketServer 《 SocketServerFactory 《 AppServerBase
在SuperSocket.SocketBase.AppServerBase类里
private bool SetupSocketServer() { try { m_SocketServer = m_SocketServerFactory.CreateSocketServer<TRequestInfo>(this, m_Listeners, Config); return m_SocketServer != null; } catch (Exception e) { if (Logger.IsErrorEnabled) Logger.Error(e); return false; } }
#region IActiveConnector /// <summary> /// Connect the remote endpoint actively. /// </summary> /// <param name="targetEndPoint">The target end point.</param> /// <param name="localEndPoint">The local end point.</param> /// <returns></returns> /// <exception cref="System.Exception">This server cannot support active connect.</exception> Task<ActiveConnectResult> IActiveConnector.ActiveConnect(EndPoint targetEndPoint, EndPoint localEndPoint) { var activeConnector = m_SocketServer as IActiveConnector; if (activeConnector == null) throw new Exception("This server cannot support active connect."); return activeConnector.ActiveConnect(targetEndPoint, localEndPoint); } /// <summary> /// Connect the remote endpoint actively. /// </summary> /// <param name="targetEndPoint">The target end point.</param> /// <returns></returns> /// <exception cref="System.Exception">This server cannot support active connect.</exception> Task<ActiveConnectResult> IActiveConnector.ActiveConnect(EndPoint targetEndPoint) { return ((IActiveConnector)this).ActiveConnect(targetEndPoint, null); } #endregion IActiveConnector
private bool SetupSocketServer() { try { m_SocketServer = m_SocketServerFactory.CreateSocketServer<TRequestInfo>(this, m_Listeners, Config); return m_SocketServer != null; } catch (Exception e) { if (Logger.IsErrorEnabled) Logger.Error(e); return false; } }
private void SetupBasic(IRootConfig rootConfig, IServerConfig config, ISocketServerFactory socketServerFactory) { if (rootConfig == null) throw new ArgumentNullException("rootConfig"); RootConfig = rootConfig; if (config == null) throw new ArgumentNullException("config"); if (!string.IsNullOrEmpty(config.Name)) m_Name = config.Name; else m_Name = string.Format("{0}-{1}", this.GetType().Name, Math.Abs(this.GetHashCode())); Config = config; SetDefaultCulture(rootConfig, config); if (!m_ThreadPoolConfigured) { if (!TheadPoolEx.ResetThreadPool(rootConfig.MaxWorkingThreads >= 0 ? rootConfig.MaxWorkingThreads : new Nullable<int>(), rootConfig.MaxCompletionPortThreads >= 0 ? rootConfig.MaxCompletionPortThreads : new Nullable<int>(), rootConfig.MinWorkingThreads >= 0 ? rootConfig.MinWorkingThreads : new Nullable<int>(), rootConfig.MinCompletionPortThreads >= 0 ? rootConfig.MinCompletionPortThreads : new Nullable<int>())) { throw new Exception("Failed to configure thread pool!"); } m_ThreadPoolConfigured = true; } if (socketServerFactory == null) { var socketServerFactoryType = Type.GetType("SuperSocket.SocketEngine.SocketServerFactory, SuperSocket.SocketEngine", true); socketServerFactory = (ISocketServerFactory)Activator.CreateInstance(socketServerFactoryType); } m_SocketServerFactory = socketServerFactory; //Read text encoding from the configuration if (!string.IsNullOrEmpty(config.TextEncoding)) TextEncoding = Encoding.GetEncoding(config.TextEncoding); else TextEncoding = new ASCIIEncoding(); }
请天就写到这里了,看到这里你可以结合源码应该能知道SuperSocket是如何发送数据和接收数据。当前接收的数据还是停留在
TCP UDP层面上。下一稿将说明应用动,协议和命令是怎样工作。
以上只是个人想法和实践经验所得,如果有文字错误和语法错误,请加以指点!
QQ:247039968
emil:wujc@younger.com
无论是美女的歌声,还是鬣狗的狂吠,无论是鳄鱼的眼泪,还是恶狼的嚎叫,都不会使我动摇