C# Socket通信

前言

        使用C#编写Socket通信代码方式有很多种,对面不同的编码方式,一直是困扰我们的一个难题。于是为了让Socket通信成为我们项目开发的基础设施,而不是绊脚石,我重新整理了网上Socket通信的不同编码,方便在以后的项目中使用。

实例链接:https://download.csdn.net/download/lvxingzhe3/88738052

Socket定义

Socket:套接字(socket)是一个抽象层,应用程序之间通信不会和传输层直接建立联系,而是有一个能够连接应用层和传输层之间的套件,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

Socket通信实现方式

  • 使用Socket
  • 使用TcpListener
  • 使用SocketAsyncEventArgs
使用Socket

服务端程序:

class SocketServer
{
        static void Main(string[] args)
        {
            //1.创建Socket流模式  
            Socket tcpServe = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2.绑定IP跟端口号
            IPAddress ipAddress = new IPAddress(new byte[] {172,27,1,123 });//创建IP地址
            //IPEndPoint是对IP+端口号加了一层封装的类,7788是端口号
            EndPoint point = new IPEndPoint(ipAddress,7788);
            //向操作系统申请一个可用的IP和端口号用来做通信
            tcpServe.Bind(point);
            //3.开始监听(等待客户端做连接)
            Console.WriteLine("开始监听了");
            tcpServe.Listen(100);//参数是最大连接数
            //暂停当前线程,直到有一个客户端连接过来,进行下面的代码
            Socket clientSocket= tcpServe.Accept();
            //使用返回的Socket和客户端做通信
            Console.WriteLine("开始发送消息了");
            //4.发送给客户端消息
            string message = "hello,欢迎你";
            //对字符串做编码,得到一个字符串的字节数组
            byte[] data =Encoding.UTF8.GetBytes(message);
            clientSocket.Send(data);
            //5.接收来自客户端的消息
            byte[] b = new byte[1024];
            int length = clientSocket.Receive(b);
            string message3 = Encoding.UTF8.GetString(b,0,length);
            Console.WriteLine("接收一个从客户端发来的消息:"+message3);
            Console.ReadKey();
        }
}

客户端程序:

 class SocketClient
 {
        static void Main(string[] args)
        {
            //1.创建Socket
            Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2.发起建立连接的请求
            //可以把一个字符串的IP地址转换为一个IPAddress的对象
            IPAddress add = IPAddress.Parse("172.27.1.123");
            EndPoint point = new IPEndPoint(add, 7788);
            //通过IP:端口号定位到一个要连接的服务器端
            tcpClient.Connect(point);
            //3.接收来自服务器端的消息
            byte[] data = new byte[1024];
            //这里传递一个byte数组,实际上这个byte数组用来接收数据
            int length = tcpClient.Receive(data);
            //length返回值表示接收了多少数据
            //只把接收到的数据进行转化
            string message = Encoding.UTF8.GetString(data,0,length);
            Console.WriteLine(message);
            //4.发送给服务器端消息
            string message2 = Console.ReadLine();
            byte[] data1 = Encoding.UTF8.GetBytes(message2);
            tcpClient.Send(data1);
            Console.ReadKey();
        }
 }
使用TcpListener

服务端程序:

class User//创建客户端类
{
    public TcpClient client { get; private set; }
    //public BinaryReader br { get; private set; }
    //public BinaryWriter bw { get; private set; }
    public StreamReader br { get; private set; }
    public StreamWriter bw { get; private set; }
    public string userName { get; set; }//设备名
    public string remoteEndPoint { get; set; }//设备远程地址

    public User(TcpClient client)
    {
        this.client = client;
        //this.client.ReceiveTimeout = 60000;
        NetworkStream networkStream = client.GetStream();
        //networkStream.ReadTimeout = 5000;
        //br = new BinaryReader(networkStream);//将消息长度与消息一起发送
        //bw = new BinaryWriter(networkStream);
        br = new StreamReader(networkStream);//使用特殊标记分隔消息
        bw = new StreamWriter(networkStream);
        userName = null;
        remoteEndPoint = client.Client.RemoteEndPoint.ToString();
    }

    public void Close()
    {
        br.Close();
        bw.Close();
        client.Close();
      
    }
}

using System.Windows.Forms;
using System.Configuration;
using System.Collections.Specialized;
using System.Xml;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Diagnostics;
using System.Collections;
using System.IO;
using System.Runtime.InteropServices;



private TcpListener myListener;//新建TcpListenser对象
private List<User> userList = new List<User>();// 保存连接的所有用户
private string remoteDevice = null;//远程目标地址
private int indexDevice = 0;//远程目标地址索引
User deviceUser = null;//定义目标用户
Thread myThread=null;//新建一个监听线程

/// <summary>
/// 初始化服务端
/// </summary>
private void ServerInit()
{
    try
    {
        string ip = null;
        string name = Dns.GetHostName();
        IPAddress[] ipadrlist = Dns.GetHostAddresses(name);
        foreach (IPAddress ipa in ipadrlist)
        {
            if (ipa.AddressFamily == AddressFamily.InterNetwork)
            {
                ip = ipa.ToString();
            }
        }
        myListener = new TcpListener(IPAddress.Parse(ip), SystemInfo.Port);
        myListener.Start();
        //创建一个线程监客户端连接请求
        myThread = new Thread(ListenClientConnect);
        myThread.Start();
    }
    catch (Exception ipEX)
    {
        MessageBox.Show(ipEX.Message);
    }

}


/// <summary>
/// 接收客户端连接
/// </summary>
private void ListenClientConnect()
{
    TcpClient newClient = null;
    while (true)
    {
        try
        {
            newClient = myListener.AcceptTcpClient(); //当接收到客户端连接
        }
        catch
        {
         //当单击‘停止监听’或者退出此窗体时 AcceptTcpClient()会产生异常
         //因此可以利用此异常退出循环
            break;
        }
        //每接收一个客户端连接,就创建一个对应的线程循环接收该客户端发来的信息;
        User user = new User(newClient);
        Thread threadReceive = new Thread(ReceiveData);
        threadReceive.Start(user);
        userList.Add(user);
        //SendToClient(user, "ok");
        try
        {
            //ListviewAdd(user.remoteEndPoint);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);

        }
    }

}


/// <summary>
/// 处理接收的客户端信息
/// </summary>
/// <param name="userState">客户端信息</param>
private void ReceiveData(object userState)
{
    User user = (User)userState;
    TcpClient client = user.client;
    while (true)//当没有退出
    {
        string receiveString = null;
        try
        {
            //从网络流中读出字符串,此方法会自动判断字符串长度前缀
            receiveString = user.br.ReadLine();
        }
        catch (Exception)//user失去联系
        {
            //RemoveUser(user);
            break;
        }
        switch (receiveString)
        {

            case "ok": 
                break;
            default: ;
                break;
        }
    }

}


/// <summary>
/// 发送 message 给 user
/// </summary>
/// <param name="user">指定发给哪个用户</param>
/// <param name="message">信息内容</param>
private void SendToClient(User user, string message)
{
    try
    {
        //将字符串写入网络流,此方法会自动附加字符串长度前缀
        //user.bw.WriteLine(message);
        user.bw.Write(message);
        user.bw.Flush();

    }
    catch
    {

    }
}

/// <summary>
/// 移除用户
/// </summary>
/// <param name="user">指定要移除的用户</param>
private void RemoveUser(User user)
{
    userList.Remove(user);
    user.Close();
}


try
{
    ServerInit();//调用Socket服务端监听程序 
}
catch (Exception ex)
{

    MessageBox.Show(ex.Message);
}

客户端程序:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using Socket_Cilent;
using System.Threading;
using System.IO;



private string ServerIP; //IP
private int port;   //端口
private bool isExit = false;
private TcpClient client;
private BinaryReader br;
private BinaryWriter bw;



private void ReceiveData()//接收函数
{
    string receiveString = null;
    while (isExit == false)
    {
        try
        {
            //从网络流中读出字符串
            //此方法会自动判断字符串长度前缀,并根据长度前缀读出字符串
            receiveString = br.ReadString();
        }
        catch
        {
            if (isExit == false)
            {
                MessageBox.Show("与服务器失去连接");
            }
            break;
        }
        txt_recStr.Text += receiveString;
        //txt_recStr.AppendText(receiveString);
    }
    Application.Exit();
}



try
{
    
    ServerIP = txt_IP.Text.Trim(); //设定IP
    port = Convert.ToInt32(txt_Port.Text.Trim()); //设定端口
    //此处为方便演示,实际使用时要将Dns.GetHostName()改为服务器域名
    //IPAddress ipAd = IPAddress.Parse("182.150.193.7");
    client = new TcpClient();
    client.Connect(IPAddress.Parse(ServerIP), port);
    MessageBox.Show("连接成功");
}
catch (Exception ex)
{

    MessageBox.Show("连接失败,原因:" + ex.Message);
}
//获取网络流
NetworkStream networkStream = client.GetStream();
//将网络流作为二进制读写对象
br = new BinaryReader(networkStream);
bw = new BinaryWriter(networkStream);
Thread threadReceive = new Thread(new ThreadStart(ReceiveData));
threadReceive.IsBackground = true;
threadReceive.Start();       
SocketAsyncEventArgs

        SocketAsyncEventArgs是一个套接字操作的类,主要作用是实现socket消息的异步接收和发送。由专用的高性能套接字应用程序使用的替代异步模式,主要功能是避免在大容量异步套接字 I/O 期间重复分配和同步对象。

服务端程序:

  internal class AsyncUserToken
  {
      /// <summary>  
      /// 客户端IP地址  
      /// </summary>  
      public IPAddress IPAddress { get; set; }

      /// <summary>  
      /// 远程地址  
      /// </summary>  
      public EndPoint Remote { get; set; }

      /// <summary>  
      /// 通信SOKET  
      /// </summary>  
      public Socket Socket { get; set; }

      /// <summary>  
      /// 连接时间  
      /// </summary>  
      public DateTime ConnectTime { get; set; }

      /// <summary>  
      /// 数据缓存区  
      /// </summary>  
      public List<byte> Buffer { get; set; }
      public string username { get; set; }

      public AsyncUserToken()
      {
          this.Buffer = new List<byte>();
      }
  }

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

namespace Socket_Server
{
    /// <summary>
    /// Based on example from http://msdn2.microsoft.com/en-us/library/bb517542.aspx
    /// This class creates a single large buffer which can be divided up 
    /// and assigned to SocketAsyncEventArgs objects for use with each 
    /// socket I/O operation.  
    /// This enables bufffers to be easily reused and guards against 
    /// fragmenting heap memory.
    /// </summary>
    /// <remarks>The operations exposed on the BufferManager class are not thread safe.</remarks>
    internal sealed class BufferManager
    {
        /// <summary>
        /// The underlying Byte array maintained by the Buffer Manager.
        /// </summary>
        private Byte[] buffer;

        /// <summary>
        /// Size of the underlying Byte array.
        /// </summary>
        private Int32 bufferSize;

        /// <summary>
        /// Current index of the underlying Byte array.
        /// </summary>
        private Int32 currentIndex;

        /// <summary>
        /// Pool of indexes for the Buffer Manager.
        /// </summary>
        private Stack<Int32> freeIndexPool;

        /// <summary>
        /// The total number of bytes controlled by the buffer pool.
        /// </summary>
        private Int32 numBytes;

        /// <summary>
        /// Instantiates a buffer manager.
        /// </summary>
        /// <param name="totalBytes">The total number of bytes for the buffer pool.</param>
        /// <param name="bufferSize">Size of the buffer pool.</param>
        internal BufferManager(Int32 totalBytes, Int32 bufferSize)
        {
            this.numBytes = totalBytes;
            this.currentIndex = 0;
            this.bufferSize = bufferSize;
            this.freeIndexPool = new Stack<Int32>();
        }

        /// <summary>
        /// Removes the buffer from a SocketAsyncEventArg object. 
        /// This frees the buffer back to the buffer pool.
        /// </summary>
        /// <param name="args">SocketAsyncEventArgs where is the buffer to be removed.</param>
        internal void FreeBuffer(SocketAsyncEventArgs args)
        {
            this.freeIndexPool.Push(args.Offset);
            args.SetBuffer(null, 0, 0);
        }

        /// <summary>
        ///  Allocates buffer space used by the buffer pool. 
        /// </summary>
        internal void InitBuffer()
        {
            // Create one big large buffer and divide that out to each SocketAsyncEventArg object.
            this.buffer = new Byte[this.numBytes];
        }

        /// <summary>
        /// Assigns a buffer from the buffer pool to the specified SocketAsyncEventArgs object.
        /// </summary>
        /// <param name="args">SocketAsyncEventArgs where is the buffer to be allocated.</param>
        /// <returns>True if the buffer was successfully set, else false.</returns>
        internal Boolean SetBuffer(SocketAsyncEventArgs args)
        {
            if (this.freeIndexPool.Count > 0)
            {
                args.SetBuffer(this.buffer, this.freeIndexPool.Pop(), this.bufferSize);
            }
            else
            {
                if ((this.numBytes - this.bufferSize) < this.currentIndex)
                {
                    return false;
                }
                args.SetBuffer(this.buffer, this.currentIndex, this.bufferSize);
                this.currentIndex += this.bufferSize;
            }

            return true;
        }
    }
}

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

namespace Socket_Server
{
    /// <summary>
    /// Based on example from http://msdn2.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.socketasynceventargs.aspx
    /// Represents a collection of reusable SocketAsyncEventArgs objects.  
    /// </summary>
    internal sealed class SocketAsyncEventArgsPool
    {
        /// <summary>
        /// Pool of SocketAsyncEventArgs.
        /// </summary>
        Stack<SocketAsyncEventArgs> pool;

        /// <summary>
        /// Initializes the object pool to the specified size.
        /// </summary>
        /// <param name="capacity">Maximum number of SocketAsyncEventArgs objects the pool can hold.</param>
        internal SocketAsyncEventArgsPool(Int32 capacity)
        {
            this.pool = new Stack<SocketAsyncEventArgs>(capacity);
        }

        /// <summary>
        /// The number of SocketAsyncEventArgs instances in the pool. 
        /// </summary>
        internal Int32 Count
        {
            get { return this.pool.Count; }
        }

        /// <summary>
        /// Removes a SocketAsyncEventArgs instance from the pool.
        /// </summary>
        /// <returns>SocketAsyncEventArgs removed from the pool.</returns>
        internal SocketAsyncEventArgs Pop()
        {
            lock (this.pool)
            {
                return this.pool.Pop();
            }
        }

        /// <summary>
        /// Add a SocketAsyncEventArg instance to the pool. 
        /// </summary>
        /// <param name="item">SocketAsyncEventArgs instance to add to the pool.</param>
        internal void Push(SocketAsyncEventArgs item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("Items added to a SocketAsyncEventArgsPool cannot be null");
            }
            lock (this.pool)
            {
                this.pool.Push(item);
            }
        }
    }
}

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

namespace Socket_Server
{
    /// <summary>
    /// Based on example from http://msdn2.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
    /// Implements the connection logic for the socket server.  
    /// After accepting a connection, all data read from the client 
    /// is sent back to the client with an aditional word, to demonstrate how to manipulate the data. 
    /// The read and echo back to the client pattern is continued until the client disconnects.
    /// </summary>
    internal sealed class SocketListener
    {
        /// <summary>
        /// 所有套接字操作的大型可重用缓冲区集
        /// </summary>
        private BufferManager bufferManager;

        /// <summary>
        /// 套接字用于侦听传入的连接请求
        /// </summary>
        private Socket listenSocket;

        /// <summary>
        /// 客户端最大连接数
        /// </summary>
        private int maxNumConnections;

        /// <summary>
        /// 用于写入、读取和接受套接字操作的可重用SocketAsyncEventArgs对象池
        /// </summary>
        private SocketAsyncEventArgsPool readWritePool;

        /// <summary>
        ///读取、写入(不为接受分配缓冲区空间)
        /// </summary>
        private const int opsToPreAlloc = 2;

        /// <summary>
        /// 连接到服务器的客户端总数
        /// </summary>
        private int numConnectedSockets;

        /// <summary>
        /// 用于同步服务器执行的互斥
        /// </summary>
        private static Mutex mutex = new Mutex();

        /// <summary>
        /// 控制连接到服务器的客户端总数
        /// </summary>
        private Semaphore semaphoreAcceptedClients;

        /// <summary>
        /// 服务器接收的总字节数计数器
        /// </summary>
        private int totalBytesRead;

        /// <summary>
        /// 客户端列表
        /// </summary>
        List<AsyncUserToken> m_clients;

        /// <summary>  
        /// 获取客户端列表  
        /// </summary>  
        public List<AsyncUserToken> ClientList { get { return m_clients; } }

        /// <summary>
        /// 接收到客户端字符串委托
        /// </summary>
        public Action<string> ReceiveClientStrData;

        /// <summary>
        /// 心跳字符
        /// </summary>
        private static string HeartBeatStr = "pop";

        /// <summary>  
        /// 接收到客户端的字节数据事件  
        /// </summary>  
        public event OnReceiveData ReceiveClientData;

        /// <summary>  
        /// 客户端连接数量变化时触发  
        /// </summary>  
        /// <param name="num">当前增加客户的个数(用户退出时为负数,增加时为正数,为1)</param>  
        /// <param name="token">增加用户的信息</param>  
        public delegate void OnClientNumberChange(int num, AsyncUserToken token);

        /// <summary>  
        /// 接收到客户端的数据  
        /// </summary>  
        /// <param name="token">客户端</param>  
        /// <param name="buff">客户端数据</param>  
        public delegate void OnReceiveData(AsyncUserToken token, byte[] buff);
        /// <summary>  
        /// 客户端连接数量变化事件  
        /// </summary>  
        public event OnClientNumberChange ClientNumberChange;

        public delegate void StopSeverEvent();
        /// <summary>
        /// 服务停止事件
        /// </summary>
        public event StopSeverEvent ServerStopedEvent;

        /// <summary>
        /// Create an uninitialized server instance.  
        /// To start the server listening for connection requests
        /// call the Init method followed by Start method.
        /// </summary>
        /// <param name="maxNumConnections">Maximum number of connections to be handled simultaneously.</param>
        /// <param name="receiveBufferSize">Buffer size to use for each socket I/O operation.</param>
        internal SocketListener(int maxNumConnections, int receiveBufferSize)
        {
            this.totalBytesRead = 0;
            this.numConnectedSockets = 0;
            this.maxNumConnections = maxNumConnections;

            // Allocate buffers such that the maximum number of sockets can have one outstanding read and 
            // write posted to the socket simultaneously .
            this.bufferManager = new BufferManager(receiveBufferSize * maxNumConnections * opsToPreAlloc,
                receiveBufferSize);

            this.readWritePool = new SocketAsyncEventArgsPool(maxNumConnections);
            this.semaphoreAcceptedClients = new Semaphore(maxNumConnections, maxNumConnections);
        }

        /// <summary>
        /// Initializes the server by preallocating reusable buffers and 
        /// context objects.  These objects do not need to be preallocated 
        /// or reused, but it is done this way to illustrate how the API can 
        /// easily be used to create reusable objects to increase server performance.
        /// </summary>
        internal void Init()
        {
            // Allocates one large Byte buffer which all I/O operations use a piece of. This guards 
            // against memory fragmentation.
            this.bufferManager.InitBuffer();
            m_clients = new List<AsyncUserToken>();
            // Preallocate pool of SocketAsyncEventArgs objects.
            SocketAsyncEventArgs readWriteEventArg;

            for (int i = 0; i < this.maxNumConnections; i++)
            {
                // Preallocate a set of reusable SocketAsyncEventArgs.
                readWriteEventArg = new SocketAsyncEventArgs();
                readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnIOCompleted);
                readWriteEventArg.UserToken = new AsyncUserToken();
                // Assign a Byte buffer from the buffer pool to the SocketAsyncEventArg object.
                this.bufferManager.SetBuffer(readWriteEventArg);

                // Add SocketAsyncEventArg to the pool.
                this.readWritePool.Push(readWriteEventArg);
            }
        }

        /// <summary>
        /// Starts the server such that it is listening for incoming connection requests.    
        /// </summary>
        /// <param name="localEndPoint">The endpoint which the server will listening for connection requests on.</param>
        internal void Start(Object data)
        {
            int port = (int)data;
            m_clients.Clear();
            // Get host related information.
            IPAddress[] addressList = Dns.GetHostEntry(Environment.MachineName).AddressList;
            // Get endpoint for the listener.
            IPEndPoint localEndPoint = new IPEndPoint(addressList[addressList.Length - 1], port);

            // Create the socket which listens for incoming connections.
            this.listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
            {
                // Set dual-mode (IPv4 & IPv6) for the socket listener.
                // 27 is equivalent to IPV6_V6ONLY socket option in the winsock snippet below,
                // based on http://blogs.msdn.com/wndp/archive/2006/10/24/creating-ip-agnostic-applications-part-2-dual-mode-sockets.aspx
                this.listenSocket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);
                this.listenSocket.Bind(new IPEndPoint(IPAddress.IPv6Any, localEndPoint.Port));
            }
            else
            {
                // Associate the socket with the local endpoint.
                this.listenSocket.Bind(localEndPoint);
            }

            // Start the server with a listen backlog of max connections.
            this.listenSocket.Listen(this.maxNumConnections);

            // Post accepts on the listening socket.
            this.StartAccept(null);
            mutex.WaitOne();
        }

        /// <summary>
        /// Stop the server.
        /// </summary>
        internal void Stop()
        {
            AsyncUserToken[] clients = m_clients.ToArray();
            for (int i = 0; i < clients.Length; i++)
            {
                try
                {
                    AsyncUserToken token = clients[i];
                    token.Socket.Shutdown(SocketShutdown.Both);
                }
                catch (Exception) { }
            }
            try
            {
                if (m_clients.Count > 0)
                    listenSocket.Shutdown(SocketShutdown.Both);
            }
            catch (Exception e)//当socket 无连接时(即没有可用已连接的client)
            {
                // listenSocket.Disconnect(false);///因为无可用的socket连接,此方法不可用
                Console.WriteLine(e.Message);
            }
            finally
            {
                if (listenSocket != null)
                {
                    listenSocket.Close();
                }
                //通知界面,server已经停止
                if (ServerStopedEvent != null)
                    ServerStopedEvent();
                lock (m_clients)
                {
                    m_clients.Clear();
                }
                mutex.ReleaseMutex();
            }
        }

        /// <summary>
        /// Close the socket associated with the client.
        /// </summary>
        /// <param name="e">SocketAsyncEventArg associated with the completed send/receive operation.</param>
        private void CloseClientSocket(SocketAsyncEventArgs e)
        {        
            AsyncUserToken token = e.UserToken as AsyncUserToken;
            AsyncUserToken userToken = ClientList.Find(t => t.Remote == token.Remote);
            if(userToken!=null)
            {
                lock (m_clients) { m_clients.Remove(userToken); }
            } 
            //如果有事件,则调用事件,发送客户端数量变化通知  
            if (ClientNumberChange != null)
                ClientNumberChange(-1, token);
            try
            {
                token.Socket.Shutdown(SocketShutdown.Send);
            }
            catch (Exception ex)
            {
                // Throws if client process has already closed.
                Console.WriteLine(ex.ToString());
            }
            token.Socket.Close();
            Interlocked.Decrement(ref this.numConnectedSockets);
            // Decrement the counter keeping track of the total number of clients connected to the server.
            this.semaphoreAcceptedClients.Release();
            Console.WriteLine("A client has been disconnected from the server. There are {0} clients connected to the server", this.numConnectedSockets);
            e.UserToken = new AsyncUserToken();
            // Free the SocketAsyncEventArg so they can be reused by another client.
            this.readWritePool.Push(e);
        }

        /// <summary>
        /// Begins an operation to accept a connection request from the client.
        /// </summary>
        /// <param name="acceptEventArg">The context object to use when issuing 
        /// the accept operation on the server's listening socket.</param>
        private void StartAccept(SocketAsyncEventArgs acceptEventArg)
        {
            if (acceptEventArg == null)
            {
                acceptEventArg = new SocketAsyncEventArgs();
                acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
            }
            else
            {
                // Socket must be cleared since the context object is being reused.
                acceptEventArg.AcceptSocket = null;
            }

            this.semaphoreAcceptedClients.WaitOne();
            Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg);
            if (!willRaiseEvent)
            {
                this.ProcessAccept(acceptEventArg);
            }
        }

        /// <summary>
        /// Process the accept for the socket listener.
        /// </summary>
        /// <param name="e">SocketAsyncEventArg associated with the completed accept operation.</param>
        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            try
            {
                if(!e.AcceptSocket.Connected) 
                {
                    return;
                }
                if (e.BytesTransferred > 0)
                {
                    Interlocked.Increment(ref this.numConnectedSockets);
                    Console.WriteLine("Client connection accepted. There are {0} clients connected to the server",
                        this.numConnectedSockets);
                }

                // Get the socket for the accepted client connection and put it into the 
                // ReadEventArg object user token.
                SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop();
                AsyncUserToken userToken = (AsyncUserToken)readEventArgs.UserToken;
                //readEventArgs.UserToken = e.AcceptSocket;

                userToken.username = "Mtt";
                userToken.Socket = e.AcceptSocket;
                userToken.ConnectTime = DateTime.Now;
                userToken.Remote = e.AcceptSocket.RemoteEndPoint;
                userToken.IPAddress = ((IPEndPoint)(e?.AcceptSocket?.RemoteEndPoint))?.Address;
                if(!m_clients.Exists(t=>t.Remote==userToken.Remote))
                {
                    lock (m_clients) { m_clients.Add(userToken); }
                }
                if (ClientNumberChange != null)
                    ClientNumberChange(1, userToken);
                // As soon as the client is connected, post a receive to the connection.
                Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
                if (!willRaiseEvent)
                {
                    this.ProcessReceive(readEventArgs);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            // Accept the next connection request  
            if (e.SocketError == SocketError.OperationAborted) return;
            StartAccept(e);
        }

        /// <summary>
        /// Callback method associated with Socket.AcceptAsync 
        /// operations and is invoked when an accept operation is complete.
        /// </summary>
        /// <param name="sender">Object who raised the event.</param>
        /// <param name="e">SocketAsyncEventArg associated with the completed accept operation.</param>
        private void OnAcceptCompleted(object sender, SocketAsyncEventArgs e)
        {
            this.ProcessAccept(e);
        }

        /// <summary>
        /// Callback called whenever a receive or send operation is completed on a socket.
        /// </summary>
        /// <param name="sender">Object who raised the event.</param>
        /// <param name="e">SocketAsyncEventArg associated with the completed send/receive operation.</param>
        private void OnIOCompleted(object sender, SocketAsyncEventArgs e)
        {
            // Determine which type of operation just completed and call the associated handler.
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    this.ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    this.ProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }
        }

        /// <summary>
        /// This method is invoked when an asynchronous receive operation completes. 
        /// If the remote host closed the connection, then the socket is closed.  
        /// If data was received then the data is echoed back to the client.
        /// </summary>
        /// <param name="e">SocketAsyncEventArg associated with the completed receive operation.</param>
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            try
            {
                AsyncUserToken token = (AsyncUserToken)e.UserToken;
                // check if the remote host closed the connection  
                if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                {
                    int bytesTransferred = e.BytesTransferred;
                    // Get the message received from the listener.
                    string received = Encoding.UTF8.GetString(e.Buffer, e.Offset, bytesTransferred);
                    if(bytesTransferred==3 && received.Equals(HeartBeatStr))
                    {
                        Console.WriteLine($"接收到心跳数据->{received}");
                    }
                    else
                    {
                        // Increment the count of the total bytes receive by the server.
                        Interlocked.Add(ref this.totalBytesRead, bytesTransferred);
                        Console.WriteLine("Received: \"{0}\". The server has read a total of {1} bytes.", received, this.totalBytesRead);
                        ReceiveClientStrData?.Invoke(received);
                    }
                    #region 以字节方式接收
                    读取数据  
                    //byte[] data = new byte[e.BytesTransferred];
                    //Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);
                    //lock (token.Buffer)
                    //{
                    //    token.Buffer.AddRange(data);
                    //}
                    //do
                    //{
                    //    //判断包的长度  
                    //    byte[] lenBytes = token.Buffer.GetRange(0, 4).ToArray();
                    //    int packageLen = BitConverter.ToInt32(lenBytes, 0);
                    //    if (packageLen > token.Buffer.Count - 4)
                    //    {   //长度不够时,退出循环,让程序继续接收  
                    //        break;
                    //    }

                    //    //包够长时,则提取出来,交给后面的程序去处理  
                    //    byte[] rev = token.Buffer.GetRange(4, packageLen).ToArray();
                    //    //从数据池中移除这组数据  
                    //    lock (token.Buffer)
                    //    {
                    //        token.Buffer.RemoveRange(0, packageLen + 4);
                    //    }

                    //    if (ReceiveClientData != null)
                    //        ReceiveClientData(token, rev);

                    //} while (token.Buffer.Count > 4);
                    #endregion
                    if (!token.Socket.ReceiveAsync(e))
                        this.ProcessReceive(e);
                }
                else
                {
                    this.CloseClientSocket(e);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }

        /// <summary>
        /// This method is invoked when an asynchronous send operation completes.  
        /// The method issues another receive on the socket to read any additional 
        /// data sent from the client.
        /// </summary>
        /// <param name="e">SocketAsyncEventArg associated with the completed send operation.</param>
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                // done echoing data back to the client  
                AsyncUserToken token = (AsyncUserToken)e.UserToken;
                // Read the next block of data send from the client.
                Boolean willRaiseEvent = token.Socket.ReceiveAsync(e);
                if (!willRaiseEvent)
                {
                    this.ProcessReceive(e);
                }
            }
            else
            {
                this.CloseClientSocket(e);
            }
        }

        /// <summary>  
        /// 对数据进行打包,然后再发送  
        /// </summary>  
        /// <param name="token"></param>  
        /// <param name="message"></param>  
        /// <returns></returns>  
        public bool SendMessage(AsyncUserToken token, byte[] message)
        {
            bool isSuccess = false;
            if (token == null || token.Socket == null || !token.Socket.Connected)
                return isSuccess;

            try
            {
                //新建异步发送对象, 发送消息  
                SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();
                sendArg.UserToken = token;
                sendArg.SetBuffer(message, 0, message.Length);  //将数据放置进去.  
                isSuccess = token.Socket.SendAsync(sendArg);

                #region 以制定简单协议发送
                对要发送的消息,制定简单协议,头4字节指定包的大小,方便客户端接收(协议可以自己定)  
                //byte[] buff = new byte[message.Length + 4];
                //byte[] len = BitConverter.GetBytes(message.Length);
                //Array.Copy(len, buff, 4);
                //Array.Copy(message, 0, buff, 4, message.Length);
                token.Socket.Send(buff);  //
                新建异步发送对象, 发送消息  
                //SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();
                //sendArg.UserToken = token;
                //sendArg.SetBuffer(buff, 0, buff.Length);  //将数据放置进去.  
                //isSuccess = token.Socket.SendAsync(sendArg);
                #endregion
            }
            catch (Exception e)
            {
                Console.WriteLine("SendMessage - Error:" + e.Message);
            }
            return isSuccess;
        }


        public void CloseClient(AsyncUserToken token)
        {
            try
            {
                token.Socket.Shutdown(SocketShutdown.Both);
            }
            catch (Exception) { }
        }

    }
}

客户端程序:

      /// <summary>
    /// 自定义SocketAsyncEventArgs类,获取SocketAsyncEventArgs使用状态
    /// </summary>
    internal class MySocketEventArgs : SocketAsyncEventArgs
    {

        /// <summary>  
        /// 标识,只是一个编号而已  
        /// </summary>  
        public int ArgsTag { get; set; }

        /// <summary>  
        /// 设置/获取使用状态  
        /// </summary>  
        public bool IsUsing { get; set; }
    }

  internal class BufferManager
  {
      int m_numBytes;                 // the total number of bytes controlled by the buffer pool  
      byte[] m_buffer;                // the underlying byte array maintained by the Buffer Manager  
      Stack<int> m_freeIndexPool;        
      int m_currentIndex;
      int m_bufferSize;

      public BufferManager(int totalBytes, int bufferSize)
      {
          m_numBytes = totalBytes;
          m_currentIndex = 0;
          m_bufferSize = bufferSize;
          m_freeIndexPool = new Stack<int>();
      }

      /// <summary>
      /// Allocates buffer space used by the buffer pool  
      /// </summary>
      public void InitBuffer()
      {
          // create one big large buffer and divide that   
          // out to each SocketAsyncEventArg object  
          m_buffer = new byte[m_numBytes];
      }

      // Assigns a buffer from the buffer pool to the   
      // specified SocketAsyncEventArgs object  
      // <returns>true if the buffer was successfully set, else false</returns>  
      public bool SetBuffer(SocketAsyncEventArgs args)
      {

          if (m_freeIndexPool.Count > 0)
          {
              args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
          }
          else
          {
              if ((m_numBytes - m_bufferSize) < m_currentIndex)
              {
                  return false;
              }
              args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
              m_currentIndex += m_bufferSize;
          }
          return true;
      }

      /// <summary>
      /// Removes the buffer from a SocketAsyncEventArg object.  
      /// This frees the buffer back to the buffer pool  
      /// </summary>
      /// <param name="args"></param>
      public void FreeBuffer(SocketAsyncEventArgs args)
      {
          m_freeIndexPool.Push(args.Offset);
          args.SetBuffer(null, 0, 0);
      }
  }

  internal class Client : IDisposable
  {
      private const int BuffSize = 1024;

      // The socket used to send/receive messages.  
      private Socket clientSocket;

      // Flag for connected socket.  
      private Boolean connected = false;

      // Listener endpoint.  
      private IPEndPoint hostEndPoint;

      // Signals a connection.  
      private static AutoResetEvent autoConnectEvent = new AutoResetEvent(false);

      BufferManager m_bufferManager;
      //定义接收数据的对象  
      List<byte> m_buffer;
      //发送与接收的MySocketEventArgs变量定义
      private List<MySocketEventArgs> listArgs = new List<MySocketEventArgs>();
      private MySocketEventArgs receiveEventArgs = new MySocketEventArgs();
      int tagCount = 0;

      /// <summary>  
      /// 当前连接状态  
      /// </summary>  
      public bool Connected { get { return clientSocket != null && clientSocket.Connected; } }

      //服务器主动发出数据受理委托及事件  
      public delegate void OnServerDataReceived(byte[] receiveBuff);
      public event OnServerDataReceived ServerDataHandler;

      /// <summary>
      /// 接收到客户端字符串委托
      /// </summary>
      public Action<string> ReceiveClientStrData;

      //服务器主动关闭连接委托及事件  
      public delegate void OnServerStop();
      public event OnServerStop ServerStopEvent;

      /// <summary>
      /// Create an uninitialized client instance.  
      /// To start the send/receive processing call the Connect method followed by SendReceive method. 
      /// </summary>
      internal Client(String ip, int port)
      {
          // Instantiates the endpoint and socket.  
          hostEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
          clientSocket = new Socket(hostEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
          m_bufferManager = new BufferManager(BuffSize * 2, BuffSize);
          m_buffer = new List<byte>();
      }

      /// <summary>  
      /// 连接到主机  
      /// 0.连接成功, 其他值失败,参考SocketError的值列表
      /// </summary>  
      internal SocketError Connect()
      {
          SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs();
          connectArgs.UserToken = clientSocket;
          //AsyncUserToken userToken = new AsyncUserToken();
          //userToken.username = "user";
          //userToken.Socket = clientSocket;
          //userToken.ConnectTime = DateTime.Now;
          //userToken.Remote = clientSocket.RemoteEndPoint;
          //userToken.IPAddress = ((IPEndPoint)(clientSocket?.RemoteEndPoint))?.Address;
          //connectArgs.UserToken = userToken;

          connectArgs.RemoteEndPoint = hostEndPoint;
          connectArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnect);

          clientSocket.ConnectAsync(connectArgs);
          autoConnectEvent.WaitOne(); //阻塞. 让程序在这里等待,直到连接响应后再返回连接结果  

          SocketError re = connectArgs.SocketError;
          return re;
      }

      /// <summary>
      /// Disconnect from the host.  
      /// </summary>
      internal void Disconnect()
      {
          clientSocket.Disconnect(true);
          ///将所有注册的事件也注销掉,释放相关资源
      }

      /// <summary>
      /// Calback for connect operation  
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void OnConnect(object sender, SocketAsyncEventArgs e)
      {
          // Signals the end of connection.  
          autoConnectEvent.Set(); //释放阻塞.  
          // Set the flag for socket connected.  
          SocketError re = e.SocketError;
          connected = (e.SocketError == SocketError.Success);
          //如果连接成功,则初始化socketAsyncEventArgs  
          if (connected)
              InitArgs(e);
      }


      #region args
      /// <summary>  
      /// 初始化收发参数  
      /// </summary>  
      /// <param name="e"></param>  
      private void InitArgs(SocketAsyncEventArgs e)
      {
          m_bufferManager.InitBuffer();
          //发送参数  
          InitSendArgs();
          //接收参数  
          receiveEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
          receiveEventArgs.UserToken = e.UserToken;
          receiveEventArgs.ArgsTag = 0;
          m_bufferManager.SetBuffer(receiveEventArgs);

          //启动接收,不管有没有,一定得启动.否则有数据来了也不知道.  
          if (!e.ConnectSocket.ReceiveAsync(receiveEventArgs))
              ProcessReceive(receiveEventArgs);
      }

      /// <summary>  
      /// 初始化发送参数MySocketEventArgs  
      /// </summary>  
      /// <returns></returns>  
      MySocketEventArgs InitSendArgs()
      {
          MySocketEventArgs sendArg = new MySocketEventArgs();
          sendArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
          sendArg.UserToken = clientSocket;
          sendArg.RemoteEndPoint = hostEndPoint;
          sendArg.IsUsing = false;
          Interlocked.Increment(ref tagCount);
          sendArg.ArgsTag = tagCount;
          lock (listArgs)
          {
              listArgs.Add(sendArg);
          }
          return sendArg;
      }

      void IO_Completed(object sender, SocketAsyncEventArgs e)
      {
          MySocketEventArgs mys = (MySocketEventArgs)e;
          // determine which type of operation just completed and call the associated handler  
          switch (e.LastOperation)
          {
              case SocketAsyncOperation.Receive:
                  ProcessReceive(e);
                  break;
              case SocketAsyncOperation.Send:
                  mys.IsUsing = false; //数据发送已完成.状态设为False  
                  ProcessSend(e);
                  break;
              default:
                  throw new ArgumentException("The last operation completed on the socket was not a receive or send");
          }
      }


      #endregion

      #region read and write
      /// <summary>
      /// This method is invoked when an asynchronous receive operation completes.   
      /// If the remote host closed the connection, then the socket is closed. 
      /// If data was received then the data is echoed back to the client.  
      /// </summary> 
      private void ProcessReceive(SocketAsyncEventArgs e)
      {
          try
          {
              // check if the remote host closed the connection  
              Socket token = (Socket)e.UserToken;
              if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
              {
                  //读取数据  
                  byte[] data = new byte[e.BytesTransferred];
                  Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);
                  lock (m_buffer)
                  {
                      m_buffer.AddRange(data);
                  }

                  #region 接收字符串方法
                  // Get the message received from the listener.
                  String received = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
                  if (!string.IsNullOrEmpty(received))
                  {
                      ReceiveClientStrData(received);
                  }
                  #endregion

                  #region 以协议方式接收字节,大数据接收
                  //do
                  //{
                  //    //注意: 这里是需要和服务器有协议的,我做了个简单的协议,就是一个完整的包是包长(4字节)+包数据,便于处理,当然你可以定义自己需要的;   
                  //    //判断包的长度,前面4个字节.  
                  //    byte[] lenBytes = m_buffer.GetRange(0, 4).ToArray();
                  //    int packageLen = BitConverter.ToInt32(lenBytes, 0);
                  //    if (packageLen <= m_buffer.Count - 4)
                  //    {
                  //        //包够长时,则提取出来,交给后面的程序去处理  
                  //        byte[] rev = m_buffer.GetRange(4, packageLen).ToArray();
                  //        //从数据池中移除这组数据,为什么要lock,你懂的  
                  //        lock (m_buffer)
                  //        {
                  //            m_buffer.RemoveRange(0, packageLen + 4);
                  //        }
                  //        //将数据包交给前台去处理  
                  //        DoReceiveEvent(rev);
                  //    }
                  //    else
                  //    {   //长度不够,还得继续接收,需要跳出循环  
                  //        break;
                  //    }
                  //} while (m_buffer.Count > 4);
                  注意:你一定会问,这里为什么要用do-while循环?     
                  如果当服务端发送大数据流的时候,e.BytesTransferred的大小就会比服务端发送过来的完整包要小,    
                  需要分多次接收.所以收到包的时候,先判断包头的大小.够一个完整的包再处理.    
                  如果服务器短时间内发送多个小数据包时, 这里可能会一次性把他们全收了.    
                  这样如果没有一个循环来控制,那么只会处理第一个包,    
                  剩下的包全部留在m_buffer中了,只有等下一个数据包过来后,才会放出一个来.  
                  继续接收  
                  #endregion

                  if (!token.ReceiveAsync(e))
                      this.ProcessReceive(e);
              }
              else
              {
                  ProcessError(e); ///服务器断开
              }
          }
          catch (Exception xe)
          {
              Console.WriteLine(xe.Message);
          }
      }

      /// <summary>
      /// This method is invoked when an asynchronous send operation completes.  
      /// The method issues another receive on the socket to read any additional 
      /// The method issues another receive on the socket to read any additional   
      /// </summary>
      private void ProcessSend(SocketAsyncEventArgs e)
      {
          if (e.SocketError != SocketError.Success)
          {
              ProcessError(e);
          }
      }

      /// <summary>
      /// Close socket in case of failure and throws a SockeException according to the SocketError.  
      /// </summary>
      private void ProcessError(SocketAsyncEventArgs e)
      {
          Socket s = (Socket)e.UserToken;
          if (s.Connected)
          {
              // close the socket associated with the client  
              try
              {
                  s.Shutdown(SocketShutdown.Both);
              }
              catch (Exception ex)
              {
                  // throws if client process has already closed  
                  Console.WriteLine(ex.Message);
              }
              finally
              {
                  if (s.Connected)
                  {
                      s.Close();
                  }
                  connected = false;
              }
          }
          //这里一定要记得把事件移走,如果不移走,当断开服务器后再次连接上,会造成多次事件触发.  
          foreach (MySocketEventArgs arg in listArgs)
              arg.Completed -= IO_Completed;
          receiveEventArgs.Completed -= IO_Completed;

          if (ServerStopEvent != null)
              ServerStopEvent(); //服务器断开事件
      }

      /// <summary>
      /// Exchange a message with the host.  
      /// </summary>
      /// <param name="sendBuffer"></param>
      /// <exception cref="SocketException"></exception>
      internal void Send(byte[] sendBuffer)
      {
          if (connected)
          {
              //查找有没有空闲的发送MySocketEventArgs,有就直接拿来用,没有就创建新的
              MySocketEventArgs sendArgs = listArgs.Find(a => a.IsUsing == false);
              if (sendArgs == null)
              {
                  sendArgs = InitSendArgs();
              }
              lock (sendArgs) //要锁定,不让别的线程抢走
              {
                  sendArgs.IsUsing = true;
                  sendArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);
              }
              clientSocket.SendAsync(sendArgs);

              #region 以协议方式发送
              先对数据进行包装,就是把包的大小作为头加入,这必须与服务器端的协议保持一致,否则造成服务器无法处理数据.  
              //byte[] buff = new byte[sendBuffer.Length + 4];
              //Array.Copy(BitConverter.GetBytes(sendBuffer.Length), buff, 4);
              //Array.Copy(sendBuffer, 0, buff, 4, sendBuffer.Length);
              查找有没有空闲的发送MySocketEventArgs,有就直接拿来用,没有就创建新的.So easy!  
              //MySocketEventArgs sendArgs = listArgs.Find(a => a.IsUsing == false);
              //if (sendArgs == null)
              //{
              //    sendArgs = InitSendArgs();
              //}
              //lock (sendArgs) //要锁定,不锁定让别的线程抢走了就不妙了.  
              //{
              //    sendArgs.IsUsing = true;
              //    sendArgs.SetBuffer(buff, 0, buff.Length);
              //}
              //clientSocket.SendAsync(sendArgs);
              #endregion
          }
          else
          {
              throw new SocketException((int)SocketError.NotConnected);
          }
      }

      /// <summary>  
      /// 接收字节,使用新进程通知事件回调  
      /// </summary>  
      /// <param name="buff"></param>  
      private void DoReceiveEvent(byte[] buff)
      {
          if (ServerDataHandler == null) return;
          //ServerDataHandler(buff); //可直接调用  
          //线程不拖延接收新数据
          Thread thread = new Thread(new ParameterizedThreadStart((obj) =>
          {
              ServerDataHandler((byte[])obj);
          }));
          thread.IsBackground = true;
          thread.Start(buff);
      }
      #endregion

      #region IDisposable Members
      /// <summary>
      /// Disposes the instance of SocketClient.
      /// </summary>
      public void Dispose()
      {
          autoConnectEvent.Close();
          if (clientSocket.Connected)
          {
              clientSocket.Close();
          }
      }
      #endregion
  }

 internal class ClentManager
 {
     //定义,最好定义成静态的, 因为我们只需要一个就好  
     static Client client = null;

     //定义事件与委托  
     public delegate void ReceiveData(byte[] message);
     public delegate void ServerClosed();
     /// <summary>
     /// 接收数据委托
     /// </summary>
     public static event ReceiveData OnReceiveData;
     /// <summary>
     /// 客户端断开事件委托
     /// </summary>
     public static event ServerClosed OnServerClosed;

     /// <summary>
     /// 接收到客户端字符串委托
     /// </summary>
     public static Action<string> ReceiveClientStrData;

     /// <summary>  
     /// 心跳定时器  
     /// </summary>  
     static System.Timers.Timer heartTimer = null;

     /// <summary>  
     /// 判断是否已连接  
     /// </summary>  
     public static bool Connected
     {
         get { return client != null && client.Connected; }
     }

     #region 基本方法
     /// <summary>  
     /// 连接到服务器  
     /// </summary>  
     /// <returns></returns>  
     public static SocketError Connect(string ip, int port)
     {
         if (Connected) return SocketError.Success;
         if (string.IsNullOrWhiteSpace(ip) || port <= 1000) return SocketError.Fault;
         //创建连接对象, 连接到服务器  
         client = new Client(ip, port);
         SocketError socketError = TryConnect();
         if (socketError == SocketError.Success)
         {
             //连接成功后,就注册事件. 最好在成功后再注册.  
             client.ServerDataHandler += OnReceivedServerData;
             client.ReceiveClientStrData += OnReceiveClientStrData;
             client.ServerStopEvent += OnServerStopEvent;
         }
         return socketError;
     }

     /// <summary>  
     /// 断开连接  
     /// </summary>  
     public static void Disconnect()
     {
         try
         {
             client.Disconnect();
             if (heartTimer != null)
                 heartTimer = null;
         }
         catch (Exception)
         {
             Console.WriteLine("未能关闭socket连接");
         }
     }

     /// <summary>
     /// 尝试连接server,成功则返回true
     /// </summary>
     /// <returns></returns>
     public static SocketError TryConnect()
     {
         SocketError socketError = SocketError.ConnectionRefused;
         try
         {
             do
             {
                 socketError = client.Connect();
                 if (socketError == SocketError.Success)
                 {
                     //触发事件
                     break;
                 }
             } while (socketError != SocketError.Success);
         }
         catch (Exception ex)
         {
             Console.WriteLine($"TryConnect-->{ex.Message}");
         }
         return socketError;

     }

     /// <summary>  
     /// 发送消息  
     /// </summary>  
     /// <param name="message">消息实体</param>  
     /// <returns>True.已发送; False.未发送</returns>  
     public static bool Send(string message)
     {
         if (!Connected) return false;
         byte[] buff = Encoding.UTF8.GetBytes(message);
         //加密,根据自己的需要可以考虑把消息加密  
         //buff = AESEncrypt.Encrypt(buff, m_aesKey);  
         client.Send(buff);
         return true;
     }

     /// <summary>  
     /// 发送字节流  
     /// </summary>  
     /// <param name="buff"></param>  
     /// <returns></returns>  
     static bool Send(byte[] buff)
     {
         if (!Connected) return false;
         client.Send(buff);
         return true;
     }

     /// <summary>  
     /// 接收消息  
     /// </summary>  
     /// <param name="buff"></param>  
     private static void OnReceivedServerData(byte[] buff)
     {
         //你要处理的代码,可以实现把buff转化成你具体的对象, 再传给前台  
         if (OnReceiveData != null)
             OnReceiveData(buff);
     }

     /// <summary>
     /// 接收字符串消息
     /// </summary>
     /// <param name="str"></param>
     private static void OnReceiveClientStrData(string str)
     {
         if (ReceiveClientStrData != null)
             ReceiveClientStrData(str);
     }

     /// <summary>  
     /// 服务器已断开  
     /// </summary>  
     private static void OnServerStopEvent()
     {
         if (OnServerClosed != null)
             OnServerClosed();
     }

     #endregion

     #region 心跳包
     /// <summary>
     /// 启动心跳包发送心跳消息
     /// </summary>
     public static void StartHeartbeat()
     {
         if (heartTimer == null)
         {
             heartTimer = new System.Timers.Timer();
             heartTimer.Elapsed += TimeElapsed;
         }
         heartTimer.AutoReset = true;     //循环执行  
         heartTimer.Interval = 30 * 1000; //每30秒执行一次  
         heartTimer.Enabled = true;
         heartTimer.Start();
     }
     /// <summary>
     /// 
     /// </summary>
     /// <param name="sender"></param>
     /// <param name="e"></param>
     private static void TimeElapsed(object sender, System.Timers.ElapsedEventArgs e)
     {
         Send("pop");
     }
     #endregion

 }

参考:

Socket编程的四种通信方式_socket通信有哪几种-CSDN博客

C#socket通信(服务器端与客户端实现简单的通信)_c#利用socket实现客户端之间直接通信-CSDN博客

SocketAsyncEventArgs 类 (System.Net.Sockets) | Microsoft Learn

C# IOCP SocketAsyncEventArgs 使用流程-CSDN博客

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无熵~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值