在C#中对TCP客户端的状态封装详解

TCP客户端连接TCP服务器端有几种应用状态:
1.与服务器的连接已建立
2.与服务器的连接已断开
3.与服务器的连接发生异常

应用程序可按需求合理处理这些逻辑,比如:
1.连接断开后自动重连
2.连接断开后选择备用地址重连
3.所有状态变化上报告警
本文描述的TcpClient实现了状态变化的事件通知机制。

代码如下:

/// <summary>
   /// 异步TCP客户端
   /// </summary>
   public class AsyncTcpClient : IDisposable
   {
     #region Fields

private TcpClient tcpClient;
     private bool disposed = false;
     private int retries = 0;

#endregion

#region Ctors

/// <summary>
     /// 异步TCP客户端
     /// </summary>
     /// <param name="remoteEP">远端服务器终结点</param>
     public AsyncTcpClient(IPEndPoint remoteEP)
       : this(new[] { remoteEP.Address }, remoteEP.Port)
     {
     }

/// <summary>
     /// 异步TCP客户端
     /// </summary>
     /// <param name="remoteEP">远端服务器终结点</param>
     /// <param name="localEP">本地客户端终结点</param>
     public AsyncTcpClient(IPEndPoint remoteEP, IPEndPoint localEP)
       : this(new[] { remoteEP.Address }, remoteEP.Port, localEP)
     {
     }

/// <summary>
     /// 异步TCP客户端
     /// </summary>
     /// <param name="remoteIPAddress">远端服务器IP地址</param>
     /// <param name="remotePort">远端服务器端口</param>
     public AsyncTcpClient(IPAddress remoteIPAddress, int remotePort)
       : this(new[] { remoteIPAddress }, remotePort)
     {
     }

/// <summary>
     /// 异步TCP客户端
     /// </summary>
     /// <param name="remoteIPAddress">远端服务器IP地址</param>
     /// <param name="remotePort">远端服务器端口</param>
     /// <param name="localEP">本地客户端终结点</param>
     public AsyncTcpClient(
       IPAddress remoteIPAddress, int remotePort, IPEndPoint localEP)
       : this(new[] { remoteIPAddress }, remotePort, localEP)
     {
     }

/// <summary>
     /// 异步TCP客户端
     /// </summary>
     /// <param name="remoteHostName">远端服务器主机名</param>
     /// <param name="remotePort">远端服务器端口</param>
     public AsyncTcpClient(string remoteHostName, int remotePort)
       : this(Dns.GetHostAddresses(remoteHostName), remotePort)
     {
     }

/// <summary>
     /// 异步TCP客户端
     /// </summary>
     /// <param name="remoteHostName">远端服务器主机名</param>
     /// <param name="remotePort">远端服务器端口</param>
     /// <param name="localEP">本地客户端终结点</param>
     public AsyncTcpClient(
       string remoteHostName, int remotePort, IPEndPoint localEP)
       : this(Dns.GetHostAddresses(remoteHostName), remotePort, localEP)
     {
     }

/// <summary>
     /// 异步TCP客户端
     /// </summary>
     /// <param name="remoteIPAddresses">远端服务器IP地址列表</param>
     /// <param name="remotePort">远端服务器端口</param>
     public AsyncTcpClient(IPAddress[] remoteIPAddresses, int remotePort)
       : this(remoteIPAddresses, remotePort, null)
     {
     }

/// <summary>
     /// 异步TCP客户端
     /// </summary>
     /// <param name="remoteIPAddresses">远端服务器IP地址列表</param>
     /// <param name="remotePort">远端服务器端口</param>
     /// <param name="localEP">本地客户端终结点</param>
     public AsyncTcpClient(
       IPAddress[] remoteIPAddresses, int remotePort, IPEndPoint localEP)
     {
       this.Addresses = remoteIPAddresses;
       this.Port = remotePort;
       this.LocalIPEndPoint = localEP;
       this.Encoding = Encoding.Default;

if (this.LocalIPEndPoint != null)
       {
         this.tcpClient = new TcpClient(this.LocalIPEndPoint);
       }
       else
       {
         this.tcpClient = new TcpClient();
       }

Retries = 3;
       RetryInterval = 5;
     }

#endregion

#region Properties

/// <summary>
     /// 是否已与服务器建立连接
     /// </summary>
     public bool Connected { get { return tcpClient.Client.Connected; } }
     /// <summary>
     /// 远端服务器的IP地址列表
     /// </summary>
     public IPAddress[] Addresses { get; private set; }
     /// <summary>
     /// 远端服务器的端口
     /// </summary>
     public int Port { get; private set; }
     /// <summary>
     /// 连接重试次数
     /// </summary>
     public int Retries { get; set; }
     /// <summary>
     /// 连接重试间隔
     /// </summary>
     public int RetryInterval { get; set; }
     /// <summary>
     /// 远端服务器终结点
     /// </summary>
     public IPEndPoint RemoteIPEndPoint
     {
       get { return new IPEndPoint(Addresses[0], Port); }
     }
     /// <summary>
     /// 本地客户端终结点
     /// </summary>
     protected IPEndPoint LocalIPEndPoint { get; private set; }
     /// <summary>
     /// 通信所使用的编码
     /// </summary>
     public Encoding Encoding { get; set; }

#endregion

#region Connect

/// <summary>
     /// 连接到服务器
     /// </summary>
     /// <returns>异步TCP客户端</returns>
     public AsyncTcpClient Connect()
     {
       if (!Connected)
       {
         // start the async connect operation
         tcpClient.BeginConnect(
           Addresses, Port, HandleTcpServerConnected, tcpClient);
       }

return this;
     }

/// <summary>
     /// 关闭与服务器的连接
     /// </summary>
     /// <returns>异步TCP客户端</returns>
     public AsyncTcpClient Close()
     {
       if (Connected)
       {
         retries = 0;
         tcpClient.Close();
         RaiseServerDisconnected(Addresses, Port);
       }

return this;
     }

#endregion

#region Receive

private void HandleTcpServerConnected(IAsyncResult ar)
     {
       try
       {
         tcpClient.EndConnect(ar);
         RaiseServerConnected(Addresses, Port);
         retries = 0;
       }
       catch (Exception ex)
       {
         ExceptionHandler.Handle(ex);
         if (retries > 0)
         {
           Logger.Debug(string.Format(CultureInfo.InvariantCulture,
             "Connect to server with retry {0} failed.", retries));
         }

retries++;
         if (retries > Retries)
         {
           // we have failed to connect to all the IP Addresses,
           // connection has failed overall.
           RaiseServerExceptionOccurred(Addresses, Port, ex);
           return;
         }
         else
         {
           Logger.Debug(string.Format(CultureInfo.InvariantCulture,
             "Waiting {0} seconds before retrying to connect to server.",
             RetryInterval));
           Thread.Sleep(TimeSpan.FromSeconds(RetryInterval));
           Connect();
           return;
         }
       }

// we are connected successfully and start asyn read operation.
       byte[] buffer = new byte[tcpClient.ReceiveBufferSize];
       tcpClient.GetStream().BeginRead(
         buffer, 0, buffer.Length, HandleDatagramReceived, buffer);
     }

private void HandleDatagramReceived(IAsyncResult ar)
     {
       NetworkStream stream = tcpClient.GetStream();

int numberOfReadBytes = 0;
       try
       {
         numberOfReadBytes = stream.EndRead(ar);
       }
       catch
       {
         numberOfReadBytes = 0;
       }

if (numberOfReadBytes == 0)
       {
         // connection has been closed
         Close();
         return;
       }

// received byte and trigger event notification
       byte[] buffer = (byte[])ar.AsyncState;
       byte[] receivedBytes = new byte[numberOfReadBytes];
       Buffer.BlockCopy(buffer, 0, receivedBytes, 0, numberOfReadBytes);
       RaiseDatagramReceived(tcpClient, receivedBytes);
       RaisePlaintextReceived(tcpClient, receivedBytes);

// then start reading from the network again
       stream.BeginRead(
         buffer, 0, buffer.Length, HandleDatagramReceived, buffer);
     }

#endregion

#region Events

/// <summary>
     /// 接收到数据报文事件
     /// </summary>
     public event EventHandler<TcpDatagramReceivedEventArgs<byte[]>> DatagramReceived;
     /// <summary>
     /// 接收到数据报文明文事件
     /// </summary>
     public event EventHandler<TcpDatagramReceivedEventArgs<string>> PlaintextReceived;

private void RaiseDatagramReceived(TcpClient sender, byte[] datagram)
     {
       if (DatagramReceived != null)
       {
         DatagramReceived(this,
           new TcpDatagramReceivedEventArgs<byte[]>(sender, datagram));
       }
     }

private void RaisePlaintextReceived(TcpClient sender, byte[] datagram)
     {
       if (PlaintextReceived != null)
       {
         PlaintextReceived(this,
           new TcpDatagramReceivedEventArgs<string>(
             sender, this.Encoding.GetString(datagram, 0, datagram.Length)));
       }
     }

/// <summary>
     /// 与服务器的连接已建立事件
     /// </summary>
     public event EventHandler<TcpServerConnectedEventArgs> ServerConnected;
     /// <summary>
     /// 与服务器的连接已断开事件
     /// </summary>
     public event EventHandler<TcpServerDisconnectedEventArgs> ServerDisconnected;
     /// <summary>
     /// 与服务器的连接发生异常事件
     /// </summary>
     public event EventHandler<TcpServerExceptionOccurredEventArgs> ServerExceptionOccurred;

private void RaiseServerConnected(IPAddress[] ipAddresses, int port)
     {
       if (ServerConnected != null)
       {
         ServerConnected(this,
           new TcpServerConnectedEventArgs(ipAddresses, port));
       }
     }

private void RaiseServerDisconnected(IPAddress[] ipAddresses, int port)
     {
       if (ServerDisconnected != null)
       {
         ServerDisconnected(this,
           new TcpServerDisconnectedEventArgs(ipAddresses, port));
       }
     }

private void RaiseServerExceptionOccurred(
       IPAddress[] ipAddresses, int port, Exception innerException)
     {
       if (ServerExceptionOccurred != null)
       {
         ServerExceptionOccurred(this,
           new TcpServerExceptionOccurredEventArgs(
             ipAddresses, port, innerException));
       }
     }

#endregion

#region Send

/// <summary>
     /// 发送报文
     /// </summary>
     /// <param name="datagram">报文</param>
     public void Send(byte[] datagram)
     {
       if (datagram == null)
         throw new ArgumentNullException("datagram");

if (!Connected)
       {
         RaiseServerDisconnected(Addresses, Port);
         throw new InvalidProgramException(
           "This client has not connected to server.");
       }

tcpClient.GetStream().BeginWrite(
         datagram, 0, datagram.Length, HandleDatagramWritten, tcpClient);
     }

private void HandleDatagramWritten(IAsyncResult ar)
     {
       ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar);
     }

/// <summary>
     /// 发送报文
     /// </summary>
     /// <param name="datagram">报文</param>
     public void Send(string datagram)
     {
       Send(this.Encoding.GetBytes(datagram));
     }

#endregion

#region IDisposable Members

/// <summary>
     /// Performs application-defined tasks associated with freeing,
     /// releasing, or resetting unmanaged resources.
     /// </summary>
     public void Dispose()
     {
       Dispose(true);
       GC.SuppressFinalize(this);
     }

/// <summary>
     /// Releases unmanaged and - optionally - managed resources
     /// </summary>
     /// <param name="disposing"><c>true</c> to release both managed
     /// and unmanaged resources; <c>false</c>
     /// to release only unmanaged resources.
     /// </param>
     protected virtual void Dispose(bool disposing)
     {
       if (!this.disposed)
       {
         if (disposing)
         {
           try
           {
             Close();

if (tcpClient != null)
             {
               tcpClient = null;
             }
           }
           catch (SocketException ex)
           {
             ExceptionHandler.Handle(ex);
           }
         }

disposed = true;
       }
     }

#endregion
   }

使用举例


代码如下:

class Program
   {
     static AsyncTcpClient client;

static void Main(string[] args)
     {
       LogFactory.Assign(new ConsoleLogFactory());

// 测试用,可以不指定由系统选择端口
       IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);
       IPEndPoint localEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9998);
       client = new AsyncTcpClient(remoteEP, localEP);
       client.Encoding = Encoding.UTF8;
       client.ServerExceptionOccurred +=
         new EventHandler<TcpServerExceptionOccurredEventArgs>(client_ServerExceptionOccurred);
       client.ServerConnected +=
         new EventHandler<TcpServerConnectedEventArgs>(client_ServerConnected);
       client.ServerDisconnected +=
         new EventHandler<TcpServerDisconnectedEventArgs>(client_ServerDisconnected);
       client.PlaintextReceived +=
         new EventHandler<TcpDatagramReceivedEventArgs<string>>(client_PlaintextReceived);
       client.Connect();

Console.WriteLine("TCP client has connected to server.");
       Console.WriteLine("Type something to send to server...");
       while (true)
       {
         try
         {
           string text = Console.ReadLine();
           client.Send(text);
         }
         catch (Exception ex)
         {
           Console.WriteLine(ex.Message);
         }
       }
     }

static void client_ServerExceptionOccurred(
       object sender, TcpServerExceptionOccurredEventArgs e)
     {
       Logger.Debug(string.Format(CultureInfo.InvariantCulture,
         "TCP server {0} exception occurred, {1}.",
         e.ToString(), e.Exception.Message));
     }

static void client_ServerConnected(
       object sender, TcpServerConnectedEventArgs e)
     {
       Logger.Debug(string.Format(CultureInfo.InvariantCulture,
         "TCP server {0} has connected.", e.ToString()));
     }

static void client_ServerDisconnected(
       object sender, TcpServerDisconnectedEventArgs e)
     {
       Logger.Debug(string.Format(CultureInfo.InvariantCulture,
         "TCP server {0} has disconnected.", e.ToString()));
     }

static void client_PlaintextReceived(
       object sender, TcpDatagramReceivedEventArgs<string> e)
     {
       Console.Write(string.Format("Server : {0} --> ",
         e.TcpClient.Client.RemoteEndPoint.ToString()));
       Console.WriteLine(string.Format("{0}", e.Datagram));
     }
   }

补充代码


代码如下:

/// <summary>
   /// Internal class to join the TCP client and buffer together
   /// for easy management in the server
   /// </summary>
   internal class TcpClientState
   {
     /// <summary>
     /// Constructor for a new Client
     /// </summary>
     /// <param name="tcpClient">The TCP client</param>
     /// <param name="buffer">The byte array buffer</param>
     public TcpClientState(TcpClient tcpClient, byte[] buffer)
     {
       if (tcpClient == null)
         throw new ArgumentNullException("tcpClient");
       if (buffer == null)
         throw new ArgumentNullException("buffer");

this.TcpClient = tcpClient;
       this.Buffer = buffer;
     }

/// <summary>
     /// Gets the TCP Client
     /// </summary>
     public TcpClient TcpClient { get; private set; }

/// <summary>
     /// Gets the Buffer.
     /// </summary>
     public byte[] Buffer { get; private set; }

/// <summary>
     /// Gets the network stream
     /// </summary>
     public NetworkStream NetworkStream
     {
       get { return TcpClient.GetStream(); }
     }
   }

代码如下:

/// <summary>
   /// 与客户端的连接已建立事件参数
   /// </summary>
   public class TcpClientConnectedEventArgs : EventArgs
   {
     /// <summary>
     /// 与客户端的连接已建立事件参数
     /// </summary>
     /// <param name="tcpClient">客户端</param>
     public TcpClientConnectedEventArgs(TcpClient tcpClient)
     {
       if (tcpClient == null)
         throw new ArgumentNullException("tcpClient");

this.TcpClient = tcpClient;
     }

/// <summary>
     /// 客户端
     /// </summary>
     public TcpClient TcpClient { get; private set; }
   }

代码如下:

/// <summary>
  /// 与客户端的连接已断开事件参数
  /// </summary>
  public class TcpClientDisconnectedEventArgs : EventArgs
  {
    /// <summary>
    /// 与客户端的连接已断开事件参数
    /// </summary>
    /// <param name="tcpClient">客户端</param>
    public TcpClientDisconnectedEventArgs(TcpClient tcpClient)
    {
      if (tcpClient == null)
        throw new ArgumentNullException("tcpClient");

this.TcpClient = tcpClient;
    }

/// <summary>
    /// 客户端
    /// </summary>
    public TcpClient TcpClient { get; private set; }
  }

代码如下:

/// <summary>
   /// 与服务器的连接发生异常事件参数
   /// </summary>
   public class TcpServerExceptionOccurredEventArgs : EventArgs
   {
     /// <summary>
     /// 与服务器的连接发生异常事件参数
     /// </summary>
     /// <param name="ipAddresses">服务器IP地址列表</param>
     /// <param name="port">服务器端口</param>
     /// <param name="innerException">内部异常</param>
     public TcpServerExceptionOccurredEventArgs(
       IPAddress[] ipAddresses, int port, Exception innerException)
     {
       if (ipAddresses == null)
         throw new ArgumentNullException("ipAddresses");

this.Addresses = ipAddresses;
       this.Port = port;
       this.Exception = innerException;
     }

/// <summary>
     /// 服务器IP地址列表
     /// </summary>
     public IPAddress[] Addresses { get; private set; }
     /// <summary>
     /// 服务器端口
     /// </summary>
     public int Port { get; private set; }
     /// <summary>
     /// 内部异常
     /// </summary>
     public Exception Exception { get; private set; }

/// <summary>
     /// Returns a <see cref="System.String"/> that represents this instance.
     /// </summary>
     /// <returns>
     /// A <see cref="System.String"/> that represents this instance.
     /// </returns>
     public override string ToString()
     {
       string s = string.Empty;
       foreach (var item in Addresses)
       {
         s = s + item.ToString() + ',';
       }
       s = s.TrimEnd(',');
       s = s + ":" + Port.ToString(CultureInfo.InvariantCulture);

return s;
     }
   }

(0)

相关推荐

  • C#实现TCP连接信息统计的方法

    本文实例讲述了C#实现TCP连接信息统计的方法.分享给大家供大家参考.具体实现方法如下: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net.Netw

  • C#基于TCP协议的服务器端和客户端通信编程的基础教程

    运行在TCP之上常见的网络应用协议有比如HTTP.FTP.SMTP.POP3.IMAP. TCP是TCP/IP体系中最重要的传输协议,它提供全双工和可靠交付的服务,是大多数应用协议工作的基础. TCP是一种面向连接(连接导向)的,可靠的,基于字节流的传输层通信协议. TCP的工作过程 建立连接 传输数据 连接的终止 TCP的主要特点 1.TCP是面向连接的协议 2.是端到端的通信.每个TCP连接只能有两个端点,而且只能一对一通信,不能点对多的 的直接通信 3.高可靠性 4.全双工方式传输 5.数

  • C# Socket粘包处理讲解示例

    当socket接收到数据后,会根据buffer的大小一点一点的接收数据,比如: 对方发来了1M的数据量过来,但是,本地的buffer只有1024字节,那就代表socket需要重复很多次才能真正收完这逻辑上的一整个消息.对方发来了5条2个字符的消息,本地的buffer(大小1024字节)会将这5条消息全部收入囊下...那么,如何处理呢?下面我以最简单的一种文本消息来demo 根据上面所描述的情况,最重要的关键落在了下面3个因素的处理上 消息的结尾标记接收消息时判断结尾标记当本次buffer中没有结

  • C#中TCP粘包问题的解决方法

    一.TCP粘包产生的原理 1.TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾.出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成. 2.发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据.若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据.接收方引起的粘包是由于接收方用户进程不及时接收数据,从

  • 使用C#实现基于TCP和UDP协议的网络通信程序的基本示例

    C#中使用TCP通信 TCP通信需要通信双方都在线,所以需要先启动服务端进行监听,客户端才能获得连接,服务端代码: static void Main(string[] args) { TcpClient client = null; NetworkStream stream = null; byte[] buffer = null; string receiveString = null; IPAddress localIP = IPAddress.Parse("127.0.0.1")

  • 在C#中对TCP客户端的状态封装详解

    TCP客户端连接TCP服务器端有几种应用状态:1.与服务器的连接已建立2.与服务器的连接已断开3.与服务器的连接发生异常 应用程序可按需求合理处理这些逻辑,比如:1.连接断开后自动重连2.连接断开后选择备用地址重连3.所有状态变化上报告警本文描述的TcpClient实现了状态变化的事件通知机制. 复制代码 代码如下: /// <summary>   /// 异步TCP客户端   /// </summary>   public class AsyncTcpClient : IDisp

  • K8S之StatefulSet有状态服务详解

    目录 一.概念 二.实例 一.概念 1.1.无状态和有状态的区别 主要从网络和存储来对比 无状态不考虑存储和网络,可以任意漂移,每个副本是一样的,如Nginx 有状态应用需要考虑存储和网络,每个副本是不对等的,具有唯一的ID,如etcd.mysql 1.2.StatefulSet的特点 专为部署有状态服务而生 解决Pod独立生命周期,保持Pod启动顺序和唯一性 应用场景:分布式应用.数据库集群 稳定,唯一的网络标识符,持久存储有序,优雅的部署和扩展.删除.终止有序,滚动更新 1.3.Headle

  • python中函数总结之装饰器闭包详解

    1.前言 函数也是一个对象,从而可以增加属性,使用句点来表示属性. 如果内部函数的定义包含了在外部函数中定义的对象的引用(外部对象可以是在外部函数之外),那么内部函数被称之为闭包. 2.装饰器 装饰器就是包装原来的函数,从而在不需要修改原来代码的基础之上,可以做更多的事情. 装饰器语法如下: @deco2 @deco1 def func(arg1,arg2...): pass 这个表示了有两个装饰器的函数,那么表示的含义为:func = deco2(deco1(func)) 无参装饰器语法如下:

  • JAVA 中解密RSA算法JS加密实例详解

    JAVA 中解密RSA算法JS加密实例详解 有这样一个需求,前端登录的用户名密码,密码必需加密,但不可使用MD5,因为后台要检测密码的复杂度,那么在保证安全的前提下将密码传到后台呢,答案就是使用RSA非对称加密算法解决 . java代码 需要依赖 commons-codec 包 RSACoder.Java import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import java.security.

  • Android 监听网络状态方法详解

    Android 监听网络状态方法详解 一.加入网络权限 获取网络信息需要在AndroidManifest.xml文件中加入相应的权限. <uses-permission Android:name="android.permission.ACCESS_NETWORK_STATE" /> 二.判断手机网络的几个方案 1)判断是否有网络连接 public boolean isMobileConnected(Context context) { if (context != nul

  • Android 中CheckBox的isChecked的使用实例详解

    Android 中CheckBox的isChecked的使用实例详解 范例说明 所有的网络服务在User使用之前,都需要签署同意条款,在手机应用程序.手机游戏的设计经验中,常看见CheckBox在同意条款情境的运用,其选取的状态有两种即isChecked=true与isChecked=false. 以下范例将设计一个TextView放入条款文字,在下方配置一个CheckBox Widget作为选取项,通过Button.onClickListener按钮事件处理,取得User同意条款的状态. 当C

  • Hibernate中Session增删改查操作代码详解

    把三状态转换图放在这,方便分析方法的作用: 1.Session的save()方法 Session是Hibernate所有接口中最重要的接口,提供了对数据保存,更新,查询和删除的方法. Session的save()方法可以使临时态或游离态转换为持久态.例如,保存一个Customer对象: SessionFactory sessionFactory; Configuration configuration = new Configuration().configure(); sessionFacto

  • 基于php数组中的索引数组和关联数组详解

    php中的索引数组是指以数字为键的数组.并且这个键值 是自增的 关联数组指的是一个键值对应一个值,并且这个键值是不规律的,通常都是我们自己指定的. 他们两还有不同的地方,索引数组转为json后是数组.而关联数组转为json后是对象.通常我们给app端写接口都是用索引数组转成json传过去.客户端那边对数组更为友好一点. 需要注意点: $arr = [0=>1,2=>3a]; 上述数组$arr转为json会是对象形式的. $arr = ['a','b']; 这里的$arr转为json后是数组的形

  • Java中常见死锁与活锁的实例详解

    本文介绍了Java中常见死锁与活锁的实例详解,分享给大家,具体如下: 顺序死锁:过度加锁,导致由于执行顺序的原因,互相持有对方正在等待的锁 资源死锁:多个线程在相同的资源上发生等待 由于调用顺序而产生的死锁 public class Test { Object leftLock = new Object(); Object rightLock = new Object(); public static void main(String[] args) { final Test test = ne

  • vue中datepicker的使用教程实例代码详解

    写这个文章主要是记录下用法,官网已经说的很详细了 npm install vue-datepicker --save html代码 <myDatepicker :date="startTime" :option="multiOption" :limit="limit"></myDatepicker> <myDatepicker :date="endtime" :option="timeo

随机推荐