29.TCP异步服务端基础实现

29.网络通信-套接字Socket-TCP通信-异步-服务端


29.1 知识点

实现服务端ClientSocket类

创建ClientSocket类,定义和同步ClientSocket类类似的成员变量

private static int CLIENT_BEGIN_ID = 1;  // 静态变量,用于为客户端分配唯一的客户端ID
public int clientID;  // 客户端的唯一ID
public Socket clientSocket;  // 与客户端通信的套接字对象

//用于处理分包时 缓存的 字节数组 和 字节数组长度
private byte[] cacheBytes = new byte[1024 * 1024];
private int cacheNum = 0;

/// <summary>
/// 是否是连接状态
/// </summary>
public bool isClientConnected => this.clientSocket.Connected;  // 判断套接字是否处于连接状态

在构造函数中开启接收来自客户端的消息。添加接收客户端消息回调函数,向客户端发消息和向客户端发消息回调函数,暂时模拟客户端字符串的收发。

public ClientSocket(Socket clientSocket)
{
    this.clientID = CLIENT_BEGIN_ID;  // 初始化客户端ID
    this.clientSocket = clientSocket;  // 初始化套接字
    ++CLIENT_BEGIN_ID;  // 为下一个客户端分配不同的ID

    //开始收消息
    this.clientSocket.BeginReceive(cacheBytes, cacheNum, cacheBytes.Length, SocketFlags.None, ReceiveCallBack, null);
}

//接收客户端消息回调函数
private void ReceiveCallBack(IAsyncResult asyncResult)
{
    try
    {
        cacheNum = this.clientSocket.EndReceive(asyncResult);
        //通过字符串去解析
        Console.WriteLine(Encoding.UTF8.GetString(cacheBytes, 0, cacheNum));
        //如果是连接状态再继续收消息
        //因为目前我们是以字符串的形式解析的 所以 解析完 就直接 从0又开始收
        cacheNum = 0;
        if (isClientConnected)
            this.clientSocket.BeginReceive(cacheBytes, cacheNum, cacheBytes.Length, SocketFlags.None, ReceiveCallBack, this.clientSocket);
        else
        {
            Console.WriteLine("没有连接,不用再收消息了");
        }
    }
    catch (SocketException e)
    {
        Console.WriteLine("接受消息错误" + e.SocketErrorCode + e.Message);
    }
}

//向客户端发消息
public void Send(string str)
{
    if (isClientConnected)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(str);
        this.clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallBack, null);
    }
    else
    {

    }
}

//向客户端发消息回调函数
private void SendCallBack(IAsyncResult asyncResult)
{
    try
    {
        this.clientSocket.EndSend(asyncResult);
    }
    catch (SocketException e)
    {
        Console.WriteLine("发送失败" + e.SocketErrorCode + e.Message);
    }
}

实现客户端ServerSocket类

创建ServerSocket类,定义和同步ServerSocket类类似的成员变量

// 服务器端Socket
public Socket serverSocket;
// 保存客户端连接的所有Socket的字典
public Dictionary<int, ClientSocket> clientSocketDictionary = new Dictionary<int, ClientSocket>();

定义开启服务端函数,服务端接收客户端连接回调函数,广播函数。

// 开启服务器端
public void Start(string ipString, int port, int clientSocketMaxNum)
{
    // 创建服务器套接字,指定地址族为IPv4、套接字类型为流套接字、协议类型为TCP
    serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    // 创建IP终结点,指定IP地址和端口号
    IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(ipString), port);

    try
    {
        // 将套接字绑定到指定的IP终结点
        serverSocket.Bind(serverEndPoint);

        // 启动服务器套接字,同时指定同时等待连接的最大客户端数
        serverSocket.Listen(clientSocketMaxNum);

        //通过异步接受客户端连入
        serverSocket.BeginAccept(AcceptCallBack, null);
    }
    catch (Exception e)
    {
        Console.WriteLine("启动服务器失败" + e.Message);
    }
}

//服务端接收客户端连接回调函数
private void AcceptCallBack(IAsyncResult asyncResult)
{
    try
    {
        //获取连入的客户端
        Socket clientSocket = serverSocket.EndAccept(asyncResult);
        ClientSocket client = new ClientSocket(clientSocket);

        //记录客户端对象
        clientSocketDictionary.Add(client.clientID, client);

        //继续去让别的客户端可以连入
        serverSocket.BeginAccept(AcceptCallBack, null);
    }
    catch (Exception e)
    {
        Console.WriteLine("客户端连入失败" + e.Message);
    }
}

// 向所有客户端广播消息
public void Broadcast(string str)
{
    foreach (ClientSocket client in clientSocketDictionary.Values)
    {
        client.Send(str);
    }
}

服务端主入口

新建服务端类并开启,死循环接收打印信息

ServerSocket serverSocket = new ServerSocket();
serverSocket.Start("127.0.0.1", 8080, 1024);
Console.WriteLine("开启服务器成功");

while (true)
{
    string input = Console.ReadLine();
    if (input.Substring(0, 2) == "B:")
    {
        serverSocket.Broadcast(input.Substring(2));
    }
}

29.2 知识点代码

ClientSocket

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

namespace Lesson29_网络通信_套接字Socket_TCP通信_异步_服务端
{
    class ClientSocket
    {
        private static int CLIENT_BEGIN_ID = 1;  // 静态变量,用于为客户端分配唯一的客户端ID
        public int clientID;  // 客户端的唯一ID
        public Socket clientSocket;  // 与客户端通信的套接字对象

        //用于处理分包时 缓存的 字节数组 和 字节数组长度
        private byte[] cacheBytes = new byte[1024 * 1024];
        private int cacheNum = 0;

        /// <summary>
        /// 是否是连接状态
        /// </summary>
        public bool isClientConnected => this.clientSocket.Connected;  // 判断套接字是否处于连接状态

        public ClientSocket(Socket clientSocket)
        {
            this.clientID = CLIENT_BEGIN_ID;  // 初始化客户端ID
            this.clientSocket = clientSocket;  // 初始化套接字
            ++CLIENT_BEGIN_ID;  // 为下一个客户端分配不同的ID

            //开始收消息
            this.clientSocket.BeginReceive(cacheBytes, cacheNum, cacheBytes.Length, SocketFlags.None, ReceiveCallBack, null);
        }

        //接收客户端消息回调函数
        private void ReceiveCallBack(IAsyncResult asyncResult)
        {
            try
            {
                cacheNum = this.clientSocket.EndReceive(asyncResult);
                //通过字符串去解析
                Console.WriteLine(Encoding.UTF8.GetString(cacheBytes, 0, cacheNum));
                //如果是连接状态再继续收消息
                //因为目前我们是以字符串的形式解析的 所以 解析完 就直接 从0又开始收
                cacheNum = 0;
                if (isClientConnected)
                    this.clientSocket.BeginReceive(cacheBytes, cacheNum, cacheBytes.Length, SocketFlags.None, ReceiveCallBack, this.clientSocket);
                else
                {
                    Console.WriteLine("没有连接,不用再收消息了");
                }
            }
            catch (SocketException e)
            {
                Console.WriteLine("接受消息错误" + e.SocketErrorCode + e.Message);
            }
        }

        //向客户端发消息
        public void Send(string str)
        {
            if (isClientConnected)
            {
                byte[] bytes = Encoding.UTF8.GetBytes(str);
                this.clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallBack, null);
            }
            else
            {

            }
        }

        //向客户端发消息回调函数
        private void SendCallBack(IAsyncResult asyncResult)
        {
            try
            {
                this.clientSocket.EndSend(asyncResult);
            }
            catch (SocketException e)
            {
                Console.WriteLine("发送失败" + e.SocketErrorCode + e.Message);
            }
        }
    }
}

ServerSocket

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

namespace Lesson29_网络通信_套接字Socket_TCP通信_异步_服务端
{
    class ServerSocket
    {
        // 服务器端Socket
        public Socket serverSocket;
        // 保存客户端连接的所有Socket的字典
        public Dictionary<int, ClientSocket> clientSocketDictionary = new Dictionary<int, ClientSocket>();

        // 开启服务器端
        public void Start(string ipString, int port, int clientSocketMaxNum)
        {
            // 创建服务器套接字,指定地址族为IPv4、套接字类型为流套接字、协议类型为TCP
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // 创建IP终结点,指定IP地址和端口号
            IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(ipString), port);

            try
            {
                // 将套接字绑定到指定的IP终结点
                serverSocket.Bind(serverEndPoint);

                // 启动服务器套接字,同时指定同时等待连接的最大客户端数
                serverSocket.Listen(clientSocketMaxNum);

                //通过异步接受客户端连入
                serverSocket.BeginAccept(AcceptCallBack, null);
            }
            catch (Exception e)
            {
                Console.WriteLine("启动服务器失败" + e.Message);
            }
        }

        //服务端接收客户端连接回调函数
        private void AcceptCallBack(IAsyncResult asyncResult)
        {
            try
            {
                //获取连入的客户端
                Socket clientSocket = serverSocket.EndAccept(asyncResult);
                ClientSocket client = new ClientSocket(clientSocket);

                //记录客户端对象
                clientSocketDictionary.Add(client.clientID, client);

                //继续去让别的客户端可以连入
                serverSocket.BeginAccept(AcceptCallBack, null);
            }
            catch (Exception e)
            {
                Console.WriteLine("客户端连入失败" + e.Message);
            }
        }

        // 向所有客户端广播消息
        public void Broadcast(string str)
        {
            foreach (ClientSocket client in clientSocketDictionary.Values)
            {
                client.Send(str);
            }
        }
    }
}

Lesson29_网络通信_套接字Socket_TCP通信_异步_服务端

namespace Lesson29_网络通信_套接字Socket_TCP通信_异步_服务端
{
    internal class Program
    {
        static void Main(string[] args)
        {
            ServerSocket serverSocket = new ServerSocket();
            serverSocket.Start("127.0.0.1", 8080, 1024);
            Console.WriteLine("开启服务器成功");

            while (true)
            {
                string input = Console.ReadLine();
                if (input.Substring(0, 2) == "B:")
                {
                    serverSocket.Broadcast(input.Substring(2));
                }
            }
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏