异步 TCP 通信 ---- AsyncTcpClient

  1 using System;
  2 using System.Diagnostics;
  3 using System.Globalization;
  4 using System.Net;
  5 using System.Net.Sockets;
  6 using System.Text;
  7 using System.Threading;
  8 
  9 namespace JCommon.Network
 10 {
 11     /// <summary>
 12     /// 异步 TCP 客户端
 13     /// </summary>
 14     public class AsyncTcpClient : IDisposable
 15     {
 16         public event EventHandler<TcpDatagramReceivedEventArgs<byte[]>> DatagramReceived;       // 接收到数据报文事件
 17         public event EventHandler<TcpDatagramReceivedEventArgs<string>> PlaintextReceived;      // 接收到数据报文明文事件
 18         public event EventHandler<TcpServerConnectedEventArgs> ServerConnected;                 // 与服务器的连接已建立事件
 19         public event EventHandler<TcpServerDisconnectedEventArgs> ServerDisconnected;           // 与服务器的连接已断开事件
 20         public event EventHandler<TcpServerExceptionOccurredEventArgs> ServerExceptionOccurred; // 与服务器的连接发生异常事件
 21         
 22         private TcpClient tcpClient;
 23         private bool disposed = false;
 24         private int retries = 0;        // 重连计数
 25        
 26         public AsyncTcpClient(IPAddress remoteIPAddress, int remotePort)
 27         {
 28             this.Addresses = remoteIPAddress;
 29             this.Port = remotePort;
 30             this.Encoding = Encoding.Default;
 31             this.Retries = 3;
 32             this.RetryInterval = 5;
 33         }
 34 
 35         /// <summary>
 36         /// 是否已与服务器建立连接
 37         /// </summary>
 38         public bool Connected
 39         {
 40             get
 41             {
 42                 return this.tcpClient.Client.Connected;
 43             }
 44         }
 45 
 46         /// <summary>
 47         /// 远端服务器的IP地址列表
 48         /// </summary>
 49         public IPAddress Addresses { get; private set; }
 50 
 51         /// <summary>
 52         /// 远端服务器的端口
 53         /// </summary>
 54         public int Port { get; private set; }
 55 
 56         /// <summary>
 57         /// 连接重试次数
 58         /// </summary>
 59         public int Retries { get; set; }
 60 
 61         /// <summary>
 62         /// 连接重试间隔
 63         /// </summary>
 64         public int RetryInterval { get; set; }
 65 
 66         /// <summary>
 67         /// 远端服务器终结点
 68         /// </summary>
 69         public IPEndPoint RemoteIPEndPoint
 70         {
 71             get
 72             {
 73                 return new IPEndPoint(this.Addresses, this.Port);
 74             }
 75         }
 76 
 77         /// <summary>
 78         /// 通信所使用的编码
 79         /// </summary>
 80         public Encoding Encoding { get; set; }
 81 
 82         /// <summary>
 83         /// 连接到服务器
 84         /// </summary>
 85         /// <returns></returns>
 86         public AsyncTcpClient Connect()
 87         {
 88             this.tcpClient = new TcpClient();
 89             if (!this.Connected)
 90             {
 91                 this.tcpClient.BeginConnect(this.Addresses, this.Port, new AsyncCallback(this.HandleTcpServerConnected), this.tcpClient);
 92             }
 93             return this;
 94         }
 95 
 96         /// <summary>
 97         /// 关闭与服务器的连接
 98         /// </summary>
 99         /// <returns>异步TCP客户端</returns>
100         public AsyncTcpClient Close()
101         {
102             if (this.Connected)
103             {
104                 this.retries = 0;
105                 this.tcpClient.Close();
106                 this.RaiseServerDisconnected(this.Addresses, this.Port);
107             }
108             return this;
109         }
110 
111         private void HandleTcpServerConnected(IAsyncResult ar)
112         {
113             try
114             {
115                 this.tcpClient.EndConnect(ar);
116                 this.RaiseServerConnected(this.Addresses, this.Port);
117                 this.retries = 0;
118             }
119             catch (Exception ex)
120             {
121                 // 限制重连次数
122                 if (this.Retries > 0)
123                 {
124                     if (this.retries > 0)
125                     {
126                         Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Connect to server with retry {0} failed.", retries));
127                     }
128 
129                     this.retries++;
130                     if (this.retries > this.Retries)
131                     {
132                         this.RaiseServerExceptionOccurred(this.Addresses, this.Port, ex);
133                         return;
134                     }
135                 }
136                 
137                 Thread.Sleep(TimeSpan.FromSeconds((double)this.RetryInterval));
138                 this.Connect();
139                 return;
140             }
141             byte[] buffer = new byte[this.tcpClient.ReceiveBufferSize];
142             this.tcpClient.GetStream().BeginRead(buffer, 0, buffer.Length, new AsyncCallback(this.HandleDatagramReceived), buffer);
143         }
144 
145         private void HandleDatagramReceived(IAsyncResult ar)
146         {
147             try
148             {
149                 NetworkStream stream = this.tcpClient.GetStream();
150                 int numberOfReadBytes = 0;
151                 try
152                 {
153                     numberOfReadBytes = stream.EndRead(ar);
154                 }
155                 catch
156                 {
157                     numberOfReadBytes = 0;
158                 }
159                 if (numberOfReadBytes == 0)
160                 {
161                     this.Close();
162                     
163                     // 断开重连可写在这里
164                     Trace.WriteLine("连接断开,尝试重连...");
165                     this.Connect();
166                 }
167                 else
168                 {
169                     byte[] buffer = (byte[])ar.AsyncState;
170                     byte[] receivedBytes = new byte[numberOfReadBytes];
171                     Buffer.BlockCopy(buffer, 0, receivedBytes, 0, numberOfReadBytes);
172                     this.RaiseDatagramReceived(this.tcpClient, receivedBytes);
173                     this.RaisePlaintextReceived(this.tcpClient, receivedBytes);
174 
175                     // then start reading from the network again
176                     stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(this.HandleDatagramReceived), buffer);
177                 }
178             }
179             catch(Exception ex)
180             {
181                 Trace.WriteLine(ex.Message);
182             }
183         }
184 
185         private void RaiseDatagramReceived(TcpClient sender, byte[] datagram)
186         {
187             if (this.DatagramReceived != null)
188             {
189                 this.DatagramReceived(this, new TcpDatagramReceivedEventArgs<byte[]>(sender, datagram));
190             }
191         }
192 
193         private void RaisePlaintextReceived(TcpClient sender, byte[] datagram)
194         {
195             if (this.PlaintextReceived != null)
196             {
197                 this.PlaintextReceived(this, new TcpDatagramReceivedEventArgs<string>(sender, this.Encoding.GetString(datagram, 0, datagram.Length)));
198             }
199         }
200 
201         private void RaiseServerConnected(IPAddress ipAddresses, int port)
202         {
203             if (this.ServerConnected != null)
204             {
205                 this.ServerConnected(this, new TcpServerConnectedEventArgs(ipAddresses, port));
206             }
207         }
208 
209         private void RaiseServerDisconnected(IPAddress ipAddresses, int port)
210         {
211             if (this.ServerDisconnected != null)
212             {
213                 this.ServerDisconnected(this, new TcpServerDisconnectedEventArgs(ipAddresses, port));
214             }
215         }
216 
217         private void RaiseServerExceptionOccurred(IPAddress ipAddresses, int port, Exception innerException)
218         {
219             if (this.ServerExceptionOccurred != null)
220             {
221                 this.ServerExceptionOccurred(this, new TcpServerExceptionOccurredEventArgs(ipAddresses, port, innerException));
222             }
223         }
224 
225         /// <summary>
226         /// 发送报文
227         /// </summary>
228         /// <param name="datagram"></param>
229         public void Send(byte[] datagram)
230         {
231             if (datagram == null)
232             {
233                 throw new ArgumentNullException("datagram");
234             }
235             if (!this.Connected)
236             {
237                 this.RaiseServerDisconnected(this.Addresses, this.Port);
238                 throw new InvalidProgramException("This client has not connected to server.");
239             }
240             this.tcpClient.GetStream().BeginWrite(datagram, 0, datagram.Length, new AsyncCallback(this.HandleDatagramWritten), this.tcpClient);
241         }
242 
243         private void HandleDatagramWritten(IAsyncResult ar)
244         {
245             ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar);
246         }
247 
248         public void Send(string datagram)
249         {
250             this.Send(this.Encoding.GetBytes(datagram));
251         }
252 
253         /// <summary>
254         /// 释放非托管资源
255         /// </summary>
256         public void Dispose()
257         {
258             this.Dispose(true);
259             GC.SuppressFinalize(this);
260         }
261 
262         protected virtual void Dispose(bool disposing)
263         {
264             if (!this.disposed)
265             {
266                 if (disposing)
267                 {
268                     try
269                     {
270                         this.Close();
271                         if (this.tcpClient != null)
272                         {
273                             this.tcpClient = null;
274                         }
275                     }
276                     catch// (SocketException ex)
277                     {
278                     }
279                 }
280                 this.disposed = true;
281             }
282         }
283     }
284 }
 1 using System;
 2 using System.Net.Sockets;
 3 
 4 namespace JCommon.Network
 5 {
 6     /// <summary>
 7     /// 接收到数据报文事件
 8     /// </summary>
 9     /// <typeparam name="T"></typeparam>
10     public class TcpDatagramReceivedEventArgs<T> : EventArgs
11     {
12         public TcpDatagramReceivedEventArgs(TcpClient tcpClient, T datagram)
13         {
14             this.TcpClient = tcpClient;
15             this.Datagram = datagram;
16         }
17 
18         public TcpClient TcpClient { get; private set; }
19 
20         public T Datagram { get; private set; }
21     }
22 }
 1 using System;
 2 using System.Net.Sockets;
 3 
 4 namespace JCommon.Network
 5 {
 6     /// <summary>
 7     /// 接收到数据报文事件
 8     /// </summary>
 9     /// <typeparam name="T"></typeparam>
10     public class TcpDatagramReceivedEventArgs<T> : EventArgs
11     {
12         public TcpDatagramReceivedEventArgs(TcpClient tcpClient, T datagram)
13         {
14             this.TcpClient = tcpClient;
15             this.Datagram = datagram;
16         }
17 
18         public TcpClient TcpClient { get; private set; }
19 
20         public T Datagram { get; private set; }
21     }
22 }
 1 using System;
 2 using System.Globalization;
 3 using System.Net;
 4 
 5 namespace JCommon.Network
 6 {
 7     /// <summary>
 8     /// 与服务器的连接已建立事件
 9     /// </summary>
10     public class TcpServerConnectedEventArgs : EventArgs
11     {
12         public TcpServerConnectedEventArgs(IPAddress ipAddress, int port)
13         {
14             if (ipAddress == null)
15             {
16                 throw new ArgumentNullException("ipAddress");
17             }
18             this.Address = ipAddress;
19             this.Port = port;
20         }
21 
22         public IPAddress Address { get; private set; }
23 
24         public int Port { get; private set; }
25 
26         public override string ToString()
27         {
28             return this.Address + ":" + this.Port.ToString(CultureInfo.InvariantCulture);
29         }
30     }
31 }
 1 using System;
 2 using System.Globalization;
 3 using System.Net;
 4 
 5 namespace JCommon.Network
 6 {
 7     /// <summary>
 8     /// 与服务器的连接已断开事件
 9     /// </summary>
10     public class TcpServerDisconnectedEventArgs : EventArgs
11     {
12         public TcpServerDisconnectedEventArgs(IPAddress ipAddress, int port)
13         {
14             if (ipAddress == null)
15             {
16                 throw new ArgumentNullException("ipAddress");
17             }
18             this.Address = ipAddress;
19             this.Port = port;
20         }
21 
22         public IPAddress Address { get; private set; }
23 
24         public int Port { get; private set; }
25 
26         public override string ToString()
27         {
28             return this.Address + ":" + this.Port.ToString(CultureInfo.InvariantCulture);
29         }
30     }
31 }
 1 using System;
 2 using System.Globalization;
 3 using System.Net;
 4 
 5 namespace JCommon.Network
 6 {
 7     /// <summary>
 8     /// 与服务器的连接发生异常事件
 9     /// </summary>
10     public class TcpServerExceptionOccurredEventArgs : EventArgs
11     {
12         public TcpServerExceptionOccurredEventArgs(IPAddress ipAddresses, int port, Exception innerException)
13         {
14             if (ipAddresses == null)
15             {
16                 throw new ArgumentNullException("ipAddress");
17             }
18             this.Address = ipAddresses;
19             this.Port = port;
20             this.Exception = innerException;
21         }
22 
23         public IPAddress Address { get; private set; }
24 
25         public int Port { get; private set; }
26 
27         public Exception Exception { get; private set; }
28 
29         public override string ToString()
30         {
31             return this.Address + ":" + this.Port.ToString(CultureInfo.InvariantCulture);
32         }
33     }
34 }
  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Threading.Tasks;
  9 using System.Windows.Forms;
 10 using JCommon.Network;
 11 using System.Net;
 12 
 13 namespace JCommon.Test
 14 {
 15     public partial class FrmTcpClient_aync : Form
 16     {
 17         Network.AsyncTcpClient client = null;
 18 
 19 
 20         DelMsgShow msgShow;
 21 
 22         void MsgShow(string str)
 23         {
 24             if (richTextBox1.InvokeRequired)
 25             {
 26                 richTextBox1.Invoke(new Action<string>(MsgShow), new object[] { str });
 27             }
 28             else
 29             {
 30                 richTextBox1.AppendText(str);
 31                 richTextBox1.AppendText("\r\n");
 32                 richTextBox1.ScrollToCaret();
 33             }
 34         }
 35 
 36 
 37         public FrmTcpClient_aync()
 38         {
 39             InitializeComponent();
 40         }
 41 
 42         private void button1_Click(object sender, EventArgs e)
 43         {
 44             string strServerIP = textBox1.Text;
 45             int nPort = textBox2.Text.ToInt();
 46 
 47             button1.Enabled = false;
 48             if(this.client == null)
 49             {
 50                 IPEndPoint ip = new IPEndPoint(IPAddress.Parse(strServerIP), nPort);
 51                 this.client = new AsyncTcpClient(IPAddress.Parse(strServerIP), nPort);
 52 
 53                 this.client.PlaintextReceived += this.onPlaintextReceived;
 54                 this.client.ServerConnected += this.onServerConnected;
 55                 this.client.ServerDisconnected += this.onServerDisconnected;
 56             }
 57 
 58             string str = button1.Text;
 59             if (str == "连接")
 60             {
 61                 this.client.Connect();
 62                 button1.Text = "断开连接";
 63             }
 64             else
 65             {
 66                 this.client.Close();
 67                 button1.Text = "连接";
 68             }
 69 
 70             button1.Enabled = true;
 71         }
 72 
 73         private void button2_Click(object sender, EventArgs e)
 74         {
 75             string str = richTextBox2.Text;
 76             if (this.client.Connected)
 77                 this.client.Send(str);
 78         }
 79 
 80         /// <summary>
 81         /// 
 82         /// </summary>
 83         /// <param name="sender"></param>
 84         /// <param name="e"></param>
 85         public void onPlaintextReceived(object sender, TcpDatagramReceivedEventArgs<string> e)
 86         {
 87             string data = e.Datagram;
 88             msgShow("收到:" + data);
 89         }
 90 
 91         public void onServerConnected(object sender, TcpServerConnectedEventArgs e)
 92         {
 93             msgShow(this.Name + "连接成功");
 94         }
 95 
 96         public void onServerDisconnected(object sender, TcpServerDisconnectedEventArgs e)
 97         {
 98             msgShow(this.Name + "连接断开");
 99         }
100 
101         private void FrmTcpClient_aync_Load(object sender, EventArgs e)
102         {
103             msgShow = this.MsgShow;
104             this.textBox1.Text = "127.0.0.1";
105             this.textBox2.Text = "20000";
106         }
107     }
108 }
  1 namespace JCommon.Test
  2 {
  3     partial class FrmTcpClient_aync
  4     {
  5         /// <summary>
  6         /// Required designer variable.
  7         /// </summary>
  8         private System.ComponentModel.IContainer components = null;
  9 
 10         /// <summary>
 11         /// Clean up any resources being used.
 12         /// </summary>
 13         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
 14         protected override void Dispose(bool disposing)
 15         {
 16             if (disposing && (components != null))
 17             {
 18                 components.Dispose();
 19             }
 20             base.Dispose(disposing);
 21         }
 22 
 23         #region Windows Form Designer generated code
 24 
 25         /// <summary>
 26         /// Required method for Designer support - do not modify
 27         /// the contents of this method with the code editor.
 28         /// </summary>
 29         private void InitializeComponent()
 30         {
 31             this.richTextBox2 = new System.Windows.Forms.RichTextBox();
 32             this.richTextBox1 = new System.Windows.Forms.RichTextBox();
 33             this.button2 = new System.Windows.Forms.Button();
 34             this.button1 = new System.Windows.Forms.Button();
 35             this.textBox2 = new System.Windows.Forms.TextBox();
 36             this.textBox1 = new System.Windows.Forms.TextBox();
 37             this.label2 = new System.Windows.Forms.Label();
 38             this.label1 = new System.Windows.Forms.Label();
 39             this.SuspendLayout();
 40             // 
 41             // richTextBox2
 42             // 
 43             this.richTextBox2.Location = new System.Drawing.Point(32, 234);
 44             this.richTextBox2.Name = "richTextBox2";
 45             this.richTextBox2.Size = new System.Drawing.Size(306, 51);
 46             this.richTextBox2.TabIndex = 4;
 47             this.richTextBox2.Text = "";
 48             // 
 49             // richTextBox1
 50             // 
 51             this.richTextBox1.Location = new System.Drawing.Point(29, 65);
 52             this.richTextBox1.Name = "richTextBox1";
 53             this.richTextBox1.Size = new System.Drawing.Size(385, 155);
 54             this.richTextBox1.TabIndex = 3;
 55             this.richTextBox1.Text = "";
 56             // 
 57             // button2
 58             // 
 59             this.button2.Location = new System.Drawing.Point(344, 234);
 60             this.button2.Name = "button2";
 61             this.button2.Size = new System.Drawing.Size(70, 51);
 62             this.button2.TabIndex = 5;
 63             this.button2.Text = "发送";
 64             this.button2.UseVisualStyleBackColor = true;
 65             this.button2.Click += new System.EventHandler(this.button2_Click);
 66             // 
 67             // button1
 68             // 
 69             this.button1.Location = new System.Drawing.Point(320, 19);
 70             this.button1.Name = "button1";
 71             this.button1.Size = new System.Drawing.Size(75, 23);
 72             this.button1.TabIndex = 2;
 73             this.button1.Text = "连接";
 74             this.button1.UseVisualStyleBackColor = true;
 75             this.button1.Click += new System.EventHandler(this.button1_Click);
 76             // 
 77             // textBox2
 78             // 
 79             this.textBox2.Location = new System.Drawing.Point(214, 21);
 80             this.textBox2.Name = "textBox2";
 81             this.textBox2.Size = new System.Drawing.Size(84, 21);
 82             this.textBox2.TabIndex = 1;
 83             // 
 84             // textBox1
 85             // 
 86             this.textBox1.Location = new System.Drawing.Point(71, 21);
 87             this.textBox1.Name = "textBox1";
 88             this.textBox1.Size = new System.Drawing.Size(84, 21);
 89             this.textBox1.TabIndex = 0;
 90             // 
 91             // label2
 92             // 
 93             this.label2.AutoSize = true;
 94             this.label2.Location = new System.Drawing.Point(179, 24);
 95             this.label2.Name = "label2";
 96             this.label2.Size = new System.Drawing.Size(29, 12);
 97             this.label2.TabIndex = 7;
 98             this.label2.Text = "Port";
 99             // 
100             // label1
101             // 
102             this.label1.AutoSize = true;
103             this.label1.Location = new System.Drawing.Point(37, 24);
104             this.label1.Name = "label1";
105             this.label1.Size = new System.Drawing.Size(17, 12);
106             this.label1.TabIndex = 6;
107             this.label1.Text = "IP";
108             // 
109             // FrmTcpClient_aync
110             // 
111             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
112             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
113             this.ClientSize = new System.Drawing.Size(456, 311);
114             this.Controls.Add(this.richTextBox2);
115             this.Controls.Add(this.richTextBox1);
116             this.Controls.Add(this.button2);
117             this.Controls.Add(this.button1);
118             this.Controls.Add(this.textBox2);
119             this.Controls.Add(this.textBox1);
120             this.Controls.Add(this.label2);
121             this.Controls.Add(this.label1);
122             this.Name = "FrmTcpClient_aync";
123             this.Text = "FrmTcpClient_aync";
124             this.Load += new System.EventHandler(this.FrmTcpClient_aync_Load);
125             this.ResumeLayout(false);
126             this.PerformLayout();
127 
128         }
129 
130         #endregion
131 
132         private System.Windows.Forms.RichTextBox richTextBox2;
133         private System.Windows.Forms.RichTextBox richTextBox1;
134         private System.Windows.Forms.Button button2;
135         private System.Windows.Forms.Button button1;
136         private System.Windows.Forms.TextBox textBox2;
137         private System.Windows.Forms.TextBox textBox1;
138         private System.Windows.Forms.Label label2;
139         private System.Windows.Forms.Label label1;
140     }
141 }

 

posted on 2020-05-21 20:35  jshchg  阅读(1551)  评论(0编辑  收藏  举报

导航