网络通讯基础

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

猜你喜欢

转载自blog.csdn.net/sinat_34014668/article/details/133967945