看了系列一 我们开启了对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 else103 {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 try155 {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 }
运行结果看出,我们连接成功并且发送消息成功。