c#网络通讯Select多路复用——小白笔记#8

一、关于Socket网络通讯,如果我们要同时监听多个客户端进行接收和发送消息时,直接使用Socket通讯的话就可能会出现其中一个Socket接收不到客户端发来的消息而阻塞而导致其他Socket阻塞,这就需要启用多个线程来监听每一个Socket,这样就会占用很多资源。为了解决这种问题,我们就要用到Select多路复用了,它能够同时监听多个Socket,如果一个Socket阻塞,其他Socket也能够正常运行,从而可以在一个线程中监听所有的Socket。

二、Select在C#中的定义:

public static void Select(checkRead, checkWrite, checkError, microSeconds);

其中checkPead(检查可读Socket),checkWrite(检查可写Socket),checkError (检查异常Socket)都是List类型的,可以存放多个Socket。

microSeconds表示为等待时间。

为null时:一直阻塞直到前面三个发生变化时打开。

为0时:不进行阻塞一直处于到打开状态。

为自定义值时:等待自定义值过后打开。

三、Select的用法 

这里只演示Select在服务端监听Socket的用法。

服务器演示代码:

using System.Net;
using System.Net.Sockets;
using System.Text;

namespace UnityServer
{
    internal class Program
    {
        //创建字典用于存储负责与客户端通讯的Socket;
        static Dictionary<Socket,ClientDate> dicCliSocket = new Dictionary<Socket,ClientDate>();
        static void Main(string[] args)
        {    
            //创建服务器负责监听的Socket
            Socket serSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //绑定IP地址和端口号
            IPEndPoint Point = new IPEndPoint(IPAddress.Parse("192.168.1.217"), 55555);
            //绑定
            serSocket.Bind(Point);
            //设置监听数量  0为无限个
            serSocket.Listen(0);
            Console.WriteLine("服务器创建成功");
            //创建list列表用于存储可读Socket
            List<Socket> cliSocketRead = new List<Socket>();
            //死循环实时监听客户端发送数据进行处理
            while (true)
            {
                //清空可读列表
                cliSocketRead.Clear();
                //将服务器Socket添加到列表中用于监听客户端Socket
                cliSocketRead.Add(serSocket);
                //将字典中可读的Socket添加的list列表中
                foreach (ClientDate date in dicCliSocket.Values)
                {
                    cliSocketRead.Add(date.socket);
                }
                //创建Select用于监听可读列表中的Socket
                Socket.Select(cliSocketRead, null, null, 10000);
                //遍历可读列表中的Socket
                foreach(Socket socket in cliSocketRead)
                {
                    //如果列表中的Socket是服务器中的Scoket,则接收客户端Socket
                    if(socket == serSocket)
                    {
                        AcceptClientScoket(socket);
                    }
                    //如果列表中的Socket是服务器与客户端通讯的Socket,则接收数据并处理
                    else
                    {
                        SendMessage(socket);
                    }
                }
            }
        }
        private static void SendMessage(Socket cliSocket)
        {
            //将客户端与服务器通讯的Socket进行存储
            ClientDate date = dicCliSocket[cliSocket];
            try
            {
                //接收客户端发来的数据
                int count = date.socket.Receive(date.readBuff);
            }
            //客户端断开连接
            catch
            {        
                //关闭服务器关闭与客户端通讯的Socket
                cliSocket.Close();
                //可读列表移除与客户端通讯的Socket
                dicCliSocket.Remove(cliSocket);
                Console.WriteLine("客户端断开连接");
            }
            //将接收到的数据进行转换
            string receiveMessage = Encoding.UTF8.GetString(date.readBuff);
            //服务器提示收到的客户端数据
            Console.WriteLine("客户端:" + receiveMessage);
            //将收到的客户端数据处理(转换)
            byte[] buffer = Encoding.UTF8.GetBytes(receiveMessage);
            //通过可读列表中的所有Socket向客户端发送数据
            foreach (ClientDate clientDate in dicCliSocket.Values)
            {
                clientDate.socket.Send(buffer);
            }
        }
        private static void AcceptClientScoket(Socket serSocket)
        {
            //向连接服务器的客户端分配负责通讯的Socket
            Socket cliSocket = serSocket.Accept();
            //服务器提示客户端链接服务器
            Console.WriteLine(cliSocket.RemoteEndPoint.ToString() + "连接服务器");
            //将服务器与客户端通讯的Socket放到字典中
            ClientDate date = new ClientDate();
            date.socket = cliSocket;
            dicCliSocket.Add(cliSocket, date);
        }
        //创建类存储用户数据
        public class ClientDate
        {
            public Socket socket;
            public byte[] readBuff = new byte[1024];
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值