【Uinty】Socket聊天系统案例(妈宝级教程)

前言:很多人疑惑于Unity的网络游戏咋搞,从今起,我就将学习网络游戏编程的学习心得写于此(其实我也才刚刚学),分享给大家,希望对大家有所帮助。此篇文章是聊天系统,之后的状态同步与此都大同小异。

Unity测试视频:

网络笔记

目录

1.起步工作——Unity的聊天UI搭建

1.1创建两个Button、一个Inputfield和一个Text

 1.2创建控件脚本(该脚本后续也是客户端脚本)

 1.3为“连接”和“发送”按键绑定关键方法

2.异步の客户端

2.1关键方法a——Connection方法的搭建

2.2关键方法b——Send方法的搭建

3.异步の服务端

3.1

4.完善代码


1.起步工作——Unity的聊天UI搭建

1.1创建两个Button、一个Inputfield和一个Text

分别为两个Button子物体下的text命名为“连接”和“发送”(自己可以修改成自己喜欢的颜色和外DIY)

注意:这里的text和创建的Text是不同级别的,注意区分!

 1.2创建控件脚本(该脚本后续也是客户端脚本)

创建一个Script脚本组件随便挂载到一个物体上(笔者是选择挂在摄像头,它所含组件少些),拟出inputField和text,并将Unity中的Inputfield、Text物体挂上。

 

 1.3为“连接”和“发送”按键绑定关键方法

先在之前的脚本内添加Send方法和Connection方法,再在Unity中为两个Button绑定上预设方法。

 注意:此处的物体填入之前放置脚本的物体(我这里放置主摄像机)

2.异步の客户端

进入此之前,先普及一下异步通信,例如异步connect,其包含BeginConnect和EndConnect两个关键API,而BeginConnect中的参数包含一个AsyncCallback委托,即回调函数。而这个回调函数必须设有参数IAsyncResult。关于其有罗培羽老师做出的解释:(理解会用就行)

IAsyncResult是.NET提供的一种异步操作,通过名为Begin×××和End××x点的两个方法来实现原同步方法的异步调用。Begin×××方法包含同步方法中所需的参数,此外还包含另外两个参数:一个AsyncCallback委托和一个用户定义的状态对象。委托用来调用回调方法,状态对象用来向回调方法传递状态信息。且Begin×××方法返回一个实现IAsyncResult接口的对象,End×××方法用于结束异步操作并返回结果。End×××方法含有一个IAsyncResult参数,用于获取异步操作是否完成的信息,它的返回值与同步方法相同。
可完。

2.1关键方法a——Connection方法的搭建

打开脚本,为Connection()方法"生儿育女"。磨刀不误砍柴工,先上逻辑框架。

 注释:在Connection方法中,我们首先得创建一个Socket用于与服务端通信的一个客户端,然后通过API——BeginConnect连接服务器,在自定义的Connectcallback回调函数中使用BeginReceive来接收服务器的消息,并通过自定义的Receivecallback实现"接受循环"。紧接着,将异步Receive中获取的信息赋值给Text.text,从而同时实现消息的接受和展示。

接下来是代码展示:

​
public void Connection()
        {
            //构建客户端Socket
            Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //连接开始
            //"127.0.0.1"默认是本机IP。
            IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);
            client.BeginConnect(iPEndPoint, Connectcallback, client);
        }
        public void Connectcallback(IAsyncResult ar)
        {
            try
            {
                Socket tempSocket = (Socket)ar.AsyncState;
                tempSocket.EndConnect(ar);  //此处表示连接成功
                tempSocket.BeginReceive(readBuff, 0, 1024, 0, Receivecallback, tempSocket);

            }catch(SocketException ex)
            {
                Console.WriteLine("Error:" + ex);
            }
            
        }
        public void Receivecallback(IAsyncResult ar)
        {
            try
            {
                Socket tempSocket = (Socket)ar.AsyncState;
                int count = tempSocket.EndReceive(ar);
                massage = System.Text.Encoding.Default.GetString(readBuff, 0, count);

                //循环Receive
                tempSocket.BeginReceive(readBuff, 0, 1024, 0, Receivecallback, tempSocket);

            }
            catch (SocketException ex)
            {
                Console.WriteLine("Error:" + ex);
            }

        }

​private void Update()
    {
        //显示
        text.text = massage;
    }

2.2关键方法b——Send方法的搭建

同样的,先上逻辑框架。

 注释:在Send方法中,我们获取输入框的text文本,然后将文本的内容通过API——BeginSend发送给服务端,达到时刻等待用户的发送而不影响主程序的进行。

下面是代码展示:

public void Send()
    {
        //发送消息
        string sendStr = inputField.text;
        byte[] sendByte = System.Text.Encoding.Default.GetBytes(sendStr);
        socket.BeginSend(sendByte, 0, sendByte.Length, 0, Sendcallback, socket); 

       
    }
//回调函数
public void Sendcallback(IAsyncResult asyncResult) 
    {
        try
        {
            Socket temporarySocket = (Socket)asyncResult.AsyncState;
            temporarySocket.EndSend(asyncResult);

        }
        catch (SocketException ex)
        {
            Debug.Log("失败:" + ex.ToString());

        }
    }

3.异步の服务端

3.1

相对于客户端,服务端相对复杂些。在此之前,我们先了解一下我们要使用的C#提供的一个集合体——Dictionary(字典),它是一个存储结构,包含两个字段,一个是‘Key’,另一个是‘Value’,它类似于我们的List。而字典的作用是用于管理与客户端通信的多个服务Sockets,而且有利于消息的广播。

先展示逻辑框架(字有点潦草。。。)

注释:在完成Bind、Listen进入监听状态后,我们使用API——BeginAccept获取连接,在自定义的回调函数Acceptcallback中实现异步Receive(用法与之前相同),与客户端不同,服务端的接收的消息是直接广播给全部客户端Socket的,因此我们要利用Dictionary来遍历所有的用于通信的serverSockets,来一一使用API——Send。

上码:

//这里创造一个类用于封装serverSocket,有利于Dictionary的导入。
class ClientState
    {
        public Socket socket;
        public byte[] readBuff = new byte[1024];
    }
    class Program
    {
        static Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>();
        public static Socket acceptSocket;
        static void Main(string[] args)
        {

            Console.WriteLine("Welcome to Server!!!");
            //Socket
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress local_Ip = IPAddress.Parse("127.0.0.1");
            IPEndPoint iPEndPoint = new IPEndPoint(local_Ip, 8888);
            //绑定
            server.Bind(iPEndPoint);
            //Listen
            server.Listen(0);
            Console.WriteLine("[服务器]启动成功!");

            //接收客户端
            server.BeginAccept(Acceptcallback, server);
            Console.ReadLine();

            while (true)
            {
                //Accept
                Socket chat_socket = server.Accept();
                Console.WriteLine("服务器连接一名客户端:");
                //Receive
                byte[] readBuff = new byte[10240];
                int count = chat_socket.Receive(readBuff);  //将Receive接收到的字节流保存在readBuff
                string massage = System.Text.Encoding.Default.GetString(readBuff, 0, count);

                Console.WriteLine("【服务器接收】" + massage);

                //Send
                byte[] sendBuff = System.Text.Encoding.Default.GetBytes(massage);
                chat_socket.Send(sendBuff);
            }
        }
        public static void Acceptcallback(IAsyncResult ar)
        {
            try
            {
                Console.WriteLine("客户端接入");
                Socket socket = (Socket)ar.AsyncState;
                Socket chatSocket = socket.EndAccept(ar);
                //列表导入
                ClientState state = new ClientState();
                state.socket = chatSocket;
                clients.Add(chatSocket, state);
                //接收数据
                chatSocket.BeginReceive(state.readBuff, 0, 1024, 0, Receivecallback, state);
                socket.BeginAccept(Acceptcallback, socket);
            }
            catch(SocketException ex)
            {
                Console.WriteLine("Error:" + ex.ToString());
            }
        }
        public static void Receivecallback(IAsyncResult ar)
        {
            try
            {
                ClientState clientState = (ClientState)ar.AsyncState;
                Socket clientfd = clientState.socket;
                int count = clientfd.EndReceive(ar);
                //判断客户端的断开
                if (count == 0)
                {
                    clientfd.Close();
                    clients.Remove(clientfd);
                    Console.WriteLine("有客户端退出!");
                    return;
                }
                string recvStr = System.Text.Encoding.Default.GetString(clientState.readBuff, 0, count);
                //将信息广播
                string time = System.DateTime.Now.ToString();
                byte[] vs = System.Text.Encoding.Default.GetBytes("[消息]:" + recvStr);
                foreach (ClientState item in clients.Values)  //此处将clients中的所有clientstate遍历出来;
                {
                    if(item.socket!=clientfd)
                    item.socket.Send(vs);
                }
                clientfd.Send(vs);
                clientfd.BeginReceive(clientState.readBuff, 0, 1024, 0, Receivecallback, clientState);
            }
            catch (SocketException ex)
            {
                Console.WriteLine("error:" + ex.ToString());
            }
        }
    }
    

4.完善代码

为了使得客户端展示时只出现一句massage、同步时间等效果,笔者逐步完善服务端代码,展示如服务端代码区3.1。

PS:都看到这了,笔者写作不易呀,花费几天业余的时间创作,还请留下你的痕迹,接下来我会保持分享我学习网络游戏设计的心得!!!!

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值