1. 程式人生 > 實用技巧 >SuperSocket服務端原始碼邏輯解析

SuperSocket服務端原始碼邏輯解析

SuperSocket服務端預設是以 "\r\n" (回車換行0x0D0A)作為終止符(TerminatorReceiveFilter),接收預設最多處理1024個位元組【DefaultMaxRequestLength=1024】。如果一次傳送超過1024的位元組【傳送資料中無 "\r\n"】,將丟擲ProtocolError異常,並將非法的客戶端連線關閉。

接收的結果StringRequestInfo物件由Key和Body通過空格字元拼接而成。

如果Body為空,則接收的實際結果就是 Key。

如果Body不為空,則接收的實際結果是 Key+" "+Body。

新建窗體應用程式TestServer,重新命名窗體名為FormServer。窗體設計如下:

新增對類庫。SuperSocket.Common,SuperSocket.SocketBase,SuperSocket.SocketEngine,Log4Net 四個類庫的引用。

測試源程式:

using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Config;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;

using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestServer
{
public partial class FormServer : Form
{
AppServer appServer = new AppServer();
public FormServer()
{
InitializeComponent();
}

private void FormServer_Load(object sender, EventArgs e)

{
ServerConfig serverConfig = new ServerConfig()
{
Ip = "127.0.0.1",
Port = int.Parse(txtPort.Text),
TextEncoding = "GBK",
MaxConnectionNumber = ServerConfig.DefaultMaxConnectionNumber,
Mode = SocketMode.Tcp,
MaxRequestLength = ServerConfig.DefaultMaxRequestLength,
KeepAliveTime = ServerConfig.DefaultKeepAliveTime,
KeepAliveInterval = ServerConfig.DefaultKeepAliveInterval,
ListenBacklog = ServerConfig.DefaultListenBacklog,
ReceiveBufferSize = ServerConfig.DefaultReceiveBufferSize,
SendTimeOut = ServerConfig.DefaultSendTimeout
};

if (!appServer.Setup(serverConfig))
{
MessageBox.Show("開啟監聽埠失敗");
return;
}

if (!appServer.Start())
{
MessageBox.Show("伺服器開啟監聽失敗");
return;
}

DisplayContent(string.Format("伺服器啟動監聽成功,伺服器IP:{0},埠:{1}...", appServer.Config.Ip, appServer.Config.Port));

//客戶端連線事件
appServer.NewSessionConnected += AppServer_NewSessionConnected;
//接收事件
appServer.NewRequestReceived += AppServer_NewRequestReceived; ;
//客戶端已關閉事件
appServer.SessionClosed += AppServer_SessionClosed; ;
}

private void AppServer_SessionClosed(AppSession session, SuperSocket.SocketBase.CloseReason value)
{
string sessionIp = string.Format("{0}:{1}", session.RemoteEndPoint.Address, session.RemoteEndPoint.Port);
DisplayContent(string.Format("客戶端已關閉:{0},埠:{1},原因:{2}", session.RemoteEndPoint.Address, session.RemoteEndPoint.Port, value));
}

private void AppServer_NewRequestReceived(AppSession session, SuperSocket.SocketBase.Protocol.StringRequestInfo requestInfo)
{
string body = requestInfo.Body;
string charSet = session.Charset.BodyName;
DateTime dt = session.LastActiveTime;
string key = requestInfo.Key;//session.CurrentCommand;
string sessionIp = string.Format("{0}:{1}", session.RemoteEndPoint.Address, session.RemoteEndPoint.Port);
StringBuilder sb = new StringBuilder(sessionIp + ":\n接收內容:");
string content = key;
if (body.Length > 0)
{
content = key + (" " + body);
}
sb.Append(content);
sb.Append(" \n傳送時間:" + dt.ToString("yyyy-MM-dd HH:mm:ss"));
sb.Append(" \n字元編碼:" + charSet);
DisplayContent(sb.ToString());
}

private void AppServer_NewSessionConnected(AppSession session)
{
string sessionIp = string.Format("{0}:{1}", session.RemoteEndPoint.Address, session.RemoteEndPoint.Port);
DisplayContent(string.Format("客戶端已連線:{0},虛擬埠:{1}", session.RemoteEndPoint.Address, session.RemoteEndPoint.Port));
}

/// <summary>
/// 非同步顯示內容
/// </summary>
/// <param name="addContent"></param>
private void DisplayContent(string addContent)
{
if (this.InvokeRequired)
{
Action<string> actionUpd = DisplayContentEx;
actionUpd.BeginInvoke(addContent, null, null);
}
else
{
DisplayContentEx(addContent);
}
}

public void DisplayContentEx(string addContent)
{
this.Invoke(new MethodInvoker(() =>
{
if (rtxtDisplay.TextLength >= 10240)
{
rtxtDisplay.Clear();
}
rtxtDisplay.AppendText(addContent + "\n");
rtxtDisplay.ScrollToCaret();
}));
}
}
}
程式執行如圖:【新開一個客戶端,建立連線,併發送】

一、方法appServer.Setup(serverConfig),基本配置設定。最終的本質就是 例項化類 newAsyncSocketServer(appServer, listeners)

1.例項化類SuperSocket.SocketEngine.SocketServerFactory,如果不指定編碼格式,將預設為ASCII

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();

2.設定監聽配置:

if (config.Port > 0)
{
listeners.Add(new ListenerInfo
{
EndPoint = new IPEndPoint(ParseIPAddress(config.Ip), config.Port),
BackLog = config.ListenBacklog,
Security = BasicSecurity
});
}

3.設定接收過濾:

return new CommandLineReceiveFilterFactory(TextEncoding);

=>new CommandLineReceiveFilterFactory(encoding, new BasicRequestInfoParser());

=>newTerminatorReceiveFilterFactory("\r\n",encoding, new BasicRequestInfoParser());

//注意:這裡指定 終止符為 回車換行。

4.建立socket服務,最終目的就是例項化類:AsyncSocketServer

CreateSocketServer<TRequestInfo>(IAppServer appServer, ListenerInfo[] listeners, IServerConfig config)

=>case(SocketMode.Tcp):
return new AsyncSocketServer(appServer, listeners);

二、方法:appServer.Start()。啟動一個服務端例項。監聽

=>AsyncSocketServer 物件m_SocketServer.Start()

例項化最大連線數【MaxConnectionNumber】個非同步socket代理物件:SocketAsyncEventArgsProxy

newSocketAsyncEventArgsProxy(SocketAsyncEventArgs socketEventArgs,true);

並訂閱 【完成非同步操作】的事件 Completed

public SocketAsyncEventArgsProxy(SocketAsyncEventArgs socketEventArgs, bool isRecyclable)
{
SocketEventArgs = socketEventArgs;
OrigOffset = socketEventArgs.Offset;
SocketEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(SocketEventArgs_Completed);
IsRecyclable = isRecyclable;
}

static void SocketEventArgs_Completed(object sender, SocketAsyncEventArgs e)
{
var socketSession = e.UserToken as IAsyncSocketSession;

if (socketSession == null)
return;

if (e.LastOperation == SocketAsyncOperation.Receive)
{
socketSession.AsyncRun(() => socketSession.ProcessReceive(e));
}
else
{
throw new ArgumentException("The last operation completed on the socket was not a receive");
}
}

2.執行父類的啟動監聽:抽象類SocketServerBase的Start()方法

內部類 TcpAsyncSocketListener的Start()方法

var listener => return new TcpAsyncSocketListener(listenerInfo);

var listener = CreateListener(ListenerInfos[i]);
listener.Error += new ErrorHandler(OnListenerError);
listener.Stopped += new EventHandler(OnListenerStopped);

//當接受Accept一個客戶端時觸發
listener.NewClientAccepted += new NewClientAcceptHandler(OnNewClientAccepted);

3.執行非同步監聽類TcpAsyncSocketListener的Start(Config)方法,這才是真正的啟用監聽。

public override bool Start(IServerConfig config)
{
m_ListenSocket = new Socket(this.Info.EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

try
{
m_ListenSocket.Bind(this.Info.EndPoint);
m_ListenSocket.Listen(m_ListenBackLog);

m_ListenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
m_ListenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);

SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs();
m_AcceptSAE = acceptEventArg;
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(acceptEventArg_Completed);

if (!m_ListenSocket.AcceptAsync(acceptEventArg))
ProcessAccept(acceptEventArg);

return true;

}
catch (Exception e)
{
OnError(e);
return false;
}
}

4.等待一個新的客戶端連線:

開始一個非同步操作以接受傳入的連線嘗試。

m_ListenSocket.AcceptAsync(acceptEventArg)

當一個客戶端連線成功時,會觸發完成事件:

void ProcessAccept(SocketAsyncEventArgs e)

生成一個用於傳送和接收的Socket物件

socket = e.AcceptSocket;

if (socket != null)
OnNewClientAccepted(socket, null);

自動觸發事件:AsyncSocketServer類的

protected override void OnNewClientAccepted(ISocketListener listener, Socket client, object state)