花了幾天時間了解了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 這個類是工作最底運類
SupperSocket深入淺出(一)
從這個類可以知道資料的接收來源。
這個類是由
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);
}
}
View Code
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:[email protected]
無論是美女的歌聲,還是鬣狗的狂吠,無論是鳄魚的眼淚,還是惡狼的嚎叫,都不會使我動搖