• TcpClient例子(2)更robust的程序


    目的:在上一个例子的基础上,我们的程序需要更加的robust。需求是一个在客户端的长连接,在连接,发送和接受发生各种非正常情况下,

    程序需要自动重连,自动恢复运行。

    怎样判定socket异常需要重连。

     连接:在回调函数的EndConnect时需要try catch

    发送:在BeginSend直接try catch,也不需要回调了

    接受:在BeginReceive的时候try catch。还有一种情况需要特别注意,没有异常出现,但是服务器close socket了,此时判断EndReceive的返回值,为0说明异常。

     下面的代码在连接,发送,接受都可以检测socket不正常的情况

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    
    namespace meNet
    {
        /// <summary>
        ///假定一切工作正常
        ///连接后发送一次消息,然后不停接受消息并且打印
        /// </summary>
        class Program
        {
            byte[] recvData = new byte[1024 * 10];
            TcpClient client = new TcpClient();
            NetworkStream stream = null;
            bool isConnect = false;
            void doWork()
            {
                string str = "legionis here
    ";
                byte[] toSent = Encoding.ASCII.GetBytes(str);
                Console.WriteLine("preparing to connect in main thread  " + Thread.CurrentThread.ManagedThreadId);
                client.BeginConnect("127.0.0.1", 8888, ConnectCallBack, client);
                while (true)
                {
                    try
                    {
                        if (isConnect)
                            client.Client.BeginSend(toSent, 0, toSent.Length, SocketFlags.None, SendCallBack, null);
                        Console.WriteLine("sent done
    ");
                        Thread.Sleep(2000);
                    }
    
                    catch (Exception ex)
                    {
                        Console.WriteLine("send error
    ");
                        isConnect = false;
                    }
    
                }
            }
            public void SendCallBack(IAsyncResult result)
            {
                try
                {
                    client.Client.EndSend(result);
                    Console.WriteLine("yes sent");
                }
                catch (Exception ex)
                {
    
                }
            }
            static void Main(string[] args)
            {
                Program p = new Program();
                p.doWork();
                Console.Read();
            }
            private void ConnectCallBack(IAsyncResult result)
            {
                Console.WriteLine("well, i am in the connect thread..." + Thread.CurrentThread.ManagedThreadId);
                TcpClient client = result.AsyncState as TcpClient;
                try
                {
                    client.EndConnect(result);
                }
                catch (Exception ex)
                {
                    isConnect = false;
                    Console.WriteLine("well, seems an error occured...");
                    Console.WriteLine("which is " + ex.ToString());
                    return;
                }
                isConnect = true;
                Console.WriteLine("Hooray, it worked.");
                client.Client.BeginReceive(recvData, 0, recvData.Length, SocketFlags.None, RecvCallBack, client);
    
            }
            public void RecvCallBack(IAsyncResult result)
            {
                Console.WriteLine("here in recv callback thread.." + Thread.CurrentThread.ManagedThreadId);
                int count = -1;
                try
                {
                    count = client.Client.EndReceive(result);
                }
                catch (Exception ex)
                {
                    isConnect = false;
                    Console.WriteLine("远程计算机关闭");
                    return;
                }
                if (count <= 0)
                {
                    Console.WriteLine("cannot recv any data");
                    isConnect = false;
                    return;
                }
                //an recv is done,display it and continue..
                string msg = Encoding.ASCII.GetString(recvData, 0, count);
                Console.WriteLine("your recv this time is:");
                Console.WriteLine(msg);
                try {
                    client.Client.BeginReceive(recvData, 0, recvData.Length, SocketFlags.None, RecvCallBack, client);
     
                }
                catch(Exception ex)
                {
                    isConnect = false;
                }
                
            }
        }
    }
    View Code

    下面的代码实现使用多线程,定时检测,发现异常,不停的重新连接

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    
    namespace meNet
    {
        /// <summary>
        ///可以处理各种异常情况下的重连
        ///连接后不停发送消息,然后不停接受消息并且打印
        /// </summary>
        class Program
        {
            byte[] recvData = new byte[1024 * 10];
            TcpClient client = new TcpClient();
            NetworkStream stream = null;
            bool isConnect = false;
            AutoResetEvent do_connect=new AutoResetEvent(false);
            public void reconnectThread(){
                while (true)
                {
                    do_connect.WaitOne();
                    try
                    {
                        if (!isConnect)
                        {
                            client.Close();
                            client = new TcpClient();
                            client.Connect("127.0.0.1", 8888);
                        }
                        
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("well, reconnect failed this time ,try again in five secs");
                        Thread.Sleep(5000);
                        do_connect.Set();
                        continue;
                    }
                    Console.WriteLine("connection re-established...
    ");
                    isConnect = true;
                    try
                    {
                        client.Client.BeginReceive(recvData, 0, recvData.Length, SocketFlags.None, RecvCallBack, client);
    
                    }
                    catch (Exception ex)
                    {
                        rees();
                    }
                }
            }
            void rees()
            {
                Console.WriteLine("initiating an reopening...");
    
                isConnect = false;
                do_connect.Set();
            }
            void doWork()
            {
                Thread t = new Thread(reconnectThread);
                t.Start();
                string str = "legionis here
    ";
                byte[] toSent = Encoding.ASCII.GetBytes(str);
                Console.WriteLine("preparing to connect in main thread  " + Thread.CurrentThread.ManagedThreadId);
                client.BeginConnect("127.0.0.1", 8888, ConnectCallBack, client);
                while (true)
                {
                    try
                    {
                        if (isConnect)
                        {
                            client.Client.BeginSend(toSent, 0, toSent.Length, SocketFlags.None, null, null);
                            Console.WriteLine("sent done
    ");
                        }
                        Thread.Sleep(2000);
                    }
    
                    catch (Exception ex)
                    {
                        Console.WriteLine("send error
    ");
                        rees();
                    }
    
                }
            }
            public void SendCallBack(IAsyncResult result)
            {
                try
                {
                    client.Client.EndSend(result);
                    Console.WriteLine("yes sent");
                }
                catch (Exception ex)
                {
                    rees();
                }
            }
            static void Main(string[] args)
            {
                Program p = new Program();
                p.doWork();
                Console.Read();
            }
            private void ConnectCallBack(IAsyncResult result)
            {
                Console.WriteLine("well, i am in the connect thread..." + Thread.CurrentThread.ManagedThreadId);
                TcpClient client = result.AsyncState as TcpClient;
                try
                {
                    client.EndConnect(result);
                }
                catch (Exception ex)
                {
                    isConnect = false;
                    Console.WriteLine("well, seems an error occured...");
                    Console.WriteLine("which is " + ex.ToString());
                    rees();
                    return;
                }
                isConnect = true;
                Console.WriteLine("Hooray, it worked.");
                client.Client.BeginReceive(recvData, 0, recvData.Length, SocketFlags.None, RecvCallBack, client);
    
            }
            public void RecvCallBack(IAsyncResult result)
            {
                Console.WriteLine("here in recv callback thread.." + Thread.CurrentThread.ManagedThreadId);
                int count = -1;
                try
                {
                    count = client.Client.EndReceive(result);
                }
                catch (Exception ex)
                {
                    isConnect = false;
                    Console.WriteLine("远程计算机关闭");
                    rees();
                    return;
                }
                if (count <= 0)
                {
                    Console.WriteLine("cannot recv any data");
                    rees();
                    return;
                }
                //an recv is done,display it and continue..
                string msg = Encoding.ASCII.GetString(recvData, 0, count);
                Console.WriteLine("your recv this time is:");
                Console.WriteLine(msg);
                try {
                    client.Client.BeginReceive(recvData, 0, recvData.Length, SocketFlags.None, RecvCallBack, client);
     
                }
                catch(Exception ex)
                {
                    rees();
                }
                
            }
        }
    }
    View Code

    技术关键点:

    重连线程框架如下

    while(true)

    {

      do_connect.WaitOne();

      尝试新建TcpClient,并且连接

    }

    其他代码发现socket不正常,就会使能AutoResetEvent,WaitOne不阻塞了,开始运行。

    更加robust的封装之使用log4net

     考虑到程序以后使用winform,为了更好的调试,这里介绍log4net的使用

    1.下载

    到https://logging.apache.org/log4net/download_log4net.cgi

    或者直接在此下载dll文件,项目中添加引用log4net.dll

    2.在项目的Properties->AssemblyInfo.cs文件中,添加

    [assembly: log4net.Config.XmlConfigurator(ConfigFileExtension="config",Watch=true)]

    3.右键项目,添加新建项->应用程序配置文件App.config(当然,如果已经有,无需添加)

    4.App.config 文件添加如下代码,这里我们的目的是将日志输出磁盘中的一个txt文件中。

    <?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="C:\TestWeb\Debug\Error\" />
          <param name="AppendToFile" value="true" />
          <param name="RollingStyle" value="Date" />
          <param name="DatePattern" value="&quot;Logs_&quot;yyyyMMdd&quot;.txt&quot;" />
          <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--------------------------&#xD;&#xA;" />
            <param name="Footer" value=" ----------------------footer--------------------------&#xD;&#xA;" />
          </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>
    View Code

    注意这一行

    <param name="File" value="C:\TestWeb\Debug\Error\" />,日志文件会存在对应盘符目录下,

    我们改成<param name="File" value="log\" />让日志存储在当前exe目录的log子目录下

    5.代码中使用非常简单

    using log4net;

    程序开始创建一个logger

    ILog logInfo = LogManager.GetLogger("loginfo");

    然后在你需要的地方使用

    loginfo.Info("blahblah");

     更多关于配置文件的说明:

    https://blog.csdn.net/zhoufoxcn/article/details/2220533

    https://www.cnblogs.com/xuxuzhaozhao/p/6640623.html

  • 相关阅读:
    NET基础课--NET的一些概念0
    项目异常及处理方法记录
    个人总结
    NuGet学习笔记(4)—— 实践
    NuGet学习笔记(3)——搭建属于自己的NuGet服务器(转)
    NuGet学习笔记(2)——使用图形化界面打包自己的类库(转)
    NuGet学习笔记(1)——初识NuGet及快速安装使用(转)
    关系数据库SQL之基本数据查询:子查询、分组查询、模糊查询
    <iOS>UIImage变为NSData并进行压缩
    iOS 将NSArray、NSDictionary转换为JSON格式进行网络传输
  • 原文地址:https://www.cnblogs.com/legion/p/9115457.html
Copyright © 2020-2023  润新知