程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#基礎知識 >> 常用類之TCP連接類-socket編程

常用類之TCP連接類-socket編程

編輯:C#基礎知識
tcp一般用於維持一個可信任的連接,比起udp更為安全可靠,在vs.net,分別有tcpclient和udpclient以及tcplistener,一般開發中基本可以滿足需要,但是這個有個很大的弊端,對於維持一個時間較長的,相互交互的來說,數據處理不是很明朗,vs/net中還有一個socket類,用他來做一個客戶/服務器段,同時在接發數據的時候,能相互獨立,這需要一個異步通訊過程
先實現服務器段:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

// State object for reading client data asynchronously
namespace TcpServer
{
public class StateObject 
{
    // Client  socket.
    public Socket workSocket = null;
    // Size of receive buffer.
    public const int BufferSize = 1024;
    // Receive buffer.
    public byte[] buffer = new byte[BufferSize];
    // Received data string.
    public StringBuilder sb = new StringBuilder();  
}

public class AsynchronousSocketListener 
{
    // Thread signal.
    public static ManualResetEvent allDone = new ManualResetEvent(false);
    private static Socket listener;
    private int _port=9010;
    public AsynchronousSocketListener() 
    {
    }
    public  void StopListening() 
    {
        listener.Close(); 
    }
    public int Port
    {
        set
        {
        _port=value;
        }
    }
    public  void StartListening() 
    {
        // Data buffer for incoming data.
        byte[] bytes = new Byte[1024];

        // Establish the local endpoint for the socket.
        // The DNS name of the computer
        // running the listener is "host.contoso.com".
        IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
        IPAddress ipAddress = ipHostInfo.AddressList[0];
        IPEndPoint localEndPoint = new IPEndPoint(ipAddress, _port);

        // Create a TCP/IP socket.
        listener = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp );

        // Bind the socket to the local endpoint and listen for incoming connections.
        try 
        {
            listener.Bind(localEndPoint);
            listener.Listen(100);

            while (true) 
            {
                // Set the event to nonsignaled state.
                allDone.Reset();

                // Start an asynchronous socket to listen for connections.
                Console.WriteLine("接收連接..");
                listener.BeginAccept( 
                    new AsyncCallback(AcceptCallback),
                    listener );

                // Wait until a connection is made before continuing.
                allDone.WaitOne();
            }

        } 
        catch (Exception e) 
        {
            Console.WriteLine(e.ToString());
        }

        Console.WriteLine("\nPress ENTER to continue...");
        Console.Read();

    }

    private void AcceptCallback(IAsyncResult ar) 
    {
        // Signal the main thread to continue.
        allDone.Set();

        // Get the socket that handles the client request.
        Socket listener = (Socket) ar.AsyncState;
        Socket handler = listener.EndAccept(ar);

        // Create the state object.
        StateObject state = new StateObject();
        state.workSocket = handler;
        handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
            new AsyncCallback(ReadCallback), state);
    }

    private  void ReadCallback(IAsyncResult ar) 
    {
        String content = String.Empty;

        // Retrieve the state object and the handler socket
        // from the asynchronous state object.
        StateObject state = (StateObject) ar.AsyncState;
        Socket handler = state.workSocket;
        int bytesRead=0 ;
        // Read data from the client socket.
        if(handler.Connected )
        {

            try
            {
                bytesRead = handler.EndReceive(ar);
            }
            catch(Exception ex)
            {
                handler.Close(); 
            }


            if (bytesRead > 0) 
            {
                // There  might be more data, so store the data received so far.


                // Check for end-of-file tag. If it is not there, read 
                // more data.
                content = Encoding.ASCII.GetString(
                    state.buffer,0,bytesRead);
                if (content.Length>0 && content.EndsWith("<EOF>")  ) 
                {
                    // All the data has been read from the 
                    // client. Display it on the console.
                    Console.WriteLine("從客戶端收到 {0} bytes 數據. \n Data : {1}",
                        content.Length, content );
                    // Echo the data back to the client.
                    Send(handler, "-數據確認,已經收到-<EOF>");
                } 
                else 
                {
                    // Not all data received. Get more.
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReadCallback), state);
                }
            }

        }

    }

    private void Send(Socket handler, String data) 
    {
        // Convert the string data to byte data using ASCII encoding.
        byte[] byteData = Encoding.UTF8.GetBytes(data);

        // Begin sending the data to the remote device.
        handler.BeginSend(byteData, 0, byteData.Length, 0,
            new AsyncCallback(SendCallback), handler);
    }

    private void SendCallback(IAsyncResult ar) 
    {
        try 
        {
            // Retrieve the socket from the state object.
            Socket handler = (Socket) ar.AsyncState;

            // Complete sending the data to the remote device.
            int bytesSent = handler.EndSend(ar);
            Console.WriteLine("發送 {0} bytes 到客戶端.", bytesSent);

            handler.Shutdown(SocketShutdown.Both);
            handler.Close();

        } 
        catch (Exception e) 
        {
            Console.WriteLine(e.ToString());
        }
    }

    
}

}

具體調用如下:
string p="";
            AsynchronousSocketListener _server=new AsynchronousSocketListener();
            _server.StartListening();
            if((p=Console.ReadLine().ToLower() )!="exit" )
            {
                _server.StopListening(); 
            }


緊接著實現客戶端,客戶端稍微復雜點,用一個session類來維持一個會話過程,coder類實現多種編碼,Datagram類定義一個具體的數據報文,默認為64個字節大小,

using System;
using System.Collections; 
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading; 
using System.Data; 
using System.Xml; 
using System.Xml.XPath; 
namespace Client
{

    #region 通訊對象
    public delegate void NetEvent(object sender, NetEventArgs e);
    public class CSocket
    {
        #region 字段

      
        /// <summary>
        /// 客戶端與服務器之間的會話類
        /// </summary>
        private Session _session;

        /// <summary>
        /// 客戶端是否已經連接服務器
        /// </summary>
        private bool _isConnected = false;
        private bool _isEcho = false;
        private StringBuilder sb=new StringBuilder(); 
        /// <summary>
        /// 接收數據緩沖區大小64K
        /// </summary>
        public const int DefaultBufferSize = 64*1024;

        /// <summary>
        /// 報文解析器
        /// </summary>
        private DatagramResolver _resolver;

        /// <summary>
        /// 通訊格式編碼解碼器
        /// </summary>
        private Coder _coder;

        

        /// <summary>
        /// 接收數據緩沖區
        /// </summary>
        private byte[] _recvDataBuffer = new byte[DefaultBufferSize];
        public  ManualResetEvent allDone = new ManualResetEvent(false);
        #endregion

        #region 事件定義

        //需要訂閱事件才能收到事件的通知,如果訂閱者退出,必須取消訂閱

        /// <summary>
        /// 已經連接服務器事件
        /// </summary>


        /// <summary>
        /// 接收到數據報文事件
        /// </summary>
          public event NetEvent ReceivedDatagram;
          public event NetEvent DisConnectedServer;
    public event NetEvent ConnectedServer;
        /// <summary>
        /// 連接斷開事件
        /// </summary>

        #endregion

        #region 屬性

        /// <summary>
        /// 返回客戶端與服務器之間的會話對象
        /// </summary>
        public Session ClientSession
        {
            get
            {
                return _session;
            }
        }

        /// <summary>
        /// 返回客戶端與服務器之間的連接狀態
        /// </summary>
        public bool IsConnected
        {
            get
            {
                return _isConnected;
            }
        }
        public bool IsEchoBack
        {
            get
            {
                return _isEcho;
            }
        }
        /// <summary>
        /// 數據報文分析器
        /// </summary>
        public DatagramResolver Resovlver
        {
            get
            {
                return _resolver;
            }
            set
            {
                _resolver = value;
            }
        }

        /// <summary>
        /// 編碼解碼器
        /// </summary>

        public Coder ServerCoder
        {
            get
            {
                return _coder;
            }
        }
        #endregion

        #region 公有方法

        /// <summary>
        /// 默認構造函數,使用默認的編碼格式
        /// </summary>
        public CSocket()
        {
            _coder = new Coder( Coder.EncodingMothord.gb2312 );
        }
                /// <summary>
        /// 構造函數,使用一個特定的編碼器來初始化
        /// </summary>
        /// <param name="_coder">報文編碼器</param>
        public CSocket( Coder coder )
        {
            _coder = coder;
        }

        /// <summary>
        /// 連接服務器
        /// </summary>
        /// <param name="ip">服務器IP地址</param>
        /// <param name="port">服務器端口</param>
        public virtual void Connect( string ip, int port)
        {
            if(IsConnected)
            {
                            Close();
            }

            Socket newsock= new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);

            IPEndPoint iep = new IPEndPoint( IPAddress.Parse(ip), port);
            newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock);

        }

        /// <summary>
        /// 發送數據報文
        /// </summary>
        /// <param name="datagram"></param>
        public virtual void Send( string datagram)
        {
            try
            {
                if(datagram.Length ==0 )
                {
                    return;
                }

                
allDone.WaitOne();
                //獲得報文的編碼字節
                byte [] data = _coder.GetEncodingBytes(datagram);
                _session.ClientSocket.BeginSend( data, 0, data.Length, SocketFlags.None,
                new AsyncCallback( SendDataEnd ), _session.ClientSocket);
            }

            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString() ); 
            }

        }

        /// <summary>
        /// 關閉連接
        /// </summary>
        public virtual void Close()
        {
            if(!_isConnected)
            {
                return;
            }

            _session.Close();

            _session = null;

            _isConnected = false;
        }

        #endregion 

        #region 受保護方法

        /// <summary>
        /// 數據發送完成處理函數
        /// </summary>
        /// <param name="iar"></param>
        protected virtual void SendDataEnd(IAsyncResult iar)
        {

            try
            {

                Socket remote = (Socket)iar.AsyncState;
                int sent = remote.EndSend(iar);

            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString() ); 
            }


        }

        /// <summary>
        /// 建立Tcp連接後處理過程
        /// </summary>
        /// <param name="iar">異步Socket</param>
        protected virtual void Connected(IAsyncResult iar)
        {

            Socket socket = (Socket)iar.AsyncState;
            //返回一個與之廉潔的連接
            socket.EndConnect(iar);

            //創建新的會話
            _session = new Session(socket);

            _isConnected = true;
            allDone.Set();

            
            try
            {
                _session.ClientSocket.BeginReceive(_recvDataBuffer, 0, 
                    DefaultBufferSize, SocketFlags.None,
                    new AsyncCallback(RecvData), socket);}
            catch(Exception ex)
            {
                socket.Close(); 
            }
        }

        /// <summary>
        /// 數據接收處理函數
        /// </summary>
        /// <param name="iar">異步Socket</param>
        public string recevie()
        {
            return this.sb.ToString()  ;
        }



        protected virtual void RecvData(IAsyncResult iar)
        {
            Socket remote = (Socket)iar.AsyncState;

            try
            {
                string receivedData="" ;
                int recv = remote.EndReceive(iar);
                if(recv>0)
                {
    receivedData = System.Text.Encoding.UTF8.GetString(_recvDataBuffer,0,recv )    ;

                    if(receivedData.EndsWith("<EOF>")  )
                    {
                        _isEcho=true;
                        sb.Append(receivedData); 
                        this._session.Datagram=  receivedData;
                        if(ReceivedDatagram==null)
                        {
                        ReceivedDatagram(this,new NetEventArgs(_session)  ) ;
                        }

                        Console.WriteLine(string.Format("{0},來自{1}",receivedData,_session.ClientSocket.RemoteEndPoint.ToString()  ) ) ;
                        this.allDone.Set();                            
                    }
                    else
                    {
                        Console.WriteLine("listen"); 
                        _session.ClientSocket.BeginReceive(_recvDataBuffer, 0, DefaultBufferSize, SocketFlags.None,
                            new AsyncCallback(RecvData), _session.ClientSocket);
                    }

                }

            

                
            }
            catch(SocketException ex)
            {
                Console.WriteLine(ex.ToString() ); 
            }

        }

        #endregion


    }

    /// <summary>
    /// 通訊編碼格式提供者,為通訊服務提供編碼和解碼服務
    /// 你可以在繼承類中定制自己的編碼方式如:數據加密傳輸等
    /// </summary>
    public class Coder
    {
        /// <summary>
        /// 編碼方式
        /// </summary>
        private EncodingMothord _encodingMothord;

        protected Coder()
        {

        }

        public Coder(EncodingMothord encodingMothord)
        {
            _encodingMothord = encodingMothord;
        }

        public enum EncodingMothord
        {
            gb2312=0,
            Default ,
            Unicode,
            UTF8,
            ASCII,
        }

        /// <summary>
        /// 通訊數據解碼
        /// </summary>
        /// <param name="dataBytes">需要解碼的數據</param>
        /// <returns>編碼後的數據</returns>
        public virtual string GetEncodingString( byte [] dataBytes,int size)
        {
            switch( _encodingMothord ) 
            {
                case EncodingMothord.gb2312:
                {
                    return Encoding.GetEncoding("gb2312").GetString(dataBytes,0,size);
                }
                case EncodingMothord.Default:
                {
                    return Encoding.Default.GetString(dataBytes,0,size);
                }
                case EncodingMothord.Unicode:
                {
                    return Encoding.Unicode.GetString(dataBytes,0,size);
                }
                case EncodingMothord.UTF8:
                {
                    return Encoding.UTF8.GetString(dataBytes,0,size);
                }
                case EncodingMothord.ASCII:
                {
                    return Encoding.ASCII.GetString(dataBytes,0,size);
                }
                default:
                {
                    throw( new Exception("未定義的編碼格式"));
                }
            }

        }

        /// <summary>
        /// 數據編碼
        /// </summary>
        /// <param name="datagram">需要編碼的報文</param>
        /// <returns>編碼後的數據</returns>
        public virtual byte[] GetEncodingBytes(string datagram)
        {
            switch( _encodingMothord) 
            {
                case EncodingMothord.gb2312:
                {
                    return Encoding.GetEncoding("gb2312").GetBytes(datagram);
                }
                case EncodingMothord.Default:
                {
                    return Encoding.Default.GetBytes(datagram);
                }
                case EncodingMothord.Unicode:
                {
                    return Encoding.Unicode.GetBytes(datagram);
                }
                case EncodingMothord.UTF8:
                {
                    return Encoding.UTF8.GetBytes(datagram);
                }
                case EncodingMothord.ASCII:
                {
                    return Encoding.ASCII.GetBytes(datagram);
                }
                default:
                {
                    throw( new Exception("未定義的編碼格式"));
                }
            }
        }

    }


    /// <summary>
    /// 數據報文分析器,通過分析接收到的原始數據,得到完整的數據報文.
    /// 繼承該類可以實現自己的報文解析方法.
    /// 通常的報文識別方法包括:固定長度,長度標記,標記符等方法
    /// 本類的現實的是標記符的方法,你可以在繼承類中實現其他的方法
    /// </summary>
    public class DatagramResolver
    {
        /// <summary>
        /// 報文結束標記
        /// </summary>
        private string endTag;

        /// <summary>
        /// 返回結束標記
        /// </summary>
        string EndTag
        {
            get
            {
                return endTag;
            }
        }

        /// <summary>
        /// 受保護的默認構造函數,提供給繼承類使用
        /// </summary>
        protected DatagramResolver()
        {

        }

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="endTag">報文結束標記</param>
        public DatagramResolver(string endTag)
        {
            if(endTag == null)
            {
                throw (new ArgumentNullException("結束標記不能為null"));
            }

            if(endTag == "")
            {
                throw (new ArgumentException("結束標記符號不能為空字符串"));
            }

            this.endTag = endTag;
        }

        /// <summary>
        /// 解析報文
        /// </summary>
        /// <param name="rawDatagram">原始數據,返回未使用的報文片斷,
        /// 該片斷會保存在Session的Datagram對象中</param>
        /// <returns>報文數組,原始數據可能包含多個報文</returns>
        public virtual string [] Resolve(ref string rawDatagram)
        {
            ArrayList datagrams  = new ArrayList();

            //末尾標記位置索引
            int tagIndex =-1;

            while(true)
            {
                tagIndex = rawDatagram.IndexOf(endTag,tagIndex+1);

                if( tagIndex == -1 )
                {
                    break;
                }
                else
                {
                    //按照末尾標記把字符串分為左右兩個部分
                    string newDatagram = rawDatagram.Substring(
                        0, tagIndex+endTag.Length);

                    datagrams.Add(newDatagram);

                    if(tagIndex+endTag.Length >= rawDatagram.Length)
                    {
                        rawDatagram="";

                        break;
                    }

                    rawDatagram = rawDatagram.Substring(tagIndex+endTag.Length,
                        rawDatagram.Length - newDatagram.Length);

                    //從開始位置開始查找
                    tagIndex=0;
                }
            }

            string [] results= new string[datagrams.Count];

            datagrams.CopyTo(results);

            return results;
        }

    }


    /// <summary>
    /// 客戶端與服務器之間的會話類
    /// 
    /// 版本:  1.1
    /// 替換版本: 1.0
    /// 
    /// 說明:
    ///    會話類包含遠程通訊端的狀態,這些狀態包括Socket,報文內容,
    ///    客戶端退出的類型(正常關閉,強制退出兩種類型)
    /// </summary>
    public class Session:ICloneable
    {
        #region 字段

        /// <summary>
        /// 會話ID
        /// </summary>
        private SessionId _id;

        /// <summary>
        /// 客戶端發送到服務器的報文
        /// 注意:在有些情況下報文可能只是報文的片斷而不完整
        /// </summary>
        private string _datagram;

        /// <summary>
        /// 客戶端的Socket
        /// </summary>
        private Socket _cliSock;

        /// <summary>
        /// 客戶端的退出類型
        /// </summary>
        private ExitType _exitType;

        /// <summary>
        /// 退出類型枚舉
        /// </summary>
        public enum ExitType
        {
            NormalExit ,
            ExceptionExit
        };

        #endregion

        #region 屬性

        /// <summary>
        /// 返回會話的ID
        /// </summary>
        public SessionId ID
        {
            get
            {
                return _id;
            }
        }

        /// <summary>
        /// 存取會話的報文
        /// </summary>
        public string Datagram
        {
            get
            {
                return _datagram;
            }
            set
            {
                _datagram = value;
            }
        }

        /// <summary>
        /// 獲得與客戶端會話關聯的Socket對象
        /// </summary>
        public Socket ClientSocket
        {
            get
            {
                return _cliSock;
            }
        }

        /// <summary>
        /// 存取客戶端的退出方式
        /// </summary>
        public ExitType TypeOfExit
        {
            get
            {
                return _exitType;
            }

            set
            {
                _exitType = value;
            }
        }

        #endregion

        #region 方法

        /// <summary>
        /// 使用Socket對象的Handle值作為HashCode,它具有良好的線性特征.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return (int)_cliSock.Handle;
        }

        /// <summary>
        /// 返回兩個Session是否代表同一個客戶端
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            Session rightObj = (Session)obj;

            return (int)_cliSock.Handle == (int)rightObj.ClientSocket.Handle;

        }

        /// <summary>
        /// 重載ToString()方法,返回Session對象的特征
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string result = string.Format("Session:{0},IP:{1}",
                _id,_cliSock.RemoteEndPoint.ToString());

            //result.C
            return result;
        }

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="cliSock">會話使用的Socket連接</param>
        public Session( Socket cliSock)
        {
            Debug.Assert( cliSock !=null );

            _cliSock = cliSock;

            _id = new SessionId( (int)cliSock.Handle);
        }

        /// <summary>
        /// 關閉會話
        /// </summary>
        public void Close()
        {
            Debug.Assert( _cliSock !=null );

            //關閉數據的接受和發送
            _cliSock.Shutdown( SocketShutdown.Both );

            //清理資源
            _cliSock.Close();
        }

        #endregion

        #region ICloneable 成員

        object System.ICloneable.Clone()
        {
            Session newSession = new Session(_cliSock);
            newSession.Datagram = _datagram;
            newSession.TypeOfExit = _exitType;

            return newSession;
        }

        #endregion
    }


    /// <summary>
    /// 唯一的標志一個Session,輔助Session對象在Hash表中完成特定功能
    /// </summary>
    public class SessionId
    {
        /// <summary>
        /// 與Session對象的Socket對象的Handle值相同,必須用這個值來初始化它
        /// </summary>
        private int _id;

        /// <summary>
        /// 返回ID值
        /// </summary>
        public int ID
        {
            get
            {
                return _id;
            }
        }

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="id">Socket的Handle值</param>
        public SessionId(int id)
        {
            _id = id;
        }

        /// <summary>
        /// 重載.為了符合Hashtable鍵值特征
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if(obj != null )
            {
                SessionId right = (SessionId) obj;

                return _id == right._id;
            }
            else if(this == null)
            {
                return true;
            }
            else
            {
                return false;
            }

        }

        /// <summary>
        /// 重載.為了符合Hashtable鍵值特征
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return _id;
        }

        /// <summary>
        /// 重載,為了方便顯示輸出
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return _id.ToString ();
        }

    }


    /// <summary>
    /// 服務器程序的事件參數,包含了激發該事件的會話對象
    /// </summary>
    public class NetEventArgs:EventArgs
    {

        #region 字段

        /// <summary>
        /// 客戶端與服務器之間的會話
        /// </summary>
        private Session _client;

        #endregion 

        #region 構造函數
        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="client">客戶端會話</param>
        public NetEventArgs(Session client)
        {
            if( null == client)
            {
                throw(new ArgumentNullException());
            }

            _client = client;
        }
        #endregion 

        #region 屬性

        /// <summary>
        /// 獲得激發該事件的會話對象
        /// </summary>
        public Session Client
        {
            get
            {
                return _client;
            }

        }

        #endregion 

    }
    #endregion
}
具體調用為:

using System;
using System.Collections; 
using System.Diagnostics;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading; 

namespace test
{
    /// <summary>
    /// Class1 的摘要說明。
    /// </summary>
    class Class1
    {
        /// <summary>
        /// 應用程序的主入口點。
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            //
            // TODO: 在此處添加代碼以啟動應用程序
            //

        string op="";

            while((op=Console.ReadLine())!="exit" )
            {
                if(op!="")
                {
                s( op);
                }
            }

        }

    static    void s(string d)
        {
            Client.CSocket _socket=new Client.CSocket();
            _socket.Connect("192.168.0.100",9010);
            _socket.Send(d +"<EOF>");
sd ds=new sd();
             _socket.ReceivedDatagram+=new Client.NetEvent(ds.asd);

        }
    }
    class sd
    {

    public    void    asd(object send,Client.NetEventArgs e)
        {

        }
    }
}
用<eof>標記來說明一段報文的結束,同時在各個階段可以構造事件讓兩個類更通用些,基本上完成了socket的異步通訊,可以再增加一個協議類,你可以利用兩類來實現符合你業務邏輯的協議,相互通訊
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved