常用类之TCP连接类-socket编程

2020年2月24日07:25:06常用类之TCP连接类-socket编程已关闭评论 408 25702字阅读85分40秒

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的异步通讯,可以再增加一个协议类,你可以利用两类来实现符合你业务逻辑的协议,相互通讯


继续阅读
编程十万个怎么办