• 异步 TCP 通信 ---- AsyncTcpClient


      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("
    ");
     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 }
  • 相关阅读:
    barnes-hut算法 && Fast Multipole Methods算法
    最大独立集问题-maximal independent set problem
    kernighan lin算法
    浅析Struts2中的OGNL和ValueStack
    Python框架之Django学习笔记(十四)
    C++抓网页/获取网页内容
    SpiderMonkey-让你的C++程序支持JavaScript脚本
    关于职位的解释---转CSDN的文章
    优雅的css写法
    linux
  • 原文地址:https://www.cnblogs.com/jshchg/p/12933450.html
Copyright © 2020-2023  润新知