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 }