网络通讯基础

Socket

在这里插入图片描述
Socket是应用层与TCP/IP协议簇通信的中间软件抽象层,它是一组接口。Socket通常用于实现客户端和服务器之间的通信。它允许客户端应用程序与服务器应用程序建立连接,并通过网络传输数据。

Socket包含了网络通讯必须的5种信息
Socket例子
{
协议: TCP/UDP
本地: IP、端口
远程: IP、 端口
}

Socket通信的基本步骤如下:

  1. 服务器创建一个Socket,并绑定到指定的IP地址和端口上。
  2. 客户端创建一个Socket,并连接到服务器的IP地址和端口上。
  3. 服务器接受客户端的连接请求,并建立连接。
  4. 客户端和服务器之间可以通过Socket发送和接收数据。
  5. 连接结束后,客户端和服务器都可以关闭Socket。

在这里插入图片描述

TCP

同步方法

依照上图建立客户端和服务端的连接并发送消息,这里用到的是同步方法,会阻塞

namespace Client
{
    class Program_Sync
    {
        static void Main(string[] args)
        {
            //创建客户端Socket
            //AddressFamily.InterNetwork:表示使用IPv4地址族。AddressFamily.InterNetworkV6:IPv6地址族
            //SocketType.Stream:表示使用流式套接字。流式套接字提供了可靠、面向连接的通信服务,数据是按照顺序传输的
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //连接服务器
            //IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            //IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);
            //clientSocket.Connect(ipEndPoint);
            clientSocket.Connect("127.0.0.1", 8888);

            //发送数据
            string input = Console.ReadLine();
            byte[] sendData = Encoding.UTF8.GetBytes(input);
            clientSocket.Send(sendData);

            //接收数据
            byte[] rece = new byte[1024];
            clientSocket.Receive(rece);
            string receiveStr = Encoding.UTF8.GetString(rece);
            Console.WriteLine("收到服务端消息: " + receiveStr);

            clientSocket.Close();
        }
    }
}
namespace Server
{
    class Program_Sync
    {
        static void Main(string[] args)
        {
            //创建服务端Socket
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //绑定端口
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);
            serverSocket.Bind(ipEndPoint);

            //监听端口
            //参数0表示操作系统会根据系统的设置和硬件资源等情况来决定等待队列的长度,当等待队列已满时,新的连接请求将被拒绝
            serverSocket.Listen(0);
            Console.WriteLine("服务端启动成功");

            //循环等待客户端连接
            //阻塞当前线程,直到有客户端连接到服务器,然后返回一个新的Socket对象,该Socket对象可以用于和客户端进行通信
            Socket connectSocket = serverSocket.Accept();
            Console.WriteLine("客户端连接成功, IP和端口: " + (IPEndPoint)connectSocket.RemoteEndPoint);

            //接收数据
            byte[] rece = new byte[1024];
            connectSocket.Receive(rece);
            string receiveStr = Encoding.UTF8.GetString(rece);
            Console.WriteLine("收到客户端消息: " + receiveStr);

            //发送数据
            string input = Console.ReadLine();
            byte[] sendData = Encoding.UTF8.GetBytes(input);
            connectSocket.Send(sendData);

            serverSocket.Close();
        }
    }
}
异步方法

增加了各种回调方法,代码更复杂些。当我们调用Socket的异步方法(如BeginConnect、BeginSend、BeginReceive等)时,底层会创建一个或多个线程来执行异步操作,容易造成线程问题。

namespace Client
{
    class Program_Async
    {
        private static byte[] _readBuffer = new byte[1024];

        static void Main(string[] args)
        {
            //创建客户端Socket
            //AddressFamily.InterNetwork:表示使用IPv4地址族。AddressFamily.InterNetworkV6:IPv6地址族
            //SocketType.Stream:表示使用流式套接字。流式套接字提供了可靠、面向连接的通信服务,数据是按照顺序传输的
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //异步连接服务器
            clientSocket.BeginConnect("127.0.0.1", 8888, ConnectCallback, clientSocket);

            Thread.Sleep(99999999);
        }

        /// <summary>
        /// 异步连接回调
        /// </summary>
        static void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                Socket clientSocket = (Socket)ar.AsyncState;
                //EndConnect方法只是完成异步连接操作,并返回连接结果,它并不会中断连接
                clientSocket.EndConnect(ar);
                Console.WriteLine("连接服务端成功");

                Send(clientSocket, "client");

                //异步接收数据
                clientSocket.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, ReceiveCallback, clientSocket);
            }
            catch(Exception e)
            {
                Console.WriteLine("连接失败");
            }
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        static void Send(Socket socket, string str)
        {
            byte[] sendData = Encoding.UTF8.GetBytes(str);
            socket.BeginSend(sendData, 0, sendData.Length, SocketFlags.None, SendCallback, socket);
        }

        /// <summary>
        /// 发送回调
        /// </summary>
        static void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket socket = (Socket)ar.AsyncState;
                //EndSend完成异步发送操作
                socket.EndSend(ar);
            }
            catch (Exception e)
            {
                Console.WriteLine("发送失败");
            }
        }

        /// <summary>
        /// 接收回调
        /// </summary>
        static void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                Socket socket = (Socket)ar.AsyncState;
                //EndReceive完成异步接收操作
                int receiveCount = socket.EndReceive(ar);
                if (receiveCount == 0)
                {
                    Console.WriteLine("服务端已断开");
                    socket.Close();
                }
                else
                {
                    string receiveStr = Encoding.UTF8.GetString(_readBuffer);
                    Console.WriteLine("收到客户端消息: " + receiveStr);

                    //继续接收
                    socket.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, ReceiveCallback, socket);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("接收失败");
            }
        }
    }
}
namespace Server
{
    public struct ClientData
    {
        public Socket socket;
        public byte[] readBuffer;
    }

    class Program_Async
    {
        private static Dictionary<Socket, ClientData> _clientDict = new Dictionary<Socket, ClientData>();

        static void Main(string[] args)
        {
            //创建服务端Socket
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //绑定端口
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);
            serverSocket.Bind(ipEndPoint);

            //监听端口
            //参数0表示操作系统会根据系统的设置和硬件资源等情况来决定等待队列的长度,当等待队列已满时,新的连接请求将被拒绝
            serverSocket.Listen(0);
            Console.WriteLine("服务端启动成功");

            //异步Accept
            serverSocket.BeginAccept(AcceptCallback, serverSocket);

            Thread.Sleep(99999999);
        }

        static void AcceptCallback(IAsyncResult ar)
        {
            try
            {
                Socket serverSocket = (Socket)ar.AsyncState;
                //EndSend完成异步发送操作
                Socket connectSocket = serverSocket.EndAccept(ar);
                Console.WriteLine("客户端连接成功, IP和端口: " + (IPEndPoint)connectSocket.RemoteEndPoint);
                ClientData data = new ClientData();
                data.socket = connectSocket;
                data.readBuffer = new byte[1024];
                _clientDict.Add(connectSocket, data);

                //接收其他客户端
                serverSocket.BeginAccept(AcceptCallback, serverSocket);

                //接收数据
                connectSocket.BeginReceive(data.readBuffer, 0, 1024, SocketFlags.None, ReceiveCallback, connectSocket);

                //发送数据
                Send(connectSocket, "111");
            }
            catch (Exception e)
            {
                Console.WriteLine("Accept失败");
            }
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        static void Send(Socket socket, string str)
        {
            byte[] sendData = Encoding.UTF8.GetBytes(str);
            socket.BeginSend(sendData, 0, sendData.Length, SocketFlags.None, SendCallback, socket);
        }

        /// <summary>
        /// 发送回调
        /// </summary>
        static void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket socket = (Socket)ar.AsyncState;
                //EndSend完成异步发送操作
                socket.EndSend(ar);
            }
            catch (Exception e)
            {
                Console.WriteLine("发送失败");
            }
        }

        /// <summary>
        /// 接收回调
        /// </summary>
        static void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                Socket socket = (Socket)ar.AsyncState;
                //EndReceive完成异步接收操作
                int receiveCount = socket.EndReceive(ar);
                //客户端调用Socket.Shutdown后receiveCount为0
                if (receiveCount == 0)
                {
                    Console.WriteLine("客户端关闭, IP和端口: " + (IPEndPoint)socket.RemoteEndPoint);
                    _clientDict.Remove(socket);
                    socket.Close();
                }
                else
                {
                    byte[] buffer = _clientDict[socket].readBuffer;
                    string receiveStr = Encoding.UTF8.GetString(buffer);
                    Console.WriteLine("收到客户端消息: " + receiveStr);

                    //继续接收
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, socket);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("接收失败");
            }
        }
    }
}
Poll方法改造同步方法

调用Socket.Poll(int microSeconds, SelectMode mode)方法时,当microSeconds参数为0,表示不会等待任何时间,立即返回。这个方法会立即检查Socket连接的读取状态,如果可以读取数据,就返回true;如果不能读取数据,就返回false。因此,从这个意义上说,这个方法是非阻塞的。因为要一直检测,所有性能消耗较大

namespace Client
{
    class Program_Poll
    {
        static void Main(string[] args)
        {
            //创建客户端Socket
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //连接服务器
            clientSocket.Connect("127.0.0.1", 8888);

            while (true)
            {
                //Poll方法用于轮询Socket连接状态,检查Socket连接是否处于可读、可写或异常状态,第一个参数是轮询的超时时间
                if (clientSocket.Poll(0, SelectMode.SelectRead))
                {
                    Receive(clientSocket);
                }
                if (clientSocket.Poll(0, SelectMode.SelectWrite))
                {
                    Send(clientSocket, "client1");
                }
                //避免CPU占用率过高
                Thread.Sleep(10);
            }
        }

        static void Send(Socket socket, string str)
        {
            byte[] sendData = Encoding.UTF8.GetBytes(str);
            socket.Send(sendData);
        }

        static void Receive(Socket socket)
        {
            byte[] rece = new byte[1024];
            socket.Receive(rece);
            string receiveStr = Encoding.UTF8.GetString(rece);
            Console.WriteLine("收到服务端消息: " + receiveStr);
        }
    }
}
namespace Server
{
    class Program_Poll
    {
        public struct ClientData
        {
            public Socket socket;
            public byte[] readBuffer;
        }

        private static Dictionary<Socket, ClientData> _clientDict = new Dictionary<Socket, ClientData>();
        static void Main(string[] args)
        {
            //创建服务端Socket
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //绑定端口
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);
            serverSocket.Bind(ipEndPoint);

            //监听端口
            //参数0表示操作系统会根据系统的设置和硬件资源等情况来决定等待队列的长度,当等待队列已满时,新的连接请求将被拒绝
            serverSocket.Listen(0);
            Console.WriteLine("服务端启动成功");

            while (true)
            {
                if(serverSocket.Poll(0, SelectMode.SelectRead))
                {
                    Accept(serverSocket);
                }

                foreach(var data in _clientDict.Values)
                {
                    if(data.socket.Poll(0, SelectMode.SelectRead))
                    {
                        Receive(data.socket);
                    }
                    if (data.socket.Poll(0, SelectMode.SelectWrite))
                    {
                        Send(data.socket, "222");
                    }
                }

                Thread.Sleep(10);
            }
        }

        static Socket Accept(Socket socket)
        {
            Socket connectSocket = socket.Accept();
            Console.WriteLine("客户端连接成功, IP和端口: " + (IPEndPoint)connectSocket.RemoteEndPoint);

            ClientData data = new ClientData();
            data.socket = connectSocket;
            data.readBuffer = new byte[1024];
            _clientDict.Add(connectSocket, data);

            return connectSocket;
        }

        static void Send(Socket socket, string str)
        {
            byte[] sendData = Encoding.UTF8.GetBytes(str);
            socket.Send(sendData);
        }

        static void Receive(Socket socket)
        {
            byte[] buffer = _clientDict[socket].readBuffer;
            socket.Receive(buffer);
            string receiveStr = Encoding.UTF8.GetString(buffer);
            Console.WriteLine("收到客户端消息: " + receiveStr);
        }
    }
}
Select方法

Select方法用于在多个Socket对象之间进行选择,以确定哪些Socket对象已经准备好进行I/O操作,前三个参数分别表示要检查的Socket对象的列表,最后一个参数表示等待的超时时间。Select方法会阻塞程序执行,直到有一个或多个Socket对象准备好进行I/O操作或超时。

namespace Client
{
    class Program_Select
    {
        private static List<Socket> _readCheckList = new List<Socket>();
        private static List<Socket> _writeCheckList = new List<Socket>();

        static void Main(string[] args)
        {
            //创建客户端Socket
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //连接服务器
            clientSocket.Connect("127.0.0.1", 8888);

            while (true)
            {
                _readCheckList.Clear();
                _writeCheckList.Clear();
                _readCheckList.Add(clientSocket);
                _writeCheckList.Add(clientSocket);
                Socket.Select(_readCheckList, _writeCheckList, null, 10);

                foreach(var item in _readCheckList)
                {
                    Receive(item);
                }

                foreach (var item in _writeCheckList)
                {
                    Send(item, "111");
                }
            }
        }

        static void Send(Socket socket, string str)
        {
            byte[] sendData = Encoding.UTF8.GetBytes(str);
            socket.Send(sendData);
        }

        static void Receive(Socket socket)
        {
            byte[] rece = new byte[1024];
            socket.Receive(rece);
            string receiveStr = Encoding.UTF8.GetString(rece);
            Console.WriteLine("收到服务端消息: " + receiveStr);
        }
    }
}
namespace Server
{
    class Program_Select
    {
        public struct ClientData
        {
            public Socket socket;
            public byte[] readBuffer;
        }

        private static Dictionary<Socket, ClientData> _clientDict = new Dictionary<Socket, ClientData>();
        private static List<Socket> _readCheckList = new List<Socket>();
        private static List<Socket> _writeCheckList = new List<Socket>();

        static void Main(string[] args)
        {
            //创建服务端Socket
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //绑定端口
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);
            serverSocket.Bind(ipEndPoint);

            //监听端口
            //参数0表示操作系统会根据系统的设置和硬件资源等情况来决定等待队列的长度,当等待队列已满时,新的连接请求将被拒绝
            serverSocket.Listen(0);
            Console.WriteLine("服务端启动成功");

            while (true)
            {
                _readCheckList.Clear();
                _writeCheckList.Clear();
                _readCheckList.Add(serverSocket);
                _writeCheckList.Add(serverSocket);
                foreach (var item in _clientDict.Keys)
                {
                    _readCheckList.Add(item);
                    _writeCheckList.Add(item);
                }

                Socket.Select(_readCheckList, _writeCheckList, null, 10);

                foreach (var item in _readCheckList)
                {
                    if(item == serverSocket)
                    {
                        Accept(item);
                    }
                    else
                    {
                        Receive(item);
                    }
                }

                foreach (var item in _writeCheckList)
                {
                    Send(item, "333");
                }
            }
        }

        static Socket Accept(Socket socket)
        {
            Socket connectSocket = socket.Accept();
            Console.WriteLine("客户端连接成功, IP和端口: " + (IPEndPoint)connectSocket.RemoteEndPoint);

            ClientData data = new ClientData();
            data.socket = connectSocket;
            data.readBuffer = new byte[1024];
            _clientDict.Add(connectSocket, data);

            return connectSocket;
        }

        static void Send(Socket socket, string str)
        {
            byte[] sendData = Encoding.UTF8.GetBytes(str);
            socket.Send(sendData);
        }

        static void Receive(Socket socket)
        {
            byte[] buffer = _clientDict[socket].readBuffer;
            socket.Receive(buffer);
            string receiveStr = Encoding.UTF8.GetString(buffer);
            Console.WriteLine("收到客户端消息: " + receiveStr);
        }
    }
}

UDP

同步方法
namespace Client
{
    class Program_UDPSync
    {
        static void Main(string[] args)
        {
            //创建客户端Socket,不需要连接
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            Send(clientSocket, "123");
        }

        static void Send(Socket socket, string str)
        {
            byte[] sendData = Encoding.UTF8.GetBytes(str);
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);
            //发送到对应的ip和端口
            socket.SendTo(sendData, ipEndPoint);
        }
    }
}
namespace Server
{
    class Program_UDP_Sync
    {
        static void Main(string[] args)
        {
            //UDP使用数据包
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            //只需要绑定,不需要监听
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888);
            serverSocket.Bind(ipEndPoint);
            Console.WriteLine("服务端启动成功");

            Receive(serverSocket);
        }

        static void Receive(Socket socket)
        {
            byte[] rece = new byte[1024];
            socket.Receive(rece);
            //ReceiveFrom只接收给定ip地址的数据
            //socket.ReceiveFrom();
            string receiveStr = Encoding.UTF8.GetString(rece);
            Console.WriteLine("收到客户端消息: " + receiveStr);
        }
    }
}

Http

Http协议是基于TCP之上的简单协议,以下是常用的GET和POST方法

namespace Client
{
    class Program_Http
    {
        static void Main(string[] args)
        {
            //Get();
            Post();
        }

        private static void Get()
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://www.metools.info");
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();
            string result = new StreamReader(responseStream).ReadToEnd();
            Console.WriteLine(result);
            responseStream.Close();
        }

        private static void Post()
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://coolaf.com/tool/params?r=rtest&t2=rtest2");
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            using (StreamWriter write = new StreamWriter(request.GetRequestStream(), Encoding.GetEncoding("UTF-8")))
            {
                write.Write("s=stest&i=itest2");
            }
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();
            string result = new StreamReader(responseStream).ReadToEnd();
            Console.WriteLine(result);
            responseStream.Close();
        }
    }
}

多线程

Thread 基础用法

public class ParaTest
{
    public string str1;
    public string str2;
    public ParaTest(string s1, string s2)
    {
        str1 = s1;
        str2 = s2;
    }

    public void Task()
    {
        Console.WriteLine(str1 + str2);
    }
}

class Program_Thread
{
    public static int _sum;
    public static object _locker = new object();
    

    static void Main(string[] args)
    {
        //默认为前台线程,主线程结束后还会运行
        Thread thread1 = new Thread(Task1);
        Thread thread2 = new Thread(Task2);
        Thread thread3 = new Thread(() => 
        {
            //传递任意类型参数
            Task3("111", 222);
        });

        ParaTest para = new ParaTest("333", "444");
        //通过实例传递参数
        Thread thread4 = new Thread(para.Task);

        Console.WriteLine(thread1.ThreadState + " --1");

        thread1.Start();
        Console.WriteLine(thread1.ThreadState + " --2");

        //设置优先级
        //thread1.Priority = ThreadPriority.AboveNormal;

        //改为后台线程,随着主线程结束而结束
        //thread1.IsBackground = true;

        //阻塞线程,直到thread1执行完毕
        //thread1.Join();

        //传递参数
        thread2.Start("ttt");

        //停止线程
        //thread1.Abort();

        while (true)
        {
            Console.WriteLine(_sum);
            Thread.Sleep(1);
        }
    }

    private static void Task1()
    {
        for(int i = 0; i < 100; ++i)
        {
            Console.WriteLine("111");
            //加锁,避免同时修改变量
            lock (_locker)
            {
                _sum++;
            }
        }
    }

    private static void Task2(object obj)
    {
        for (int i = 0; i < 100; ++i)
        {
            Console.WriteLine("222" + obj.ToString());
            lock (_locker)
            {
                _sum--;
            }
        }
    }

    private static void Task3(string str, int i)
    {
    }
}

使用多线程要注意避免死锁问题(两个线程互相持有对方需要的资源)

信号量与互斥量

信号量(Semaphore)是一种用于同步线程或进程之间共享资源访问的机制。它是一种计数器,用于控制对共享资源的访问权限。
信号量的基本操作有两种:P(等待)和 V(释放)

  1. P(等待)操作:当线程或进程需要访问共享资源时,它会执行P操作。如果信号量的值大于0,表示资源可用,线程或进程可以继续执行;如果信号量的值为0,表示资源已被占用,线程或进程将被阻塞,直到资源可用为止。
  2. V(释放)操作:当线程或进程完成对共享资源的访问时,它会执行V操作,将信号量的值加1。这样,其他等待该资源的线程或进程可以被唤醒并继续执行。

互斥量(Mutex)可以看作是信号量为1的特殊形式的Semaphore,只能由一个线程获取,其他线程需要等待该线程释放锁才能访问资源。

class Program_Semaphore
{
    //信号量,参数为初始值和最大值
    private static Semaphore _semaphore = new Semaphore(2, 2);
    //互斥量,类似于Semaphore(1, 1)
    private static Mutex _mutex = new Mutex();

    static void Main(string[] args)
    {
        Thread thread1 = new Thread(Task1);
        Thread thread2 = new Thread(Task2);
        Thread thread3 = new Thread(Task3);

        thread1.Start();
        thread2.Start();
        thread3.Start();
    }

    private static void Task1()
    {
        //等待
        _semaphore.WaitOne();
        Console.WriteLine("111");
        //释放
        _semaphore.Release();
    }

    private static void Task2()
    {
        _semaphore.WaitOne();
        _mutex.WaitOne();
        Console.WriteLine("222");
        _mutex.ReleaseMutex();
    }

    private static void Task3()
    {
        _semaphore.WaitOne();
        Console.WriteLine("333");
    }
}

优先级反转:是指在使用信号量时,可能会出现的这样一种不合理的现象,即:高优先级任务被低优先级任务阻塞(高优先级任务正等待信号量,此信号量被一个低优先级任务拥有着),导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上来看,好像是中优先级的任务比高优先级任务具有更高的优先权。

线程池

线程池(ThreadPool)提供了一种管理和重用多个工作线程的机制,可以更高效地管理线程资源。

static void Main(string[] args)
{
    //将工作项添加到线程池中进行异步执行
    ThreadPool.QueueUserWorkItem(Task1);
    ThreadPool.QueueUserWorkItem((satte)=> 
    {
        Task2();
    });
    ThreadPool.QueueUserWorkItem(Task3);

    //线程池中当前可用的工作线程数量和IO线程数量
    ThreadPool.GetAvailableThreads(out int workerThreads, out int completionPortThreads);
    Console.WriteLine("工作线程数:" + workerThreads + " IO线程数量:" + completionPortThreads);

    //获取线程池的最大工作线程数量和IO线程数量
    ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);
    Console.WriteLine("最大工作线程数:" + maxWorkerThreads + " 最大IO线程数量:" + maxCompletionPortThreads);

    Thread.Sleep(100000);
}

private static void Task1(object state)
{
    Console.WriteLine("111");
}

private static void Task2()
{
    Console.WriteLine("222");
}

private static void Task3(object state)
{
    Console.WriteLine("333");
}

Unity中的网络通讯

UnityWebRequest封装了C#提供的网络通讯功能,支持各种常见的网络协议

public class Test : MonoBehaviour
{
    private void Start()
    {
        //StartCoroutine(DownLoadText());
        //StartCoroutine(DownLoadText());
        //StartCoroutine(DownLoadAssetBundle());
        StartCoroutine(PostTest());
    }

    IEnumerator DownLoadText()
    {
        UnityWebRequest unityWebRequest = UnityWebRequest.Get("https://www.baidu.com");
        yield return unityWebRequest.SendWebRequest();

        if (unityWebRequest.result == UnityWebRequest.Result.Success)
        {
            //返回字符串数据
            Debug.Log(unityWebRequest.downloadHandler.text);
            //返回原始字节数组,适用于处理图像、音频、视频
            Debug.Log(unityWebRequest.downloadHandler.data);
        }
    }

    IEnumerator DownLoadTexture()
    {
        UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture("图片地址");
        yield return unityWebRequest.SendWebRequest();

        if (unityWebRequest.result == UnityWebRequest.Result.Success)
        {
            DownloadHandlerTexture downloadHandlerTexture = (DownloadHandlerTexture)unityWebRequest.downloadHandler;
            Debug.Log(downloadHandlerTexture.texture.width);
        }
    }

    IEnumerator DownLoadAssetBundle()
    {
        UnityWebRequest unityWebRequest = UnityWebRequestAssetBundle.GetAssetBundle("bundle地址");
        yield return unityWebRequest.SendWebRequest();

        if (unityWebRequest.result == UnityWebRequest.Result.Success)
        {
            AssetBundle assetBundle = ((DownloadHandlerAssetBundle)(unityWebRequest.downloadHandler)).assetBundle;
            //从bundle中加载资源
            GameObject go = assetBundle.LoadAsset<GameObject>("Cube");
            //实例化资源
            GameObject.Instantiate(go);
        }
    }

    IEnumerator PostTest()
    {
        //http不安全,默认不能使用,需要更改设置
        UnityWebRequest unityWebRequest = UnityWebRequest.PostWwwForm("http://coolaf.com/tool/params?r=rtest&t2=rtest2", "s=stest&i=itest2");
        yield return unityWebRequest.SendWebRequest();

        if (unityWebRequest.result == UnityWebRequest.Result.Success)
        {
            Debug.Log(unityWebRequest.downloadHandler.text);
        }
    }
}

在这里插入图片描述
出于安全考虑,unity默认不允许通过http下载,需要设置才能下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/136979.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机毕业设计 基于Vue篮球联盟管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

vue:实现顶部消息横向滚动通知

前言 最近有个需求&#xff0c;是在系统顶部展示一个横向滚动的消息通知。需求很简单&#xff0c;就是消息内容从右往左一直滚动。 效果如下&#xff1a; 因为我的需求很简单&#xff0c;功能就这样。如果有什么其他需求&#xff0c;可以再继续修改。 代码 使用 <noti…

《深入理解计算机系统》书籍学习笔记 - 第二课 - 位,字节和整型

Lecture 02 Bits,Bytes, and Integer 位&#xff0c;字节和整型 文章目录 Lecture 02 Bits,Bytes, and Integer 位&#xff0c;字节和整型Byte 字节位操作布尔代数集合的表现形式和操作C语言的逻辑操作 位移操作整型数值范围无符号与有符号数值无符号与有符号在C中 拓展和截断拓…

2023年【电工(初级)】考试内容及电工(初级)复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 电工&#xff08;初级&#xff09;考试内容是安全生产模拟考试一点通总题库中生成的一套电工&#xff08;初级&#xff09;复审模拟考试&#xff0c;安全生产模拟考试一点通上电工&#xff08;初级&#xff09;作业手…

C++语言的广泛应用领域

目录 1. 系统级编程 2. 游戏开发 3. 嵌入式系统 4. 大数据处理 5. 金融和量化分析 6. 人工智能和机器学习 7. 网络和通信 结语 C是一种多范式编程语言&#xff0c;具有高性能、中级抽象能力和面向对象的特性。由Bjarne Stroustrup于1979年首次设计并实现&#xff0c;C在…

电路综合-基于简化实频的SRFT集总参数切比雪夫低通滤波器设计

电路综合-基于简化实频的SRFT集总参数切比雪夫低通滤波器设计 6、电路综合-基于简化实频的SRFT微带线切比雪夫低通滤波器设计中介绍了使用微带线进行切比雪夫滤波器的设计方法&#xff0c;在此对集总参数的切比雪夫响应进行分析。 SRFT集总参数切比雪夫低通滤波器综合不再需要…

SpringBootWeb案例——Tlias智能学习辅助系统(3)——登录校验

前一节已经实现了部门管理、员工管理的基本功能。但并没有登录&#xff0c;就直接访问到了Tlias智能学习辅助系统的后台&#xff0c;这节来实现登录认证。 目录 登录功能登录校验(重点)会话技术会话跟踪方案一 Cookie&#xff08;客户端会话跟踪技术&#xff09;会话跟踪方案二…

Django(三、数据的增删改查、Django生命周期流程图)

文章目录 一、 基于ORM进行的CURDuser_list&#xff1a;作为主页使用路由文件urls.py配置如下&#xff1a;add.html&#xff1a;用于新增用户的数据页add页面视图函数如下:edit.html&#xff1a;修改数据的页面那么来总结一下上序所操作所用到的内容。 导入已存在的表其方式有两…

某手游完整性校验分析

前言 只是普通的单机手游&#xff0c;广告比较多&#xff0c;所以分析处理了下&#xff0c;校验流程蛮有意思的&#xff0c;所以就分享出来了 1.重打包崩溃处理 样本进行了加固&#xff0c;对其dump出dex后重打包出现崩溃 ida分析地址发现为jni函数引起 利用Xposed直接替换…

Java学习

Java的三大版本 Write Once、Run Anywhere JavaSE:标准版&#xff08;桌面程序,控制台开发…) JavaME:嵌入式开发(手机,小家电…) JavaEE: E企业级开发(web端&#xff0c;服务器开发…) JDK : Java Development Kit&#xff0c;Java开发者工具&#xff0c;在JRE之上扩充了一些…

算法进阶指南图论 通信线路

通信线路 思路&#xff1a;我们考虑需要升级的那条电缆的花费&#xff0c;若其花费为 w &#xff0c;那么从 1 到 n 的路径上&#xff0c;至多存在 k 条路径的价值大于 w &#xff0c;这具有一定的单调性&#xff0c;当花费 w 越大&#xff0c;我们路径上价值大于 w 的花费会越…

Unity 使用INI文件存储数据或配置参数预设

法1&#xff1a;调用外部Capi库 具体使用&#xff1a; public class Ini{//读取INI文件需要调用C的APP[System.Runtime.InteropServices.DllImport("kernel32")]private static extern long WritePrivateProfileString(string section, string key, string val, st…

STM32--系统滴答SysTick

一、SysTick是什么&#xff1f; Systick定时器是一个24bit的倒计时&#xff08;向下计数&#xff09;定时器&#xff0c;功能就是实现简单的延时。 SysTick 是一种系统定时器&#xff0c;通常在嵌入式系统中使用。它是 ARM Cortex-M 处理器的一个特殊定时器&#xff0c;用于提…

7.运算符

目录 一.算数运算符 1、算术运算符 2、比较运算符 1、等号()用来判断数字、字符串和表达式是否相等。 2、安全等于运算符(<>) 3、不等于运算符(<>或者!) 4、小于或等于运算符(<) 5、小于运算符(<) 6、IS NULL(IS NULL)&#xff0c;IS NOT NULL 运算…

[MySQL] MySQL表的基础操作

文章目录 一、创建表 1、1 SQL语法 1、2 实例演示 二、查询表 三、修改表 3、1 修改表名字 3、2 新增列&#xff08;字段&#xff09; 3、3 修改列类型 3、4 修改列名 3、5 删除表 四、总结 &#x1f64b;‍♂️ 作者&#xff1a;Ggggggtm &#x1f64b;‍♂️ &#x1f440; 专…

【MySQL日志与备份篇】数据库备份与恢复

数据库备份与恢复 文章目录 数据库备份与恢复1. 物理备份与逻辑备份2. mysqldump实现逻辑备份2.1 备份一个数据库2.2 备份全部数据库2.3 备份部分数据库2.4 备份部分表2.5 备份单表的部分数据2.6 排除某些表的备份2.7 只备份结构或只备份数据2.8 备份中包含存储过程、函数、事件…

微信聊天,收到二维码图片就自动帮你提取出来的方法

10-3 如果你是二维码收集的重度用户&#xff0c;那我非常推荐你好好阅读本文&#xff0c;也许可以帮你解决你的问题&#xff0c;比如做网推的人&#xff0c;需要常年混迹在各种微信群&#xff0c;那如何在各个微信群中收集到群友分享出来的二维码&#xff0c;并且要立即保存出…

吃透 Spring 系列—MVC部分

目录 ◆ SpringMVC简介 - SpringMVC概述 - SpringMVC快速入门 - Controller中访问容器中的Bean - SpringMVC关键组件浅析 ◆ SpringMVC的请求处理 - 请求映射路径的配置 - 请求数据的接收 - Javaweb常用对象获取 - 请求静态资源 - 注解驱动 标签 ◆ SpringMV…

推荐系统笔记--Swing模型的原理

1--Swing模型的引入 在 Item CF 召回中&#xff0c;物品的相似度是基于其受众的交集来衡量的&#xff0c;但当受众的交集局限在一个小圈子时&#xff0c;就会误将两个不相似的物品定义为相似&#xff1b; Swing 模型引入用户的重合度来判断两个用户是否属于一个小圈子&#xff…

C++基础(2)——类和对象

目录 1. 类的引入&#xff1a; 2. 类的定义&#xff1a; 2.1类的定义以及基本结构&#xff1a; 2.2 类的访问限定符&#xff1a; 3. 类的声明与定义的分离&#xff1a; 4. 类的实例化&#xff1a; 5. 类的大小计算&#xff1a; 1. 类的引入&#xff1a; 在数据结构系列的…