配色: 字号:
C# 对 TCP 客户端的状态封装
2016-09-14 | 阅:  转:  |  分享 
  
C#对TCP客户端的状态封装
TCP客户端连接TCP服务器端有几种应用状态:

与服务器的连接已建立
与服务器的连接已断开
与服务器的连接发生异常
应用程序可按需求合理处理这些逻辑,比如:

连接断开后自动重连
连接断开后选择备用地址重连
所有状态变化上报告警
本文描述的TcpClient实现了状态变化的事件通知机制。

复制代码
1///
2///异步TCP客户端
3///

4publicclassAsyncTcpClient:IDisposable
5{
6#regionFields
7
8privateTcpClienttcpClient;
9privatebooldisposed=false;
10privateintretries=0;
11
12#endregion
13
14#regionCtors
15
16///
17///异步TCP客户端
18///

19///远端服务器终结点
20publicAsyncTcpClient(IPEndPointremoteEP)
21:this(new[]{remoteEP.Address},remoteEP.Port)
22{
23}
24
25///
26///异步TCP客户端
27///

28///远端服务器终结点
29///本地客户端终结点
30publicAsyncTcpClient(IPEndPointremoteEP,IPEndPointlocalEP)
31:this(new[]{remoteEP.Address},remoteEP.Port,localEP)
32{
33}
34
35///
36///异步TCP客户端
37///

38///远端服务器IP地址
39///远端服务器端口
40publicAsyncTcpClient(IPAddressremoteIPAddress,intremotePort)
41:this(new[]{remoteIPAddress},remotePort)
42{
43}
44
45///
46///异步TCP客户端
47///

48///远端服务器IP地址
49///远端服务器端口
50///本地客户端终结点
51publicAsyncTcpClient(
52IPAddressremoteIPAddress,intremotePort,IPEndPointlocalEP)
53:this(new[]{remoteIPAddress},remotePort,localEP)
54{
55}
56
57///
58///异步TCP客户端
59///

60///远端服务器主机名
61///远端服务器端口
62publicAsyncTcpClient(stringremoteHostName,intremotePort)
63:this(Dns.GetHostAddresses(remoteHostName),remotePort)
64{
65}
66
67///
68///异步TCP客户端
69///

70///远端服务器主机名
71///远端服务器端口
72///本地客户端终结点
73publicAsyncTcpClient(
74stringremoteHostName,intremotePort,IPEndPointlocalEP)
75:this(Dns.GetHostAddresses(remoteHostName),remotePort,localEP)
76{
77}
78
79///
80///异步TCP客户端
81///

82///远端服务器IP地址列表
83///远端服务器端口
84publicAsyncTcpClient(IPAddress[]remoteIPAddresses,intremotePort)
85:this(remoteIPAddresses,remotePort,null)
86{
87}
88
89///
90///异步TCP客户端
91///

92///远端服务器IP地址列表
93///远端服务器端口
94///本地客户端终结点
95publicAsyncTcpClient(
96IPAddress[]remoteIPAddresses,intremotePort,IPEndPointlocalEP)
97{
98this.Addresses=remoteIPAddresses;
99this.Port=remotePort;
100this.LocalIPEndPoint=localEP;
101this.Encoding=Encoding.Default;
102
103if(this.LocalIPEndPoint!=null)
104{
105this.tcpClient=newTcpClient(this.LocalIPEndPoint);
106}
107else
108{
109this.tcpClient=newTcpClient();
110}
111
112Retries=3;
113RetryInterval=5;
114}
115
116#endregion
117
118#regionProperties
119
120///
121///是否已与服务器建立连接
122///

123publicboolConnected{get{returntcpClient.Client.Connected;}}
124///
125///远端服务器的IP地址列表
126///

127publicIPAddress[]Addresses{get;privateset;}
128///
129///远端服务器的端口
130///

131publicintPort{get;privateset;}
132///
133///连接重试次数
134///

135publicintRetries{get;set;}
136///
137///连接重试间隔
138///

139publicintRetryInterval{get;set;}
140///
141///远端服务器终结点
142///

143publicIPEndPointRemoteIPEndPoint
144{
145get{returnnewIPEndPoint(Addresses[0],Port);}
146}
147///
148///本地客户端终结点
149///

150protectedIPEndPointLocalIPEndPoint{get;privateset;}
151///
152///通信所使用的编码
153///

154publicEncodingEncoding{get;set;}
155
156#endregion
157
158#regionConnect
159
160///
161///连接到服务器
162///

163///异步TCP客户端
164publicAsyncTcpClientConnect()
165{
166if(!Connected)
167{
168//starttheasyncconnectoperation
169tcpClient.BeginConnect(
170Addresses,Port,HandleTcpServerConnected,tcpClient);
171}
172
173returnthis;
174}
175
176///
177///关闭与服务器的连接
178///

179///异步TCP客户端
180publicAsyncTcpClientClose()
181{
182if(Connected)
183{
184retries=0;
185tcpClient.Close();
186RaiseServerDisconnected(Addresses,Port);
187}
188
189returnthis;
190}
191
192#endregion
193
194#regionReceive
195
196privatevoidHandleTcpServerConnected(IAsyncResultar)
197{
198try
199{
200tcpClient.EndConnect(ar);
201RaiseServerConnected(Addresses,Port);
202retries=0;
203}
204catch(Exceptionex)
205{
206ExceptionHandler.Handle(ex);
207if(retries>0)
208{
209Logger.Debug(string.Format(CultureInfo.InvariantCulture,
210"Connecttoserverwithretry{0}failed.",retries));
211}
212
213retries++;
214if(retries>Retries)
215{
216//wehavefailedtoconnecttoalltheIPAddresses,
217//connectionhasfailedoverall.
218RaiseServerExceptionOccurred(Addresses,Port,ex);
219return;
220}
221else
222{
223Logger.Debug(string.Format(CultureInfo.InvariantCulture,
224"Waiting{0}secondsbeforeretryingtoconnecttoserver.",
225RetryInterval));
226Thread.Sleep(TimeSpan.FromSeconds(RetryInterval));
227Connect();
228return;
229}
230}
231
232//weareconnectedsuccessfullyandstartasynreadoperation.
233byte[]buffer=newbyte[tcpClient.ReceiveBufferSize];
234tcpClient.GetStream().BeginRead(
235buffer,0,buffer.Length,HandleDatagramReceived,buffer);
236}
237
238privatevoidHandleDatagramReceived(IAsyncResultar)
239{
240NetworkStreamstream=tcpClient.GetStream();
241
242intnumberOfReadBytes=0;
243try
244{
245numberOfReadBytes=stream.EndRead(ar);
246}
247catch
248{
249numberOfReadBytes=0;
250}
251
252if(numberOfReadBytes==0)
253{
254//connectionhasbeenclosed
255Close();
256return;
257}
258
259//receivedbyteandtriggereventnotification
260byte[]buffer=(byte[])ar.AsyncState;
261byte[]receivedBytes=newbyte[numberOfReadBytes];
262Buffer.BlockCopy(buffer,0,receivedBytes,0,numberOfReadBytes);
263RaiseDatagramReceived(tcpClient,receivedBytes);
264RaisePlaintextReceived(tcpClient,receivedBytes);
265
266//thenstartreadingfromthenetworkagain
267stream.BeginRead(
268buffer,0,buffer.Length,HandleDatagramReceived,buffer);
269}
270
271#endregion
272
273#regionEvents
274
275///
276///接收到数据报文事件
277///

278publiceventEventHandler>DatagramReceived;
279///
280///接收到数据报文明文事件
281///

282publiceventEventHandler>PlaintextReceived;
283
284privatevoidRaiseDatagramReceived(TcpClientsender,byte[]datagram)
285{
286if(DatagramReceived!=null)
287{
288DatagramReceived(this,
289newTcpDatagramReceivedEventArgs(sender,datagram));
290}
291}
292
293privatevoidRaisePlaintextReceived(TcpClientsender,byte[]datagram)
294{
295if(PlaintextReceived!=null)
296{
297PlaintextReceived(this,
298newTcpDatagramReceivedEventArgs(
299sender,this.Encoding.GetString(datagram,0,datagram.Length)));
300}
301}
302
303///
304///与服务器的连接已建立事件
305///

306publiceventEventHandlerServerConnected;
307///
308///与服务器的连接已断开事件
309///

310publiceventEventHandlerServerDisconnected;
311///
312///与服务器的连接发生异常事件
313///

314publiceventEventHandlerServerExceptionOccurred;
315
316privatevoidRaiseServerConnected(IPAddress[]ipAddresses,intport)
317{
318if(ServerConnected!=null)
319{
320ServerConnected(this,
321newTcpServerConnectedEventArgs(ipAddresses,port));
322}
323}
324
325privatevoidRaiseServerDisconnected(IPAddress[]ipAddresses,intport)
326{
327if(ServerDisconnected!=null)
328{
329ServerDisconnected(this,
330newTcpServerDisconnectedEventArgs(ipAddresses,port));
331}
332}
333
334privatevoidRaiseServerExceptionOccurred(
335IPAddress[]ipAddresses,intport,ExceptioninnerException)
336{
337if(ServerExceptionOccurred!=null)
338{
339ServerExceptionOccurred(this,
340newTcpServerExceptionOccurredEventArgs(
341ipAddresses,port,innerException));
342}
343}
344
345#endregion
346
347#regionSend
348
349///
350///发送报文
351///

352///报文
353publicvoidSend(byte[]datagram)
354{
355if(datagram==null)
356thrownewArgumentNullException("datagram");
357
358if(!Connected)
359{
360RaiseServerDisconnected(Addresses,Port);
361thrownewInvalidProgramException(
362"Thisclienthasnotconnectedtoserver.");
363}
364
365tcpClient.GetStream().BeginWrite(
366datagram,0,datagram.Length,HandleDatagramWritten,tcpClient);
367}
368
369privatevoidHandleDatagramWritten(IAsyncResultar)
370{
371((TcpClient)ar.AsyncState).GetStream().EndWrite(ar);
372}
373
374///
375///发送报文
376///

377///报文
378publicvoidSend(stringdatagram)
379{
380Send(this.Encoding.GetBytes(datagram));
381}
382
383#endregion
384
385#regionIDisposableMembers
386
387///
388///Performsapplication-definedtasksassociatedwithfreeing,
389///releasing,orresettingunmanagedresources.
390///

391publicvoidDispose()
392{
393Dispose(true);
394GC.SuppressFinalize(this);
395}
396
397///
398///Releasesunmanagedand-optionally-managedresources
399///

400///truetoreleasebothmanaged
401///andunmanagedresources;false
402///toreleaseonlyunmanagedresources.
403///
404protectedvirtualvoidDispose(booldisposing)
405{
406if(!this.disposed)
407{
408if(disposing)
409{
410try
411{
412Close();
413
414if(tcpClient!=null)
415{
416tcpClient=null;
417}
418}
419catch(SocketExceptionex)
420{
421ExceptionHandler.Handle(ex);
422}
423}
424
425disposed=true;
426}
427}
428
429#endregion
430}
复制代码
使用举例
复制代码
1classProgram
2{
3staticAsyncTcpClientclient;
4
5staticvoidMain(string[]args)
6{
7LogFactory.Assign(newConsoleLogFactory());
8
9//测试用,可以不指定由系统选择端口
10IPEndPointremoteEP=newIPEndPoint(IPAddress.Parse("127.0.0.1"),9999);
11IPEndPointlocalEP=newIPEndPoint(IPAddress.Parse("127.0.0.1"),9998);
12client=newAsyncTcpClient(remoteEP,localEP);
13client.www.wang027.comEncoding=Encoding.UTF8;
14client.ServerExceptionOccurred+=
15newEventHandler(client_ServerExceptionOccurred);
16client.ServerConnected+=
17newEventHandler(client_ServerConnected);
18client.ServerDisconnected+=
19newEventHandler(client_ServerDisconnected);
20client.PlaintextReceived+=
21newEventHandler>(client_PlaintextReceived);
22client.Connect();
23
24Console.WriteLine("TCPclienthasconnectedtoserver.");
25Console.WriteLine("Typesomethingtosendtoserver...");
26while(true)
27{
28try
29{
30stringtext=Console.ReadLine();
31client.Send(text);
32}
33catch(Exceptionex)
34{
35Console.WriteLine(ex.Message);
36}
37}
38}
39
40staticvoidclient_ServerExceptionOccurred(
41objectsender,TcpServerExceptionOccurredEventArgse)
42{
43Logger.Debug(string.Format(CultureInfo.InvariantCulture,
44"TCPserver{0}exceptionoccurred,{1}.",
45e.ToString(),e.Exception.Message));
46}
47
48staticvoidclient_ServerConnected(
49objectsender,TcpServerConnectedEventArgse)
50{
51Logger.Debug(string.Format(CultureInfo.InvariantCulture,
52"TCPserver{0}hasconnected.",e.ToString()));
53}
54
55staticvoidclient_ServerDisconnected(
56objectsender,TcpServerDisconnectedEventArgse)
57{
58Logger.Debug(string.Format(CultureInfo.InvariantCulture,
59"TCPserver{0}hasdisconnected.",e.ToString()));
60}
61
62staticvoidclient_PlaintextReceived(
63objectsender,TcpDatagramReceivedEventArgse)
64{
65Console.Write(string.Format("Server:{0}-->",
66e.TcpClient.Client.RemoteEndPoint.ToString()));
67Console.WriteLine(string.Format("{0}",e.Datagram));
68}
69}
复制代码
TCP客户端State
复制代码
1///
2///InternalclasstojointheTCPclientandbuffertogether
3///foreasymanagementintheserver
4///

5internalclassTcpClientState
6{
7///
8///ConstructorforanewClient
9///

10///TheTCPclient
11///Thebytearraybuffer
12publicTcpClientState(TcpClienttcpClient,byte[]buffer)
13{
14if(tcpClient==null)
15thrownewArgumentNullException("tcpClient");
16if(buffer==null)
17thrownewArgumentNullException("buffer");
18
19this.TcpClient=tcpClient;
20this.Buffer=buffer;
21}
22
23///
24///GetstheTCPClient
25///

26publicTcpClientTcpClient{get;privateset;}
27
28///
29///GetstheBuffer.
30///

31publicbyte[]Buffer{get;privateset;}
32
33///
34///Getsthenetworkstream
35///

36publicNetworkStreamNetworkStream
37{
38get{returnTcpClient.GetStream();}
39}
40}
复制代码
与客户端的连接已建立事件参数
复制代码
1///
2///与客户端的连接已建立事件参数
3///

4publicclassTcpClientConnectedEventArgs:EventArgs
5{
6///
7///与客户端的连接已建立事件参数
8///

9///客户端
10publicTcpClientConnectedEventArgs(TcpClienttcpClient)
11{
12if(tcpClient==null)
13thrownewArgumentNullException("tcpClient");
14
15this.TcpClient=tcpClient;
16}
17
18///
19///客户端
20///

21publicTcpClientTcpClient{get;privateset;}
22}
复制代码
与客户端的连接已断开事件参数
复制代码
///
///与客户端的连接已断开事件参数
///

publicclassTcpClientDisconnectedEventArgs:EventArgs
{
///
///与客户端的连接已断开事件参数
///

///客户端
publicTcpClientDisconnectedEventArgs(TcpClienttcpClient)
{
if(tcpClient==null)
thrownewArgumentNullException("tcpClient");

this.TcpClient=tcpClient;
}

///
///客户端
///

publicTcpClientTcpClient{get;privateset;}
}
复制代码
与服务器的连接发生异常事件参数
复制代码
1///
2///与服务器的连接发生异常事件参数
3///

4publicclassTcpServerExceptionOccurredEventArgs:EventArgs
5{
6///
7///与服务器的连接发生异常事件参数
8///

9///服务器IP地址列表
10///服务器端口
11///内部异常
12publicTcpServerExceptionOccurredEventArgs(
13IPAddress[]ipAddresses,intport,ExceptioninnerException)
14{
15if(ipAddresses==null)
16thrownewArgumentNullException("ipAddresses");
17
18this.Addresses=ipAddresses;
19this.Port=port;
20this.Exception=innerException;
21}
22
23///
24///服务器IP地址列表
25///

26publicIPAddress[]Addresses{get;privateset;}
27///
28///服务器端口
29///

30publicintPort{get;privateset;}
31///
32///内部异常
33///

34publicExceptionException{get;privateset;}
35
36///
37///Returnsathatrepresentsthisinstance.
38///

39///
40///Athatrepresentsthisinstance.
41///

42publicoverridestringToString()
43{
44strings=string.Empty;
45foreach(variteminAddresses)
46{
47s=s+item.ToString()+'','';
48}
49s=s.TrimEnd('','');
50s=s+":"+Port.ToString(CultureInfo.InvariantCulture);
51
52returns;
53}
54}
复制代码
接收到数据报文事件参数
复制代码
1///
2///接收到数据报文事件参数
3///

4///报文类型
5publicclassTcpDatagramReceivedEventArgs:EventArgs
6{
7///
8///接收到数据报文事件参数
9///

10///客户端
11///报文
12publicTcpDatagramReceivedEventArgs(TcpClienttcpClient,Tdatagram)
13{
14TcpClient=tcpClient;
15Datagram=datagram;
16}
17
18///
19///客户端
20///

21publicTcpClientTcpClient{get;privateset;}
22///
23///报文
24///

25publicTDatagram{get;privateset;}
26}
复制代码
与服务器的连接已建立事件参数
复制代码
1///
2///与服务器的连接已建立事件参数
3///

4publicclassTcpServerConnectedEventArgs:EventArgs
5{
6///
7///与服务器的连接已建立事件参数
8///

9///服务器IP地址列表
10///服务器端口
11publicTcpServerConnectedEventArgs(IPAddress[]ipAddresses,intport)
12{
13if(ipAddresses==null)
14thrownewArgumentNullException("ipAddresses");
15
16this.Addresses=ipAddresses;
17this.Port=port;
18}
19
20///
21///服务器IP地址列表
22///

23publicIPAddress[]Addresses{get;privateset;}
24///
25///服务器端口
26///

27publicintPort{get;privateset;}
28
29///
30///Returnsathatrepresentsthisinstance.
31///

32///
33///Athatrepresentsthisinstance.
34///

35publicoverridestringToString()
36{
37strings=string.Empty;
38foreach(variteminAddresses)
39{
40s=s+item.ToString()+'','';
41}
42s=s.TrimEnd('','');
43s=s+":"+Port.ToString(CultureInfo.InvariantCulture);
44
45returns;
46}
47}
复制代码
与服务器的连接已断开事件参数
复制代码
1///
2///与服务器的连接已断开事件参数
3///

4publicclassTcpServerDisconnectedEventArgs:EventArgs
5{
6///
7///与服务器的连接已断开事件参数
8///

9///服务器IP地址列表
10///服务器端口
11publicTcpServerDisconnectedEventArgs(IPAddress[]ipAddresses,intport)
12{
13if(ipAddresses==null)
14thrownewArgumentNullException("ipAddresses");
15
16this.Addresses=ipAddresses;
17this.Port=port;
18}
19
20///
21///服务器IP地址列表
22///

23publicIPAddress[]Addresses{get;privateset;}
24///
25///服务器端口
26///

27publicintPort{get;privateset;}
28
29///
30///Returnsathatrepresentsthisinstance.
31///

32///
33///Athatrepresentsthisinstance.
34///

35publicoverridestringToString()
36{
37strings=string.Empty;
38foreach(variteminAddresses)
39{
40s=s+item.ToString()+'','';
41}
42s=s.TrimEnd('','');
43s=s+":"+Port.ToString(CultureInfo.InvariantCulture);
44
45returns;
46}
47}
献花(0)
+1
(本文系thedust79首藏)