网络通讯基础

news2024/11/17 1:38:34

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下载,需要设置才能下载

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【0基础学Java第九课】-- 抽象类和接口

9. 抽象类和接口 9.1 抽象类9.1.1 抽象类概念9.1.2 抽象类语法9.1.3 抽象类的特性9.1.4 抽象类的作用 9.2 接口9.2.1 接口的概念9.2.2 语法规则9.2.3 接口使用9.2.4 接口特性9.2.5 实现多个接口9.2.6 接口的继承9.2.9 抽象类和接口的区别 9.3 Object类9.3.1 获取对象方法9.3.1 …

【论文精读】DMVSNet

今天读的是一篇发表在ICCV 2023上的文章&#xff0c;作者来自华中科技大学。 文章地址&#xff1a;点击前往 项目地址&#xff1a;Github 文章目录 Abstract1 Introduction2 Relative Work3 Motivation3.1 Estimated bias and interpolated bias3.2 One-sided V.S. Saddle-shap…

Leetcode154. Find Minimum in Rotated Sorted Array II

旋转数组找最小&#xff0c;这次值可以重复 不妨假设你已经做了上一题&#xff0c;题解 上一题的方法1肯定是用不了了&#xff0c;因为不再能完全分成2个不同的部分 所以我们沿着方法2走 如果 > n u m s [ r ] >nums[r] >nums[r]&#xff0c;我们依然可以找右半边 …

SpringBoot 自动配置原理

SpringBoot 自动配置原理 注: 本文使用的springboot版本为 2.7.11 1、SpringBootApplication 字面分析&#xff0c;这个注解是标注一个Spring Boot应用。 Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented Inherited SpringBootConfiguration En…

电路设计之36V 自动断电和防浪涌电路

1. 电路图纸 2. 解释防浪涌功能怎么实现的 1. 首先当电源上电的一瞬间是 电容C1 是相当于短路的。 &#xff08;电容的充电状态。电容充电相当于短路状态&#xff09; 2. 当上电的一瞬间是有 浪涌的。 3.当上电的瞬间有浪涌的&#xff0c;此时电容C1 相当于短路&#xff0c;所…

第一百七十一回 SearchBar组件

文章目录 1. 概念介绍2. 使用方法3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了"Material3中的IconButton"相关的内容&#xff0c;本章回中将 介绍SearchBar组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在…

自定义类型:联合和枚举

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 联合体 1.1 联合体类型的声明 1.2 联合体的特点 1.3 相同成员的结构体和联合体对比 1.4 联合体大小的计算 1.5 联合的一个练习 2. 枚举类型 2.1 枚举类型的声明…

MSF图形化工具Viper快速安装

简介 Viper(炫彩蛇)是一款图形化内网渗透工具,将内网渗透过程中常用的战术及技术进行模块化及武器化. Viper(炫彩蛇)集成杀软绕过,内网隧道,文件管理,命令行等基础功能. Viper(炫彩蛇)当前已集成70个模块,覆盖初始访问/持久化/权限提升/防御绕过/凭证访问/信息收集/横向移动等…

链表OJ题(4)

目录 10.链表的回文结构 11.随机链表的复制 &#x1f642;找中间节点一定要考虑偶数个和奇数个的问题。 &#x1f642;指针指向的前中后。 &#x1f642;链表节点的位置个数/链表的节点中的数字。&#x1f197;&#x1f197; 今天最后两道链表OJ题目。 10.链表的回文结构…

事件循环Eventloop

事件循环 浏览器的进程模型 何为进程&#xff1f; 程序运行需要有它自己专属的内存空间&#xff0c;可以把这块内存空间简单的理解为进程 每个应用至少有一个进程&#xff0c;进程之间相互独立&#xff0c;即使要通信&#xff0c;也需要双方同意。 何为线程&#xff1f; 有…

AIGC专栏8——EasyPhoto 视频领域拓展-让AIGC肖像动起来

AIGC专栏8——EasyPhoto 视频领域初拓展-让AIGC肖像动起来 学习前言源码下载地址技术原理储备Video Inference 功能说明 & 效果展示1、Text2Video功能说明a、实现原理简介b、文到视频UI介绍c、结果展示 2、Image2Video功能说明a、实现原理简介i、单图模式ii、首尾图模式 b、…

KDE Plasma 6 将不支持较旧的桌面小部件

KDE Plasma 6 进行了一些修改&#xff0c;需要小部件作者进行调整。开发人员&#xff0c;移植时间到了&#xff01; KDE Plasma 6 是备受期待的桌面环境版本升级版本。 最近&#xff0c;其发布时间表公布&#xff0c;第一个 Alpha 版本将于 2023 年 11 月 8 日上线&#xff0…

Vue3+NodeJS 接入文心一言, 发布一个 VSCode 大模型问答插件

目录 一&#xff1a;首先明确插件开发方式 二&#xff1a;新建一个Vscode 插件项目 1. 官网教程地址 2. 一步一步来创建 3. 分析目录结构以及运行插件 三&#xff1a;新建一个Vue3 项目&#xff0c;在侧边栏中展示&#xff0c;实现vscode插件 <> vue项目 双向消息传…

Nuxt.js——基于 Vue 的服务端渲染应用框架

文章目录 前言一、知识普及什么是服务端渲染什么是客户端渲染&#xff1f;服务端渲染与客户端渲染那个更优秀&#xff1f; 二、Nuxt.js的特点Nuxt.js的适用情况&#xff1f; 三、Vue是如何实现服务端渲染的&#xff1f;安装依赖使用vue安装 Nuxt使用npm install安装依赖包使用n…

配置云服务器

一、概念 现如今越来越多的企业或者个人开发者都会选择去购买一台云服务器&#xff0c;云服务器相比较与传统的物理服务器他的价格优势&#xff0c;以及一系列可客制化的服务也方便大家去选择&#xff0c;大大节约了空间&#xff0c;运维&#xff0c;开发等等一系列成本。现如…

Python---字符串 lstrip()--删除字符串两边的空白字符、rstrip()--删除字符串左边的空白字符、strip()--删除字符串右边的空白字符

strip() 方法主要作用&#xff1a;删除字符串两边的空白字符&#xff08;如空格&#xff09; lstrip() 方法 left strip&#xff0c;作用&#xff1a;只删除字符串左边的空白字符 left 英 /left/ 左 rstrip() 方法 right strip&#xff0c;作用&#xff1a;只删除字符…

ELK之Logstash解析时间相差8h的问题

一、问题描述 服务器当前时间为&#xff1a;2022年 06月 28日 星期二 11:24:22 CST 而logstash解析的时间为2022-06-28T03:15:25.545Z与实际时间相差8h 一、解决办法&#xff1a; 需改logstash的配置文件&#xff1a; 原理就是&#xff1a;定义一个中间变量timestamp&…

【Java 进阶篇】Java与JQuery:探秘事件绑定、入口函数与样式控制

在现代的Web开发中&#xff0c;Java和JQuery是两个不可或缺的角色。Java为我们提供了强大的后端支持&#xff0c;而JQuery则是前端开发的得力助手。本篇博客将围绕Java和JQuery&#xff0c;深入探讨事件绑定、入口函数和样式控制&#xff0c;带你进入前端开发的奇妙世界。 Jav…

小程序中如何设置门店信息

小程序是商家转型升级的利器&#xff0c;小程序中门店信息的准确性和完整性对于用户的体验和信任度都有很大的影响。下面具体介绍门店信息怎么在小程序中进行设置。 在小程序管理员后台->门店设置处&#xff0c;可以门店设置相关。主要分为2个模块&#xff0c;一个是门店级…

基于springboot实现驾校管理系统项目【项目源码】计算机毕业设计

基于springboot实现驾校管理系统演示 JAVA简介 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&#xff0…