應用C#來編寫一個異步的Socket辦事器。本站提示廣大學習愛好者:(應用C#來編寫一個異步的Socket辦事器)文章只能為提供參考,不一定能成為您想要的結果。以下是應用C#來編寫一個異步的Socket辦事器正文
引見
我比來須要為一個.net項目預備一個外部線程通訊機制. 項目有多個應用ASP.NET,Windows 表單和掌握台運用法式的辦事器和客戶端組成. 斟酌到完成的能夠性,我下定決計要應用原生的socket,而不是很多.NET中曾經提早為我們構建好的組件, 像是所謂的管道, NetTcpClient 還有 Azure 辦事總線.
這篇文章中的辦事器基於System.Net.Sockets類異步辦法. 這些許可你支撐年夜量的socket客戶端, 而一個客戶真個銜接是獨一的壅塞機制. 壅塞的時光是可以疏忽不記得,所以辦事器根本上是在當作一個多線程socket辦事器在運作的.
配景
原生的socket在為你供給通訊層面的完整掌握權上具有優勢, 而在處置分歧的數據類型是具有很年夜的靈巧性. 你乃至可以經由過程socket發送序列化了的CLR對象,雖然我在這裡不會那樣做. 這個項目將會想你展現若何在socket之間發送文本.
代碼的應用
應用上面的代碼,你初始化了一個Server類,並運轉了Start()辦法:
Server myServer = new Server(); myServer.Start();
假如你籌劃在一個Windows表單中治理辦事器的話,我建議應用一個BackgroundWorker, 由於socket辦法(普通會是ManualResentEvent) 將會壅塞GUI線程的運轉.
Server 類:
using System.Net.Sockets; public class Server { private static Socket listener; public static ManualResetEvent allDone = new ManualResetEvent(false); public const int _bufferSize = 1024; public const int _port = 50000; public static bool _isRunning = true; class StateObject { public Socket workSocket = null; public byte[] buffer = new byte[bufferSize]; public StringBuilder sb = new StringBuilder(); } // Returns the string between str1 and str2 static string Between(string str, string str1, string str2) { int i1 = 0, i2 = 0; string rtn = ""; i1 = str.IndexOf(str1, StringComparison.InvariantCultureIgnoreCase); if (i1 > -1) { i2 = str.IndexOf(str2, i1 + 1, StringComparison.InvariantCultureIgnoreCase); if (i2 > -1) { rtn = str.Substring(i1 + str1.Length, i2 - i1 - str1.Length); } } return rtn; } // Checks if the socket is connected static bool IsSocketConnected(Socket s) { return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected); } // Insert all the other methods here. }
ManualResetEvent 是一個完成了你的socket辦事器中事宜的.NET類. 我們須要這個項目在我們想要宣布壅塞操作的時刻向代碼發送旌旗燈號. 你可以實驗一下用bufferSize來適配你的需求. 假如能預期到新聞的年夜小, 應用byte單元來設置新聞的年夜小參數bufferSize. port是偵聽TCP的端口參數. 要認識到為其它運用法式伺服所應用的接口. 假如你想要可以或許便利地停滯辦事器,你須要完成一些機制來將_isRunning設置成false. 這普通可以借助於應用一個 BackgroundWorker做到, 個中你可使用myWorker.CancellationPending調換_isRunning. 我提到_isRunning的緣由是給你在處置撤消操作的成績上供給一個偏向, 並向你展現偵聽器可以便利的停滯的.
Between() 和IsSocketConnected() 是幫助辦法.
如今轉過去看看辦法. 起首是Start()辦法:
public void Start() { IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName()); IPEndPoint localEP = new IPEndPoint(IPAddress.Any, _port); listener = new Socket(localEP.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listener.Bind(localEP); while (_IsRunning) { allDone.Reset(); listener.Listen(10); listener.BeginAccept(new AsyncCallback(acceptCallback), listener); bool isRequest = allDone.WaitOne(new TimeSpan(12, 0, 0)); // Blocks for 12 hours if (!isRequest) { allDone.Set(); // Do some work here every 12 hours } } listener.Close(); }
這個辦法初始化了偵聽器socket, 並開端期待用戶銜接的到來. 項目中重要的形式是應用異步委派. 異步委派是在挪用者中的狀況轉變時被異步驟用的辦法. isRequest 告知你WaitOne 能否曾經由於有客戶端銜接或許超時而加入.
假如你有年夜量的客戶端銜接同時產生, 斟酌進步Listen()辦法的隊列參數.
如今來看看下一個辦法, acceptCallback . 這個辦法由listener.BeginAccept異步驟用. 當辦法完成履行時,偵聽器會立刻偵聽新的客戶端.
static void acceptCallback(IAsyncResult ar) { // Get the listener that handles the client request. Socket listener = (Socket)ar.AsyncState; if (listener != null) { Socket handler = listener.EndAccept(ar); // Signal main thread to continue allDone.Set(); // Create state StateObject state = new StateObject(); state.workSocket = handler; handler.BeginReceive(state.buffer, 0, _bufferSize, 0, new AsyncCallback(readCallback), state); } }
acceptCallback 會派生出別的一個異步指派: readCallback. 這個辦法會讀取來自socket的現實數據. 我曾經為收發數據作了我本身的掌握, 關於_bufferSize來講是不變的. 一切發送到辦事器的字符串都必需用<!--SOCKET--> 和 <!--ENDSOCKET-->包起來. 異樣,客戶端在收到辦事器的呼應式,必需消除呼應信息的包裹, 後者被<!--RESPONSE--> 和 <!--ENDRESPONSE-->包了起來。
static void readCallback(IAsyncResult ar) { StateObject state = (StateObject)ar.AsyncState; Socket handler = state.workSocket; if (!IsSocketConnected(handler)) { handler.Close(); return; } int read = handler.EndReceive(ar); // Data was read from the client socket. if (read > 0) { state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, read)); if (state.sb.ToString().Contains("<!--ENDSOCKET-->")) { string toSend = ""; string cmd = ts.Strings.Between(state.sb.ToString(), "<!--SOCKET-->", "<!--ENDSOCKET-->"); switch (cmd) { case "Hi!": toSend = "How are you?"; break; case "Milky Way?": toSend = "No I am not."; break; } toSend = "<!--RESPONSE-->" + toSend + "<!--ENDRESPONSE-->"; byte[] bytesToSend = Encoding.UTF8.GetBytes(toSend); handler.BeginSend(bytesToSend, 0, bytesToSend.Length, SocketFlags.None , new AsyncCallback(sendCallback), state); } else { handler.BeginReceive(state.buffer, 0, _bufferSize, 0 , new AsyncCallback(readCallback), state); } } else { handler.Close(); } }
readCallback 會派生別的一個辦法, sendCallback, 它將會向客戶端發送要求. 假如客戶端沒有封閉銜接, sendCallback 將會向socket發送旌旗燈號以取得更多的數據.
static void sendCallback(IAsyncResult ar) { StateObject state = (StateObject)ar.AsyncState; Socket handler = state.workSocket; handler.EndSend(ar); StateObject newstate = new StateObject(); newstate.workSocket = handler; handler.BeginReceive(newstate.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(readCallback), newstate); }
我會將寫一個socket客戶端作為接洽留給讀者. socket客戶端應當應用同異步驟用異樣的編程形式. 我願望你能從這篇文章中收成樂趣,而且會像一個socket法式員那樣付諸理論!
要點
我在臨盆情況下應用了此代碼,個中的socket辦事器是一個自在文本搜刮引擎。 SQL Server缺少對自在文本搜刮支撐(你可使用自在文本索引,但它們是遲緩和昂貴的)。socket辦事器負載了年夜量導向IEnumerables的文本數據,並應用Linq來搜刮文本。來自socket辦事器的呼應從數百萬行的Unicode文本數據中搜刮時光在幾毫秒內。我們還應用了三個散布式的Sphinx辦事器(www.sphinxsearch.com)。socket辦事器充任了Sphinx辦事器的高速緩存。假如你須要一個疾速的自在文本搜刮引擎,我激烈建議應用Sphinx。