1. 程式人生 > >C# TCP非同步通訊框架封裝

C# TCP非同步通訊框架封裝

最近用c#寫了一個TCP非同步通訊框架TCPHelper,用於服務端客戶端通訊,採用非同步和事件驅動的方式,使用者只需要初始化和裝載事件即可使用,框架圖粗略如下所示:
這裡寫圖片描述
使用如下:(本文框架及例項下載地址
服務端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using TCPHelper;

namespace Socket非同步請求Server
{
    class
Program
{ static void Main(string[] args) { ServerAsync server = new ServerAsync(); server.Completed += new Action<string, TCPHelper.EnSocketAction>((key, enAction) => { switch (enAction) { case
EnSocketAction.Connect: Console.WriteLine("接收到來自{0}的連線",key); break; case EnSocketAction.SendMsg: Console.WriteLine("對{0}傳送了一條訊息", key); break; case EnSocketAction.Close: Console.WriteLine("{0}關閉了連線"
, key); break; default: break; } })
; server.Received += new Action<string, string>((key, msg) => { Console.WriteLine("{0}對我說:{1}", key, msg); }); server.StartAsync(10001); Console.Read(); } } }

客戶端:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TCPHelper;
using System.Net;
using System.Net.Sockets;

namespace Socket非同步請求Client
{
    class Program
    {
        static void Main(string[] args)
        {
            ClientAsync client = new ClientAsync();
            client.Completed += new Action<System.Net.Sockets.TcpClient, EnSocketAction>((c, enAction) =>
            {
                IPEndPoint iep = c.Client.RemoteEndPoint as IPEndPoint;
                string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
                switch (enAction)
                {
                    case EnSocketAction.Connect:
                        Console.WriteLine("已經與{0}建立連線",key);
                        break;
                    case EnSocketAction.SendMsg:
                        Console.WriteLine("{0}:向{1}傳送了一條訊息",DateTime.Now,key);
                        break;
                    case EnSocketAction.Close:
                        Console.WriteLine("服務端連線關閉");
                        break;
                    default:
                        break;
                }
            });
            client.Received += new Action<string,string>((key,msg)=>
            {
                Console.WriteLine("{0}對我說:{1}",key,msg);
            });
            client.ConnectAsync(10001);
            while (true)
            {
                string msg = Console.ReadLine();
                client.SendAsync(msg);
            }
        }
    }
}

上面所引用的TCPHelper為所用的框架,下載地址:點選這裡下載
框架的原始碼也貼出來:
ClientAsync.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace TCPHelper
{
   public class ClientAsync
    {
       private TcpClient client;
       /// <summary>
       /// 客戶端連線完成、傳送完成、連線異常或者服務端關閉觸發的事件
       /// </summary>
       public event Action<TcpClient,EnSocketAction> Completed;
       /// <summary>
       /// 客戶端接收訊息觸發的事件
       /// </summary>
       public event Action<string,string> Received;
       /// <summary>
       /// 用於控制非同步接收訊息
       /// </summary>
       private ManualResetEvent doReceive = new ManualResetEvent(false);
       //標識客戶端是否關閉
       private bool isClose = false;
       public ClientAsync()
       {
           client = new TcpClient();
       }
       /// <summary>
       /// 非同步連線
       /// </summary>
       /// <param name="ip">要連線的伺服器的ip地址</param>
       /// <param name="port">要連線的伺服器的埠</param>
       public void ConnectAsync(string ip, int port)
       {
           IPAddress ipAddress = null;
           try
           {
               ipAddress = IPAddress.Parse(ip);
           }
           catch (Exception)
           {
               throw new Exception("ip地址格式不正確,請使用正確的ip地址!");
           }
           client.BeginConnect(ipAddress, port,ConnectCallBack, client);
       }
       /// <summary>
       /// 非同步連線,連線ip地址為127.0.0.1
       /// </summary>
       /// <param name="port">要連線服務端的埠</param>
       public void ConnectAsync(int port)
       {
           ConnectAsync("127.0.0.1", port);
       }
       /// <summary>
       /// 非同步接收訊息
       /// </summary>
       private void ReceiveAsync()
       {
           doReceive.Reset();
           StateObject obj=new StateObject();
           obj.Client=client;

           client.Client.BeginReceive(obj.ListData, 0, obj.ListData.Length, SocketFlags.None, ReceiveCallBack, obj);
           doReceive.WaitOne();
       }
       /// <summary>
       /// 非同步傳送訊息
       /// </summary>
       /// <param name="msg"></param>
        public void SendAsync(string msg)
        {
            byte[] listData=Encoding.UTF8.GetBytes(msg);
            client.Client.BeginSend(listData, 0, listData.Length, SocketFlags.None, SendCallBack,client);
        }
       /// <summary>
       /// 非同步連線的回撥函式
       /// </summary>
       /// <param name="ar"></param>
       private void ConnectCallBack(IAsyncResult ar)
       {
           TcpClient client = ar.AsyncState as TcpClient;
           client.EndConnect(ar);
           OnComplete(client, EnSocketAction.Connect);
       }
       /// <summary>
       /// 非同步接收訊息的回撥函式
       /// </summary>
       /// <param name="ar"></param>
       private void ReceiveCallBack(IAsyncResult ar)
       {
           StateObject obj = ar.AsyncState as StateObject;
           int count=-1;
           try
           {
               count = obj.Client.Client.EndReceive(ar);
               doReceive.Set();
           }
           catch (Exception)
           {
               //如果發生異常,說明客戶端失去連線,觸發關閉事件
               Close();
               OnComplete(obj.Client, EnSocketAction.Close);
           }
           if (count > 0)
           {
               string msg = Encoding.UTF8.GetString(obj.ListData, 0, count);
               if (!string.IsNullOrEmpty(msg))
               {
                   if (Received != null)
                   {
                       IPEndPoint iep = obj.Client.Client.RemoteEndPoint as IPEndPoint;
                       string key = string.Format("{0}:{1}", iep.Address, iep.Port);
                       Received(key,msg);
                   }
               }
           }
       }
       private void SendCallBack(IAsyncResult ar)
       {
           TcpClient client = ar.AsyncState as TcpClient;
           try
           {
               client.Client.EndSend(ar);
               OnComplete(client, EnSocketAction.SendMsg);
           }
           catch (Exception)
           {
               //如果發生異常,說明客戶端失去連線,觸發關閉事件
               Close();
               OnComplete(client, EnSocketAction.Close);
           }
       }
       public virtual void OnComplete(TcpClient client, EnSocketAction enAction)
       {
           if (Completed != null)
               Completed(client, enAction);
           if (enAction == EnSocketAction.Connect)//建立連線後,開始接收資料
           {
               ThreadPool.QueueUserWorkItem(x =>
                   {
                       while (!isClose)
                       {
                           try
                           {
                               Thread.Sleep(20);
                               ReceiveAsync();
                               Thread.Sleep(20);
                           }
                           catch (Exception)
                           {
                               Close();
                               OnComplete(client, EnSocketAction.Close);
                           }
                       }
                   });
           }
       }

       public void Close()
       {
           isClose = true;
       }
    }
}

ServerAsync.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace TCPHelper
{
    public class ServerAsync
    {
        private TcpListener listener = null;

        //用於控制非同步接受連線
        private ManualResetEvent doConnect = new ManualResetEvent(false);
        //用於控制非同步接收資料
        private ManualResetEvent doReceive=new ManualResetEvent(false);
        //標識服務端連線是否關閉
        private bool isClose = false;
        private Dictionary<string,TcpClient> listClient=new Dictionary<string,TcpClient>();
        /// <summary>
        /// 已建立連線的集合
        /// key:ip:port
        /// value:TcpClient
        /// </summary>
        public Dictionary<string,TcpClient> ListClient
        {
            get{return listClient;}
            private set{listClient=value;}
        }
        /// <summary>
        /// 連線、傳送、關閉事件
        /// </summary>
        public event Action<string,EnSocketAction> Completed;
        /// <summary>
        /// 接收到資料事件
        /// </summary>
        public event Action<string,string> Received;
        public ServerAsync()
        {

        }
        /// <summary>
        /// 開始非同步監聽ip地址的埠
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public void StartAsync(string ip, int port)
        {           

            IPAddress ipAddress=null;
            try
            {
                ipAddress=IPAddress.Parse(ip);
            }
            catch(Exception e)
            {
               throw e;
            }
            listener = new TcpListener(new IPEndPoint(ipAddress, port));
            listener.Start();
            ThreadPool.QueueUserWorkItem(x =>
            {
                while (!isClose)
                {
                    doConnect.Reset();
                    listener.BeginAcceptTcpClient(AcceptCallBack, listener);
                    doConnect.WaitOne();
                }
            });


        }
        /// <summary>
        /// 開始非同步監聽本機127.0.0.1的埠號
        /// </summary>
        /// <param name="port"></param>
        public void StartAsync(int port)
        {
            StartAsync("127.0.0.1", port);
        }
        /// <summary>
        /// 開始非同步傳送資料
        /// </summary>
        /// <param name="key">客戶端的ip地址和埠號</param>
        /// <param name="msg">要傳送的內容</param>
        public void SendAsync(string key, string msg)
        {
            if (!ListClient.ContainsKey(key))
            {
                throw new Exception("所用的socket不在字典中,請先連線!");
            }
            TcpClient client = ListClient[key];
            byte[] listData=Encoding.UTF8.GetBytes(msg);
            client.Client.BeginSend(listData, 0, listData.Length, SocketFlags.None, SendCallBack, client);
        }
        /// <summary>
        /// 開始非同步接收資料
        /// </summary>
        /// <param name="key">要接收的客戶端的ip地址和埠號</param>
        private void ReceiveAsync(string key)
        {
            doReceive.Reset();
            if (ListClient.ContainsKey(key))
            {
                TcpClient client = ListClient[key];
                //if (!client.Connected)
                //{
                //    ListClient.Remove(key);
                //    OnComplete(key, EnSocketAction.Close);
                //    return;
                //}
                StateObject obj = new StateObject();
                obj.Client = client;
                try
                {
                    client.Client.BeginReceive(obj.ListData, 0, obj.ListData.Length, SocketFlags.None, ReceiveCallBack, obj);
                }
                catch (Exception)
                {

                }
                doReceive.WaitOne();
            }
        }
        /// <summary>
        /// 非同步接收連線的回撥函式
        /// </summary>
        /// <param name="ar"></param>
        private void AcceptCallBack(IAsyncResult ar)
        {

           TcpListener l= ar.AsyncState as TcpListener;
           TcpClient client = l.EndAcceptTcpClient(ar);
           doConnect.Set();

           IPEndPoint iep = client.Client.RemoteEndPoint as IPEndPoint;
           string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
           if (!ListClient.ContainsKey(key))
           {
               ListClient.Add(key, client);
               OnComplete(key, EnSocketAction.Connect);
           }

        }
        /// <summary>
        /// 非同步傳送資料的回撥函式
        /// </summary>
        /// <param name="ar"></param>
        private void SendCallBack(IAsyncResult ar)
        {
           TcpClient client= ar.AsyncState as TcpClient;
           IPEndPoint iep = client.Client.RemoteEndPoint as IPEndPoint;
           string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
           if (Completed != null)
           {
               Completed(key, EnSocketAction.SendMsg);
           }

        }
        /// <summary>
        /// 非同步接收資料的回撥函式
        /// </summary>
        /// <param name="ar"></param>
        private void ReceiveCallBack(IAsyncResult ar)
        {
            StateObject obj =ar.AsyncState as StateObject;

            int count=-1;
            try
            {
                count = obj.Client.Client.EndReceive(ar);
            }
            catch (Exception e)
            {
                if (!obj.Client.Client.Connected)
                {
                    IPEndPoint iep = obj.Client.Client.RemoteEndPoint as IPEndPoint;
                    string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);

                    ListClient.Remove(key);
                    OnComplete(key, EnSocketAction.Close);
                    doReceive.Set();
                    return;
                }
            }
            doReceive.Set();
            if (count > 0)
            {
                string msg = Encoding.UTF8.GetString(obj.ListData, 0, count);
                if (!string.IsNullOrEmpty(msg))
                {
                    if (Received != null)
                    {
                        IPEndPoint iep = obj.Client.Client.RemoteEndPoint as IPEndPoint;
                        string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
                        Received(key,msg);//觸發接收事件
                    }
                }
            }
        }
        public virtual void OnComplete(string key, EnSocketAction enAction)
        {
            if (Completed != null)
                Completed(key, enAction);
            if (enAction == EnSocketAction.Connect)//當連線建立時,則要一直接收
            {
                ThreadPool.QueueUserWorkItem(x =>
                {
                    while (ListClient.ContainsKey(key)&&!isClose)
                    {
                        Thread.Sleep(20);
                        ReceiveAsync(key);
                        Thread.Sleep(20);
                    }
                });

            }
        }
        public void Close()
        {
            isClose=true;
        }
    }

}

其他:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;

namespace TCPHelper
{
    /// <summary>
    /// 接收socket的行為
    /// </summary>
    public enum EnSocketAction
    {
        /// <summary>
        /// socket發生連線
        /// </summary>
        Connect = 1,
        /// <summary>
        /// socket傳送資料
        /// </summary>
        SendMsg = 2,
        /// <summary>
        /// socket關閉
        /// </summary>
        Close = 4
    }
    /// <summary>
    /// 對非同步接收時的物件狀態的封裝,將socket與接收到的資料封裝在一起
    /// </summary>
    public class StateObject
    {
        public TcpClient Client { get; set; }
        private byte[] listData = new byte[2048];
        /// <summary>
        /// 接收的資料
        /// </summary>
        public byte[] ListData
        {
            get
            {
                return listData;
            }
            set
            {
                listData = value;
            }
        }
    }
}

當然此框架目前只能傳送接收字串,待添加發送接收byte的功能。