C# Socket编程 通过线程方式的异步

文章目录

        • 前言
        • TCPSocket通信
          • 注意事项
        • UDPSocket通信

前言

  • 通过几个图可以很好的总结编程逻辑
    https://www.cnblogs.com/dream844/archive/2012/06/12/2546083.html
  • 这个博客总结的很好,但是有些内容我不是很理解,以后遇到再去研究好了
  • https://www.cnblogs.com/jikexianfeng/p/6413262.html 介绍什么是DMA
  • 异步是目的,为了避免某个耗时的操作阻塞当前主程序的运行。
  • 通过创建一个子线程去执行耗时操作,在操作完之后通知主程序
  • DMA功能可以不依赖CPU来进行读取到内存中,因此在读取完成后利用回调通知程序即可。这种方式没有创建线程

TCPSocket通信

C# Socket编程 通过线程方式的异步_第1张图片

  • Server端
    • 创建终端(指定ip和端口号)
    • 创建服务端socket 指定协议TCP, 数据为流方式,用IPV4,
    • socket绑定终端
    • 开始监听(此时会发生阻塞,如果没有监听到,就不会向下运行)
    • 接受到一个连接的请求时,会返回创建一个clientsock连接来处理与该客户端的通信,然后继续监听
    • 服务端代码
注意事项
  • 如果客户端断开连接,服务端会持续接收空字符串,因此在接收之前需要做判断
  • 还需要处理可能出现的异常,然后关闭连接,释放资源
  • 合理关闭连接的方式——先shutdown,再close 因为直接close会导致资源被直接释放,应该先不再接收,和不再发送,然后关闭
class Program
    {
        static List clientList = new List();
        static void Main(string[] args)
        {
            //创建服务器端的socket
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //绑定端口
            server.Bind(new IPEndPoint(IPAddress.Parse("192.168.0.106"), 8080));
            //开始监听
            server.Listen(100);
            Console.WriteLine("server is running");
            while (true)
            {
                Socket clientSockrt = server.Accept();
                Console.WriteLine("a client is connected");
                Client client = new Client(clientSockrt); //把与每个客户端通信的逻辑(封装到client类里面进行处理)
                clientList.Add(client);
            }
        }

        //广播消息
        public static void SendAllClient(byte[] data, int length)
        {
            var notConnectedList = new List();
            for (int i = 0; i < clientList.Count; i++)
            {
                if (clientList[i].Connected != false)
                {
                    clientList[i].SendMessageToClient(data, length);
                }
                else
                {
                    notConnectedList.Add(clientList[i]);
                }
            }

            foreach (var i in notConnectedList)
            {
                clientList.Remove(i);
            }
        }
    }

  • 处理与客户端通信的类即客服,
  /// 
    ///创建与客户端进行通信的类
    /// 
    class Client
    {
        private Socket clientSocket;
        private Thread t;
        private byte[] data = new byte[1024];
        private int length;

        public Client(Socket s)
        {
            clientSocket = s;
            //启动一个线程来处理客户端的数据接收
            t = new Thread(ReceiveMessage);
            t.Start();
        }

        //接收消息
        private void ReceiveMessage()
        {
            //一直接收客户端的数据
            while (true)
            {
                //在接受数据之前判断一下socket连接是否断开
                //判断是否响应 可以从客户端读取到东西
                //返回为true, 则是断开连接的意思
                if (clientSocket.Poll(10, SelectMode.SelectRead))
                {
                    clientSocket.Close();
                    break; //跳出循环,终止线程的执行
                }
                length = clientSocket.Receive(data);
                string message = Encoding.UTF8.GetString(data, 0, length);
                Console.WriteLine("从客户端发来一条消息:"+message);
                Program.SendAllClient(data, length);
            }
        }

        //向客户端发送数据的方法
        public void SendMessageToClient(byte[] data, int length)
        {
            clientSocket.Send(data, length, SocketFlags.None);
        }

        public bool Connected
        {
            get { return clientSocket.Connected; }
        }
            
    }
  • Client端

    • 创建客户端socket
    • 创建服务端终端的实例
    • 使用该socket向指定终端发起连接
    • 通过socket.send() 方法发送数据
    • 通过socket.receive()方法来接收数据
  • 这是一个网络聊天室的客户端

  • 由于unity不允许在单独一个线程里面操纵UI元素,所以需要额外创建一个变量message

public class ChatControl : MonoBehaviour {

    public Text content;
    public Text panelText;
    private Socket client;
    private byte[] buffer = new byte[1024];
    private string message;

	// Use this for initialization
	void Start () {
        content.text = "lallallalal";
        CreateConnection();
      
    }
	
	// Update is called once per frame
	void Update () {
        if (message!=null&&message!="")
        {
            ShowMessageInPanel(message);
            message = "";
        }
	}

    //创建连接与服务器
    private void CreateConnection()
    {
         client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //创建与服务端连接
        client.Connect(new IPEndPoint(IPAddress.Parse("192.168.0.106"), 8080));
        Thread t = new Thread(GetMessageFromServer);
        t.Start();
    }
    //发送消息给服务端
    private void SendMessageToServer(string message)
    {
        Debug.Log("message:" + message);
        byte[] data = Encoding.UTF8.GetBytes(message); //得到数据字节码
        client.Send(data);
        
    }

//从服务器得到数据
    private void GetMessageFromServer()
    {
        //创建缓冲数组
        while (true)
        {
            if (client.Connected == false)
            {
                break;
            }
            int n = client.Receive(buffer);
            string getMes = Encoding.UTF8.GetString(buffer, 0, n);
            message = getMes;//unity 并不允许在单独的线程里面操纵一个组件
        }
  }
  
    //当按钮被点击发送的时候
    public void OnSendSomething()
    {
        //获取content的内容
        string sm = content.text;
        //进行数据校验
        string valid = CheckMessageValid(sm);
        if (valid != "")
        {
            SendMessageToServer(valid);
            content.text = "";
        }          
    }

    //检测信息的有效性
    private string CheckMessageValid(string message)
    {
        string valid = message.TrimEnd();
        return valid;
    }

//将数据显示在面板UI上
    private void ShowMessageInPanel(string message)
    {
        panelText.text = panelText.text + "\n" + message;
    }

    private void OnDestroy()
    {
        //正确关闭socket连接的方法
        client.Shutdown(SocketShutdown.Both);
        client.Close();
    }
}

UDPSocket通信

C# Socket编程 通过线程方式的异步_第2张图片

  • Server端
    • 创建终端(指定ip和端口号)
    • 创建服务端socket 指定协议UDP, 数据为数字报方式,用IPV4,
    • socket绑定终端
    • 直接开始接受数据
  • Client端
    • 创建客户端socket
    • 创建服务端终端的实例
    • 直接向指定终端发送数据
  • 注意到UDP是没有连接的

你可能感兴趣的:(C#)