看了系列一 我們開啟了對socket tcp的監聽狀態,那麼這一章我們來講解怎麼創建socket的通信代碼
我新建一個類 TSocketBase
1 public abstract class TSocketBase 2 { 3 //封裝socket 4 internal Socket _Socket; 5 //回調 6 private AsyncCallback aCallback; 7 //接受數據的緩沖區 8 private byte[] Buffers; 9 //標識是否已經釋放 10 private volatile bool IsDispose; 11 //10K的緩沖區空間 12 private int BufferSize = 10 * 1024; 13 //收取消息狀態碼 14 private SocketError ReceiveError; 15 //發送消息的狀態碼 16 private SocketError SenderError; 17 //每一次接受到的字節數 18 private int ReceiveSize = 0; 19 //接受空消息次數 20 byte ZeroCount = 0; 21 22 public abstract void Receive(byte[] rbuff); 23 24 public void SetSocket() 25 { 26 this.aCallback = new AsyncCallback(this.ReceiveCallback); 27 this.IsDispose = false; 28 this._Socket.ReceiveBufferSize = this.BufferSize; 29 this._Socket.SendBufferSize = this.BufferSize; 30 this.Buffers = new byte[this.BufferSize]; 31 } 32 33 34 /// <summary> 35 /// 關閉並釋放資源 36 /// </summary> 37 /// <param name="msg"></param> 38 public void Close(string msg) 39 { 40 if (!this.IsDispose) 41 { 42 this.IsDispose = true; 43 try 44 { 45 try { this._Socket.Close(); } 46 catch { } 47 IDisposable disposable = this._Socket; 48 if (disposable != null) { disposable.Dispose(); } 49 this.Buffers = null; 50 GC.SuppressFinalize(this); 51 } 52 catch (Exception) { } 53 } 54 } 55 56 57 /// <summary> 58 /// 遞歸接收消息方法 59 /// </summary> 60 internal void ReceiveAsync() 61 { 62 try 63 { 64 if (!this.IsDispose && this._Socket.Connected) 65 { 66 this._Socket.BeginReceive(this.Buffers, 0, this.BufferSize, SocketFlags.None, out SenderError, this.aCallback, this); 67 CheckSocketError(ReceiveError); 68 } 69 } 70 catch (System.Net.Sockets.SocketException) { this.Close("鏈接已經被關閉"); } 71 catch (System.ObjectDisposedException) { this.Close("鏈接已經被關閉"); } 72 } 73 74 75 76 /// <summary> 77 /// 接收消息回調函數 78 /// </summary> 79 /// <param name="iar"></param> 80 private void ReceiveCallback(IAsyncResult iar) 81 { 82 if (!this.IsDispose) 83 { 84 try 85 { 86 //接受消息 87 ReceiveSize = _Socket.EndReceive(iar, out ReceiveError); 88 //檢查狀態碼 89 if (!CheckSocketError(ReceiveError) && SocketError.Success == ReceiveError) 90 { 91 //判斷接受的字節數 92 if (ReceiveSize > 0) 93 { 94 byte[] rbuff = new byte[ReceiveSize]; 95 Array.Copy(this.Buffers, rbuff, ReceiveSize); 96 this.Receive(rbuff); 97 //重置連續收到空字節數 98 ZeroCount = 0; 99 //繼續開始異步接受消息 100 ReceiveAsync(); 101 } 102 else 103 { 104 ZeroCount++; 105 if (ZeroCount == 5) { this.Close("錯誤鏈接"); } 106 } 107 } 108 } 109 catch (System.Net.Sockets.SocketException) { this.Close("鏈接已經被關閉"); } 110 catch (System.ObjectDisposedException) { this.Close("鏈接已經被關閉"); } 111 } 112 } 113 114 /// <summary> 115 /// 錯誤判斷 116 /// </summary> 117 /// <param name="socketError"></param> 118 /// <returns></returns> 119 bool CheckSocketError(SocketError socketError) 120 { 121 switch ((socketError)) 122 { 123 case SocketError.SocketError: 124 case SocketError.VersionNotSupported: 125 case SocketError.TryAgain: 126 case SocketError.ProtocolFamilyNotSupported: 127 case SocketError.ConnectionAborted: 128 case SocketError.ConnectionRefused: 129 case SocketError.ConnectionReset: 130 case SocketError.Disconnecting: 131 case SocketError.HostDown: 132 case SocketError.HostNotFound: 133 case SocketError.HostUnreachable: 134 case SocketError.NetworkDown: 135 case SocketError.NetworkReset: 136 case SocketError.NetworkUnreachable: 137 case SocketError.NoData: 138 case SocketError.OperationAborted: 139 case SocketError.Shutdown: 140 case SocketError.SystemNotReady: 141 case SocketError.TooManyOpenSockets: 142 this.Close(socketError.ToString()); 143 return true; 144 } 145 return false; 146 } 147 148 /// <summary> 149 /// 發送消息方法 150 /// </summary> 151 internal int SendMsg(byte[] buffer) 152 { 153 int size = 0; 154 try 155 { 156 if (!this.IsDispose) 157 { 158 size = this._Socket.Send(buffer, 0, buffer.Length, SocketFlags.None, out SenderError); 159 CheckSocketError(SenderError); 160 } 161 } 162 catch (System.ObjectDisposedException) { this.Close("鏈接已經被關閉"); } 163 catch (System.Net.Sockets.SocketException) { this.Close("鏈接已經被關閉"); } 164 buffer = null; 165 return size; 166 } 167 }
上面我們事先了socket的異步接受消息,和同步發送消息已經關閉釋放資源代碼
接受消息net底層提供的接受消息的方法有很多,為什麼我們要選擇上面所寫的呢?那是為了兼容U3D,silverlight, wpf, wp, wf,等程序可執行,不在重復做相同工作。
現在我們來創建一個實現類 TSocketClient
1 public class TSocketClient : TSocketBase 2 { 3 /// <summary> 4 /// 是否是服務器端的資源 5 /// </summary> 6 bool isServer = false; 7 8 /// <summary> 9 /// 客戶端主動請求服務器 10 /// </summary> 11 /// <param name="ip"></param> 12 /// <param name="port"></param> 13 public TSocketClient(string ip = "127.0.0.1", int port = 9527) 14 { 15 isServer = false; 16 this._Socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 17 this._Socket.Connect(ip, port); 18 this.SetSocket(); 19 this.ReceiveAsync(); 20 } 21 /// <summary> 22 /// 這個是服務器收到有效鏈接初始化 23 /// </summary> 24 /// <param name="socket"></param> 25 public TSocketClient(Socket socket) 26 { 27 isServer = true; 28 this._Socket = socket; 29 this.SetSocket(); 30 this.ReceiveAsync(); 31 } 32 33 /// <summary> 34 /// 收到消息後 35 /// </summary> 36 /// <param name="rbuff"></param> 37 public override void Receive(byte[] rbuff) 38 { 39 Console.WriteLine("Receive Msg:" + System.Text.UTF8Encoding.Default.GetString(rbuff)); 40 if (isServer) 41 { 42 this.SendMsg(System.Text.UTF8Encoding.Default.GetBytes("Holle Client!")); 43 } 44 } 45 }
因為是測試示例,所以我把服務器和客戶端實現類寫成了,只是用來不同的構造函數來區分,是客戶端還是服務器的標識
接下來我們測試一下代碼
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 TCPListener tcp = new TCPListener(); 6 TSocketClient client = new TSocketClient(); 7 client.SendMsg(System.Text.UTF8Encoding.Default.GetBytes("Holle Server!")); 8 Console.ReadLine(); 9 } 10 }
運行結果看出,我們連接成功並且發送消息成功。