TCPClient例子(3)基于委托和事件的TcpHelper程序
如何使用
TcpHelper t;作为Form的一个成员变量。
在Form初始化的时候,初始化TcpHelper
t = new TcpHelper(); t.SetAddr("127.0.0.1", 8888); t.connect();
同时以如下形式添加事件响应方法
t.ChannelActive += new Action<object, object>( (sender, arg) => { if (!this.IsHandleCreated||IsDisposed) return; this.Invoke(new MethodInvoker( () => { this.Text = "logged"; } )); } ); t.ReadComplete += new Action<object, object>( (sender, arg) => { if (!this.IsHandleCreated || IsDisposed) return; string info = arg as string; this.Invoke(new MethodInvoker( () => { //MessageBox.Show(info); //this.Text = info ; } )); } ); t.Read += new Action<object, object>( (sender, arg) => { if (!this.IsHandleCreated || IsDisposed) return; string info = arg as string; this.Invoke(new MethodInvoker( () => { // } )); } ); t.ExceptionOccured += new Action<object, object>( (sender, arg) => { string info = arg as string; if (!this.IsHandleCreated || IsDisposed) return; this.Invoke(new MethodInvoker( () => { this.Text = "连接异常"; } )); } );
在form关闭的时候
if (t != null)
{
t.close();
}
2.TcpHelper的源代码
using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using LEGION.Log; using System.Threading; namespace LEGION.Sockets { class TcpHelper { public TcpHelper() { log = AutoRefreshHtmlLog.getInstance(); recvData = new byte[bufSize]; reconect_worker = new Thread(reconnectThread); reconect_worker.IsBackground = true; reconect_worker.Start(); q = new DataQ(); } public TcpClient client { get; set; } AutoRefreshHtmlLog log; public bool isConnected = false; public event Action<object, object> ChannelActive; public event Action<object, object> ReadComplete; public event Action<object, object> Read; public event Action<object, object> ExceptionOccured; private string destination=null; private int port; AutoResetEvent do_connect = new AutoResetEvent(false); private Thread reconect_worker; private byte[] recvData; private const int bufSize = 2 * 1024; private DataQ q; private bool all_gone; public void SetAddr(string _destination,int _port){ this.destination=_destination; this.port=_port; } string GetRemoteIP(TcpClient cln) { string ip = cln.Client.RemoteEndPoint.ToString().Split(':')[0]; return ip; } public int GetRemotePort(TcpClient cln) { string temp = cln.Client.RemoteEndPoint.ToString().Split(':')[1]; int port = Convert.ToInt32(temp); return port; } public void reconnectThread() { while (true) { do_connect.WaitOne(); try { if (!isConnected) { client.Close(); client = new TcpClient(); client.Connect(destination,port); } } catch (Exception ex) { log.Info("well, reconnect failed this time ,try again in five secs"); Thread.Sleep(5000); do_connect.Set(); continue; } log.Info("connection re-established...\n"); isConnected = true; if (ChannelActive != null) { ChannelActive(this, "logged"); } try { client.Client.BeginReceive(recvData, 0, recvData.Length, SocketFlags.None, RecvCallBack, client); } catch (Exception ex) { log.Info(ex.ToString()); Thread.Sleep(5000); re_establish(); } } } private void handleIncoming(int count) { q.CopyFromSrc(recvData, count); int tempFront = q.front; int movingFront = q.front; while (!(movingFront%q.handlerSize==q.rear)) { if (q.handlerData[movingFront%(q.handlerSize)] == (byte)'\n'|| q.handlerData[movingFront%(q.handlerSize)]==(byte)'\r') { byte[] nba = NewByteArray(q, tempFront, movingFront); string pass = Encoding.UTF8.GetString(nba); if (Read != null && pass.Length>0) { Read(this, pass); } tempFront = movingFront+1; q.front = tempFront; } movingFront++; } } byte[] NewByteArray(DataQ q, int init, int end) { //remember ,init and end may exceed the size of DataQ buf int size = end - init; if (size < 0) size = 0; byte[] nba = new byte[size]; for (int i = 0; i < end-init; i++) { nba[i] = q.handlerData[(i + init) % q.handlerSize]; } return nba; } public static string StringToHexString(string s, Encoding encode) { byte[] b = encode.GetBytes(s); string result = string.Empty; for (int i = 0; i < b.Length; i++) { result += " " + b[i].ToString("X2"); } return result; } public void RecvCallBack(IAsyncResult result) { int count = -1; try { count = client.Client.EndReceive(result); } catch (Exception ex) { log.Info(ex.ToString()); if (ExceptionOccured != null) { ExceptionOccured(this, ex.ToString()); } re_establish(); return; } if (count <= 0) { log.Info("receive 0 bytes,it seems the remote peer actively closes the connection."); if (ExceptionOccured != null) { ExceptionOccured(this, "closes"); } re_establish(); return; } string msg = Encoding.UTF8.GetString(recvData, 0, count); handleIncoming(count); //string hs = StringToHexString(msg, Encoding.UTF8); //log.Info(msg); //log.Info(hs); if (ReadComplete != null) { ReadComplete(this, msg); } try {
if(all_gone)
return;
client.Client.BeginReceive(recvData, 0, recvData.Length, SocketFlags.None, RecvCallBack, client); } catch (Exception ex) { log.Info(ex.ToString()); if (ExceptionOccured != null) { ExceptionOccured(this, ex.ToString()); } re_establish(); } } private void re_establish() { log.Info("Initiating an re-opening..."); isConnected = false; do_connect.Set(); } public void connect() { client = new TcpClient(); log.Info("System Startup..."); try {client.BeginConnect(destination, port, ConnectCallBack, null); } catch (Exception ex) { log.Info(ex.ToString()); if (ExceptionOccured != null) { ExceptionOccured(this, ex.ToString()); } } } private void ConnectCallBack(IAsyncResult result) { try { client.EndConnect(result); } catch (Exception ex) { log.Info(ex.ToString()); if(ExceptionOccured !=null) ExceptionOccured(this, ex.ToString()); return; } isConnected = true; log.Info("Connected to "+GetRemoteIP(client)+" "+GetRemotePort(client)); if (ChannelActive != null) { ChannelActive(this, "logged"); } try { client.Client.BeginReceive(recvData, 0, recvData.Length, SocketFlags.None, RecvCallBack, client); } catch (Exception ex) { log.Info(ex.ToString()); if (ExceptionOccured != null) { ExceptionOccured(this, "closes"); } re_establish(); } } public void close() { try {
all_gone=true; client.Client.Close(); } catch (Exception ex) { log.Info(ex.ToString()); } } public void send(byte[] data, int offset, int count) { try { if (isConnected) { client.Client.BeginSend(data, offset, count, SocketFlags.None, SendCallBack, null); } } catch (Exception ex) { log.Info(ex.ToString()); if (ExceptionOccured != null) ExceptionOccured(this, "senderror"); re_establish(); } } public void SendCallBack(IAsyncResult result) { try { client.Client.EndSend(result); } catch (Exception ex) { log.Info(ex.ToString()); if (ExceptionOccured != null) ExceptionOccured(this, "senderror"); re_establish(); } } } public class DataQ { public byte[] handlerData; public int handlerSize = 10*1024; public int front, rear; public DataQ() { handlerData = new byte[handlerSize]; front = 0; rear = 0; } public bool QEmpty(){ return rear==front; } public bool QFull() { return (rear + 1) % handlerSize == front; } public int QCap()//how many bytes left to fill in { if ((rear - front) < 0) return front-rear; else return handlerSize-rear+front; } public bool CopyFromSrc(byte[] src, int count) { if (count > QCap()) { front = 0; rear = 0; return false; } else { for (int i = 0; i < count; i++) { handlerData[(rear + i) % handlerSize] = src[i]; } rear=(rear+count)%handlerSize; return true; } } } }
AutoRefreshHtmlLog的代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using log4net; /* * Singleton design pattern for the logger class */ namespace LEGION.Log { public class AutoRefreshHtmlLog { private ILog logger; private bool lever =true; private static AutoRefreshHtmlLog singleton=null; public static AutoRefreshHtmlLog getInstance(){ if(singleton==null){ singleton=new AutoRefreshHtmlLog(); return singleton; } return singleton; } public AutoRefreshHtmlLog() { logger = LogManager.GetLogger("mylogger"); log("<meta http-equiv=\"refresh\" content=\"3\">"); } public void log(string str) { if (logger.IsInfoEnabled) { logger.Info(str); } } public void Info(string str) { if (!lever) return; if (logger.IsInfoEnabled) { logger.Info( str + "</br>"); } } } } /* * Additional Notes: To facilitate the use of the helper class, * The App.config file and code for AssemblyInfo.cs is also provided */ /**************************App.config**********************/ /* <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> </configSections> <log4net> <root> <level value="ALL" /> <appender-ref ref="SysAppender" /> </root> <logger name="WebLogger"> <level value="DEBUG" /> </logger> <appender name="SysAppender" type="log4net.Appender.RollingFileAppender,log4net"> <!--<param name="File" value="App_Data/" />--> <param name="File" value="log\\" /> <param name="AppendToFile" value="true" /> <param name="RollingStyle" value="Date" /> <param name="DatePattern" value=""Logs_"yyyyMMdd".html"" /> <param name="StaticLogFileName" value="false" /> <layout type="log4net.Layout.PatternLayout,log4net"> <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" /> <param name="Header" value=" ----------------------header--------------------------
" /> <param name="Footer" value=" ----------------------footer--------------------------
" /> </layout> </appender> <appender name="consoleApp" type="log4net.Appender.ConsoleAppender,log4net"> <layout type="log4net.Layout.PatternLayout,log4net"> <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" /> </layout> </appender> </log4net> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration> */ /******************************AssemblyInfo.cs**************************/ /* [assembly: log4net.Config.XmlConfigurator(ConfigFileExtension="config",Watch=true)] */