C#之Socekt(套接字)通讯(下)

异步Socket编程

1.所谓异步操作方式,就是我们希望让某个工作开始以后,能在这个工作尚未完成的时候继续处理其他工资。比如:客户端服务器端随时可以接收与发送信息,不需要等待。

2.Unity异步通讯聊天工具开发

1)服务器端开发

界面开发

使用Unity创建一个2D文件

将当前场景另存为Server,新建UI的Panel界面,其中包含四个Input Field控件,四个Text控件,一个Dropdown控件,三个Button控件,一个空物体控件,调整控件名称及属性位置,如下图所示

新建服务器端控制脚本ServerScripts,并将代码挂在空物体_ScriptsControl,为Btn_Exit按钮增加On Click(单击)事件,与脚本中的ExitSys()方法连接,为Btn_Start按钮增加On Click(单击)事件,与脚本中的EnableServerReady()方法连接,为Btn_Send按钮增加On Click(单击)事件,与脚本中的SendMsg()方法连接,为Dd_selectIPandPort下拉列表增加On Value Changed事件,与脚本中的GetCurrentSelectIpInfo()方法连接。

代码如下:

/***
 *      Title:Socket服务器端开发
 */
using System.Collections;
using System.Collections.Generic;                           //泛型空间引用
using UnityEngine;
using UnityEngine.UI;                                       //UI控件命名空间
using System;
using System.Text;
using System.Threading;                                     //多线程命名空间
using System.Net;                                           //网络命名空间
using System.Net.Sockets;                                   //Socket命名空间

public class ServerScripts : MonoBehaviour
{
    public InputField InpIPAddress;                         //IP地址
    public InputField InpPort;                              //端口号
    public InputField InpDisplayInfo;                       //显示信息
    public InputField InpSendMsg;                           //发送信息
    public Dropdown Drd_IPList;                             //客户端的IP列表(相当于QQ聊天中的“好友列表”)

    private Socket _SockServer;                             //服务端套接字
    private bool _IsListenContection = true;                //是否正在监听。
    private StringBuilder _SbDisplayInfo = new StringBuilder();//追加信息
    private string _CurrentClientIPValues = String.Empty;   //当前选择的IP地址信息
    //保存"客户端通讯的套接字"(相当于“QQ列表”)
    private Dictionary<string, Socket> _DicSocket = new Dictionary<string, Socket>();
    //保存Dropdown 中的数据,目的为了删除节点信息。
    private Dictionary<string, Dropdown.OptionData> _DicDropdown = new Dictionary<string, Dropdown.OptionData>();



    void Start()
    {
        //控件的初始化
        InpIPAddress.text = "127.0.0.1";
        InpPort.text = "2000";
        InpSendMsg.text = string.Empty;

        //下拉列表处理
        Drd_IPList.options.Clear();                         //清空列表信息
        //添加一个空节点
        Dropdown.OptionData op = new Dropdown.OptionData();
        op.text = "";
        Drd_IPList.options.Add(op);
    }

    /// <summary>
    /// 退出系统
    /// </summary>
    public void ExitSys()
    {
        //退出会话Socket


        //退出监听Socket
        if (_SockServer != null)
        {
            try
            {
                _SockServer.Shutdown(SocketShutdown.Both);      //关闭连接
            }
            catch
            { }
            _SockServer.Close();                                //清理资源
        }
        Application.Quit();
    }

    /// <summary>
    /// 获取“当前好友”。
    /// </summary>
    public void GetCurrentSelectIpInfo()
    {
        //获取当前玩家选择的客户端列表信息(“好友”)
        _CurrentClientIPValues = Drd_IPList.options[Drd_IPList.value].text;
    }

    /// <summary>
    /// 启动服务器端
    /// </summary>
    public void EnableServerReady()
    {
        //需要绑定的地址与端口号
        IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(InpIPAddress.text), Convert.ToInt32(InpPort.text));
        //定义监听Socket 
        _SockServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //绑定
        _SockServer.Bind(endPoint);

        //开始Sockect监听
        _SockServer.Listen(10);
        //显示内容(用控件显示)
        DisplayMsg("服务器端启动...");

        //开启后台线程(监听客户端连接)
        Thread thClinetCon = new Thread(ListenClientCon);
        thClinetCon.IsBackground = true;                    //后台线程
        thClinetCon.Name = "ThrListenClientCon";
        thClinetCon.Start();
    }

    /// <summary>
    /// (后台线程) 监听客户端连接
    /// </summary>
    private void ListenClientCon()
    {
        Socket sockMsgServer = null;                        //会话Socket 

        try
        {
            while (_IsListenContection)
            {
                //等待接收客户端连接,此处会“阻断”当前线程的运行
                sockMsgServer = _SockServer.Accept();             //监听客户端连接,返回会话Socket
                //获取远程(客户端)节点信息(IP,Port)
                string strClientIpAndPort = sockMsgServer.RemoteEndPoint.ToString();
                //把“远程节点”信息,添加到DropDown 控件中
                Dropdown.OptionData op = new Dropdown.OptionData();
                op.text = strClientIpAndPort;
                Drd_IPList.options.Add(op);
                //把会话Socket 添加到字典集合中(为了后面发送信息使用)
                _DicSocket.Add(strClientIpAndPort, sockMsgServer);

                //控件显示,有客户端连接
                DisplayMsg("有客户端连接");

                //开启后台线程,接收客户端会话信息。
                Thread thClientMsg = new Thread(ReceiveMsg);
                thClientMsg.IsBackground = true;
                thClientMsg.Name = "thListenClientMsg";
                thClientMsg.Start(sockMsgServer);
            }//While_end
        }
        catch (Exception)
        {
            _IsListenContection = false;
            //关闭会话Socket
            if (sockMsgServer != null)
            {
                sockMsgServer.Shutdown(SocketShutdown.Both);
                sockMsgServer.Close();
            }
            //关闭监听Socket
            if (_SockServer != null)
            {
                _SockServer.Shutdown(SocketShutdown.Both);
                _SockServer.Close();
            }
        }
    }//ListenClientCon_end

    /// <summary>
    /// (后台线程) 接收客户端会话
    /// </summary>
    /// <param name="sockMsg"></param>
    private void ReceiveMsg(object sockMsg)
    {
        Socket socketMsg = sockMsg as Socket;

        try
        {
            while (true)
            {
                //准备接收“数据缓存”
                byte[] msgArray = new byte[1024 * 1024];    //1M空间
                //接收客户端发来的套接字消息数据
                int trueClinetMsgLength = socketMsg.Receive(msgArray);
                //byte数组转字符串
                string strMsg = System.Text.Encoding.UTF8.GetString(msgArray, 0, trueClinetMsgLength);
                //显示接收的客户端消息内容
                DisplayMsg("客户端信息: " + strMsg);
            }
        }
        catch (Exception)
        {
        }
        finally
        {
            DisplayMsg("有客户端断开连接了:" + socketMsg.RemoteEndPoint.ToString());

            //字典类移除断开连接的客户端信息
            _DicSocket.Remove(socketMsg.RemoteEndPoint.ToString());
            //客户端列表中移除
            if (_DicDropdown.ContainsKey(socketMsg.RemoteEndPoint.ToString()))
            {
                Drd_IPList.options.Remove(_DicDropdown[socketMsg.RemoteEndPoint.ToString()]);
            }
            //关闭Socket
            socketMsg.Shutdown(SocketShutdown.Both);
            socketMsg.Close();
        }
    }


    /// <summary>
    /// 发送客户端会话
    /// </summary>
    public void SendMsg()
    {
        //参数检查
        _CurrentClientIPValues = _CurrentClientIPValues.Trim();
        if (string.IsNullOrEmpty(_CurrentClientIPValues))
        {
            DisplayMsg("请选择要聊天的用户名称");
            return;
        }

        //判断是否存在指定的客户端通信的Socket
        if (_DicSocket.ContainsKey(_CurrentClientIPValues))
        {
            //得到发送的信息
            string strSendMsg = InpSendMsg.text;
            strSendMsg = strSendMsg.Trim();

            if (!string.IsNullOrEmpty(strSendMsg))
            {
                //信息转码
                byte[] byMsgArray = System.Text.Encoding.UTF8.GetBytes(strSendMsg);
                //发送数据
                _DicSocket[_CurrentClientIPValues].Send(byMsgArray);
                //记录发送数据
                DisplayMsg("已发送:" + strSendMsg);

                //控件重置
                InpSendMsg.text = string.Empty;
            }
            else
            {
                DisplayMsg("发送的信息不能为空!");
            }
        }
        else
        {
            DisplayMsg("请选择合法的聊天用户!,请重新选择");
        }
    }


    /// <summary>
    /// 主显示控件,显示消息
    /// </summary>
    /// <param name="str">需要显示的消息</param>
    private void DisplayMsg(string str)
    {
        str = str.Trim();
        if (!string.IsNullOrEmpty(str))
        {
            _SbDisplayInfo.Append(System.DateTime.Now.ToString());
            _SbDisplayInfo.Append("  ");
            _SbDisplayInfo.Append(str);
            _SbDisplayInfo.Append("\r\n");
            InpDisplayInfo.text = _SbDisplayInfo.ToString();
        }
    }
}

2)客户端开发

界面开发

新建一个场景Client,新建UI的Panel界面,其中包含四个Input Field控件,四个Text控件,三个Button控件,一个空物体控件,调整控件名称及属性位置,如下图所示

新建服务器端控制脚本ClientScripts,并将代码挂在空物体_ScriptsControl,为Btn_Exit按钮增加On Click(单击)事件,与脚本中的ExitSystem()方法连接,为Btn_Start按钮增加On Click(单击)事件,与脚本中的EnableClientCon()方法连接,为Btn_Send按钮增加On Click(单击)事件,与脚本中的SendMsg()方法连接

新建客户端控制脚本ClientScripts,代码如下:

/***
 *      Title:Socket客户端开发
 */
using System.Collections;
using System.Collections.Generic;                           //泛型空间引用
using UnityEngine;
using UnityEngine.UI;                                       //UI控件命名空间
using System;
using System.Text;
using System.Threading;                                     //多线程命名空间
using System.Net;                                           //网络命名空间
using System.Net.Sockets;                                   //Socket命名空间

public class ClientScripts : MonoBehaviour
{
    public InputField InpIPAddress;                         //IP地址
    public InputField InpPort;                              //端口号
    public InputField InpDisplayInfo;                       //显示信息
    public InputField InpSendMsg;                           //发送信息

    private Socket _SockClient;                             //客户端Socket
    private IPEndPoint endPoint;
    private bool _isSendDataConnection = true;              //发送数据连接
    private StringBuilder _SbDisplayInfo = new StringBuilder();//控件追加信息

    void Start()
    {
        InpIPAddress.text = "127.0.0.1";
        InpPort.text = "2000";
    }

    /// <summary>
    /// 客户端退出系统
    /// </summary>
    public void ExitSystem()
    {
        //关闭客户端Socket 
        if (_SockClient != null)
        {
            try
            {
                //关闭连接
                _SockClient.Shutdown(SocketShutdown.Both);
            }
            catch
            { }
            //清理资源
            _SockClient.Close();
        }

        //退出
        Application.Quit();
    }

    /// <summary>
    /// 启动客户端连接
    /// </summary>
    public void EnableClientCon()
    {
        //通讯IP与端口号
        endPoint = new IPEndPoint(IPAddress.Parse(InpIPAddress.text), Convert.ToInt32(InpPort.text));
        //建立客户端Socket
        _SockClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        try
        {
            //建立连接
            _SockClient.Connect(endPoint);
            //启动线程,监听服务端返回的信息
            Thread thrListenMsgFromServer = new Thread(ListenMsgInfoFromServer);
            thrListenMsgFromServer.IsBackground = true;
            thrListenMsgFromServer.Name = "thrListenMsgFromServer";
            thrListenMsgFromServer.Start();
        }
        catch (Exception)
        {
        }
        //控件显示连接服务端成功。
        DisplayMsg("连接服务器成功!");
    }

    /// <summary>
    /// (后台线程) 监听服务端发来的信息
    /// </summary>
    private void ListenMsgInfoFromServer()
    {
        try
        {
            while (true)
            {
                //开辟消息内存区域
                byte[] byMsgArray = new byte[1024 * 1024];  //1M空间
                //客户端接收服务器返回的数据
                int intTrueMsgLengt = _SockClient.Receive(byMsgArray);
                //转字符串
                string strMsg = System.Text.Encoding.UTF8.GetString(byMsgArray, 0, intTrueMsgLengt);
                //显示收到的信息
                DisplayMsg("服务器返回信息:" + strMsg);
            }
        }
        catch
        {
        }
        finally
        {
            DisplayMsg("服务器断开连接了!");
            //关闭Socket 
            _SockClient.Disconnect(true);
            _SockClient.Close();
        }
    }

    /// <summary>
    /// 客户端发送数据到服务器端
    /// </summary>
    public void SendMsg()
    {
        string strSendMsg = InpSendMsg.text;
        if (!string.IsNullOrEmpty(strSendMsg))
        {
            //字节转换
            byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(strSendMsg);
            //发送
            _SockClient.Send(byteArray);
            //显示发送的内容
            DisplayMsg("我:" + strSendMsg);

            //控件清空
            InpSendMsg.text = string.Empty;
        }
        else
        {
            DisplayMsg("提示:发送的数据不能为空!,请输入发送信息");
        }
    }

    /// <summary>
    /// 主显示控件,显示消息
    /// </summary>
    /// <param name="str">需要显示的消息</param>
    private void DisplayMsg(string str)
    {
        str = str.Trim();
        if (!string.IsNullOrEmpty(str))
        {
            _SbDisplayInfo.Append(System.DateTime.Now.ToString());
            _SbDisplayInfo.Append("  ");
            _SbDisplayInfo.Append(str);
            _SbDisplayInfo.Append("\r\n");
            InpDisplayInfo.text = _SbDisplayInfo.ToString();
        }
    }
}

将两个场景用Unity中File-->Buid Setting分别打包,即可实现异步通讯交互

感言:

学习C#,至今为止就告一段落了,在学校的时候也学过C#,一直都在查缺补漏.目前的阶段是面临毕业的实习生,在一家使用Unity做XR的技术部Unity组中学习进步。接下来就会开始Unity的学习笔记的更新,一起努力进步,互相借鉴吧!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱卷的小Zang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值