socket的應用場景,在快速,穩定,保持長連接的數據傳輸代碼。Http也是socket封裝出來的,基於一次請求一次回復,然後斷開的socket連接封裝。
比如我們常見的游戲服務器,目前的很火的物聯網服務器,都需要開啟socket服務器去監聽實時傳輸的數據。
那麼我們如何實現socket的監聽呢。說到這裡,我們需要知道,socket的監聽分為tcp和udp兩種形式,但是tcp其實是udp封裝而來的,可看做可靠的udp傳輸,基於udp的定向傳輸,收到消息回復發送方收到消息。等驗證,來實現tcp的數據傳輸,所以一般我們tcp的傳輸相對udp稍微慢一點。
我們先將一下socket 的tcp狀態創建一個TCPListener類
1 /// <summary> 2 /// 建立TCP通信監聽服務 3 /// </summary> 4 internal class TCPListener 5 { 6 private IPEndPoint _IP; 7 private Socket _Listeners; 8 private volatile bool IsInit = false; 9 List<TSocketBase> sockets = new List<TSocketBase>(); 10 11 /// <summary> 12 /// 初始化服務器 13 /// </summary> 14 public TCPListener(string ip = "0.0.0.0", int port = 9527) 15 { 16 IsInit = true; 17 IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(ip), port); 18 this._IP = localEP; 19 try 20 { 21 Console.WriteLine(string.Format("Listen Tcp -> {0}:{1} ", ip, port)); 22 this._Listeners = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 23 this._Listeners.Bind(this._IP); 24 this._Listeners.Listen(5000); 25 SocketAsyncEventArgs sea = new SocketAsyncEventArgs(); 26 sea.Completed += new EventHandler<SocketAsyncEventArgs>(this.AcceptAsync_Async); 27 this.AcceptAsync(sea); 28 } 29 catch (Exception ex) 30 { 31 Console.WriteLine(ex); 32 this.Dispose(); 33 } 34 } 35 36 private void AcceptAsync(SocketAsyncEventArgs sae) 37 { 38 if (IsInit) 39 { 40 if (!this._Listeners.AcceptAsync(sae)) 41 { 42 AcceptAsync_Async(this, sae); 43 } 44 } 45 else 46 { 47 if (sae != null) 48 { 49 sae.Dispose(); 50 } 51 } 52 } 53 54 private void AcceptAsync_Async(object sender, SocketAsyncEventArgs sae) 55 { 56 if (sae.SocketError == SocketError.Success) 57 { 58 var socket = new TSocketClient(sae.AcceptSocket); 59 sockets.Add(socket); 60 Console.WriteLine("Remote Socket LocalEndPoint:" + sae.AcceptSocket.LocalEndPoint + " RemoteEndPoint:" + sae.AcceptSocket.RemoteEndPoint.ToString()); 61 } 62 sae.AcceptSocket = null; 63 if (IsInit) 64 { 65 this._Listeners.AcceptAsync(sae); 66 } 67 else { sae.Dispose(); } 68 } 69 70 /// <summary> 71 /// 釋放資源 72 /// </summary> 73 public void Dispose() 74 { 75 if (IsInit) 76 { 77 IsInit = false; 78 this.Dispose(true); 79 GC.SuppressFinalize(this); 80 } 81 } 82 /// <summary> 83 /// 釋放所占用的資源 84 /// </summary> 85 /// <param name="flag1"></param> 86 protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool flag1) 87 { 88 if (flag1) 89 { 90 if (_Listeners != null) 91 { 92 try 93 { 94 Console.WriteLine(string.Format("Stop Listener Tcp -> {0}:{1} ", this.IP.Address.ToString(), this.IP.Port)); 95 _Listeners.Close(); 96 _Listeners.Dispose(); 97 } 98 catch { } 99 } 100 } 101 } 102 103 /// <summary> 104 /// 獲取綁定終結點 105 /// </summary> 106 public IPEndPoint IP { get { return this._IP; } } 107 }
主要兩點我們socket的初始化代碼 new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);初始化的類型是基於tcp。
還有就是我們綁定ip地址,過去很多人socket的bind地址習慣寫成127.0.0.1(測試環境)或者讀取網卡信息,讀取ip地址,這樣麻煩,代碼要寫很多,切不符合多網卡多線路實際環境。我們用0.0.0.0是表示開啟ipv4的所有線路監聽,包括你的多路網卡,以及127.0.0.1
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 TCPListener tcp = new TCPListener(); 6 Console.ReadLine(); 7 } 8 }
我們運行看一下效果
接下來我們使用telnet測試一下
開啟telnet
然後打開cmd
輸入 telnet 127.0.0.1 9527
我們看到收到了一個連接