Week 08-day01-Unity网络通讯之聊天室

目录

一、了解编写服务器demo 

 二、Demo一之中的不足:1.byte[1024]无内容部分都转成了空格,如何解决?2.客户端只能发送一条消息,如何解决?3.只能接受一个客户端发送的消息,如何改进?

 解决方法:

三:实现消息的广播

 四:改写成为异步服务器

思考:有没有问题呢?会不会出错呢?

五、Client类的封装

ClientPeer类

NetManager类:

main函数类:

六、动态链接库:


一、了解编写服务器demo 

基础知识:

        IP地址:计算机在网络中的位置
        DNS:将域名转化为对应IP地址的服务器
        端口号:计算机某一个程序,在操作系统中的编号
        协议:网络传输中的约定
        TCP:管道式传输
                三次握手:
                四次挥手:
        UDP:广播式传输

237c7b546a0f4816ae548aaa470a166c.png

服务器端代码1.0:

using NetMQ.Sockets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace NetWorkTest
{//ip:计算机在网络中的位置//端口:计算机应用程序在操作系统中的编号//传输协议:TCP:管道式传输,好处:安全,信息的先后顺序保持不变;缺点:传输速度限制比较大//          UDP:广播式传输,好处:传输速度很快;缺点:安全性较低,丢包class Program{static void Main(string[] args){Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ip = IPAddress.Parse("192.168.117.1");int port = 9999;     serverSocket.Bind(new IPEndPoint(ip,port));serverSocket.Listen(10);Console.WriteLine("服务器启动了!");Socket clientSocket = serverSocket.Accept();//这是一个阻塞函数Console.WriteLine("服务器收到了一个客户端连接"+clientSocket.RemoteEndPoint.ToString());Byte[] MsgArr = new Byte[1024];clientSocket.Receive(MsgArr, 0, MsgArr.Length, SocketFlags.None);string msg = Encoding.UTF8.GetString(MsgArr);Console.WriteLine("接受的数据为:"+msg);Console.ReadKey();}}
}

 客户端代码1.0:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;namespace ClientTestOne
{class Program{static void Main(string[] args){Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress iPAddress = IPAddress.Parse("192.168.117.1");int port = 9999;clientSocket.Connect(new IPEndPoint(iPAddress, port));Console.WriteLine("客户端开始连接服务器!");string content = "wo只会心疼哥哥!";byte[] msg = Encoding.UTF8.GetBytes(content);clientSocket.Send(msg);Console.ReadKey();}}
}

 二、Demo一之中的不足:
1.byte[1024]无内容部分都转成了空格,如何解决?
2.客户端只能发送一条消息,如何解决?
3.只能接受一个客户端发送的消息,如何改进?

思路:使用多线程

978d0af7928742c6838df06dd1293128.png

 解决方法:

1.

2. 

3.

服务端2.0

using NetMQ.Sockets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Threading;namespace NetWorkTest
{//ip:计算机在网络中的位置//端口:计算机应用程序在操作系统中的编号//传输协议:TCP:管道式传输,好处:安全,信息的先后顺序保持不变;缺点:传输速度限制比较大//          UDP:广播式传输,好处:传输速度很快;缺点:安全性较低,丢包class Program{static void Main(string[] args){Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ip = IPAddress.Parse("192.168.117.1");int port = 9999;     serverSocket.Bind(new IPEndPoint(ip,port));serverSocket.Listen(10);Console.WriteLine("服务器启动了!");while (true){Socket clientSocket = serverSocket.Accept();//这是一个阻塞函数Console.WriteLine("服务器收到了一个客户端连接" + clientSocket.RemoteEndPoint.ToString());Thread thread = new Thread(ReceiveMsg);thread.Start(clientSocket);}Console.ReadKey();}private static void ReceiveMsg(object obj){Socket clientSocket = obj as Socket;byte[] MsgArr = new byte[1024];while (true){int Length = clientSocket.Receive(MsgArr, 0, MsgArr.Length, SocketFlags.None);string msg = Encoding.UTF8.GetString(MsgArr, 0, Length);Console.WriteLine("接受的数据为:" + msg);}}}
}

客户端2.0

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;namespace ClientTestOne
{class Program{static Socket clientSocket;static void Main(string[] args){clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress iPAddress = IPAddress.Parse("192.168.117.1");int port = 9999;clientSocket.Connect(new IPEndPoint(iPAddress, port));Console.WriteLine("客户端开始连接服务器!");Thread thread = new Thread(ReceiveMsg);thread.Start();while (true){string content = Console.ReadLine();byte[] msg = Encoding.UTF8.GetBytes(content);clientSocket.Send(msg);}Console.ReadKey();}static void ReceiveMsg(){byte[] MsgArr = new byte[1024];while (true){int Length = clientSocket.Receive(MsgArr, 0, MsgArr.Length, SocketFlags.None);string msg = Encoding.UTF8.GetString(MsgArr, 0, Length);Console.WriteLine("接受的数据为:" + msg);}}}}

又出现一个小问题:只要有客户端关闭,服务器就会出错,那么如何改进这个问题呢?解决方式:需要使用try...catch

改进后的服务器2.0代码:

using NetMQ.Sockets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Threading;namespace NetWorkTest
{//ip:计算机在网络中的位置//端口:计算机应用程序在操作系统中的编号//传输协议:TCP:管道式传输,好处:安全,信息的先后顺序保持不变;缺点:传输速度限制比较大//          UDP:广播式传输,好处:传输速度很快;缺点:安全性较低,丢包class Program{static void Main(string[] args){Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress ip = IPAddress.Parse("192.168.117.1");int port = 9999;     serverSocket.Bind(new IPEndPoint(ip,port));serverSocket.Listen(10);Console.WriteLine("服务器启动了!");while (true){Socket clientSocket = serverSocket.Accept();//这是一个阻塞函数Console.WriteLine("服务器收到了一个客户端连接" + clientSocket.RemoteEndPoint.ToString());Thread thread = new Thread(ReceiveMsg);thread.Start(clientSocket);}Console.ReadKey();}private static void ReceiveMsg(object obj){Socket clientSocket = obj as Socket;byte[] MsgArr = new byte[1024];while (true){try{//receive的返回值,就是结束到的数据的长度,Receive()是一个阻塞函数int Length = clientSocket.Receive(MsgArr, 0, MsgArr.Length, SocketFlags.None);if (Length == 0){clientSocket.Close();return;}string msg = Encoding.UTF8.GetString(MsgArr, 0, Length);Console.WriteLine("接受的数据为:" + msg);}catch (Exception e){Console.WriteLine(e.Message);clientSocket.Close();return;}}}}
}

 运行截图:

93ed5f79fcb84cefadb8cb8e9d19cf92.png

三:实现消息的广播

提示:客户端自己不需要给自己发消息,使用List存储客户端连接

服务端3.0

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Threading;namespace ChatServer
{class Program{//消息的广播:需要使用List容器存储与客户端建立的连接private static List SocketPoolList = new List();static void Main(string[] args){try{//连接客户端Socket Serversocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);int port = 8899;IPAddress iPAddress = IPAddress.Parse("192.168.117.1");Serversocket.Bind(new IPEndPoint(iPAddress, port));Serversocket.Listen(10);Console.WriteLine("服务器启动了");//主线程中最好不要放置死循环的代码Thread thread = new Thread(AcceptClient);thread.Start(Serversocket);Console.ReadKey();}catch (Exception e){Console.WriteLine(e.Message); }}//接收客户端连接private static void AcceptClient(object obj){Socket Serversocket = obj as Socket;if (Serversocket == null){return;}Socket ClientServer = Serversocket.Accept();SocketPoolList.Add(ClientServer);Console.WriteLine("服务器收到了一个连接:" + ClientServer.RemoteEndPoint.ToString());Thread thread = new Thread(ReceiveMsg);thread.Start(ClientServer);//服务器需要一直接受客户端的消息AcceptClient(Serversocket);}private static void ReceiveMsg(Object obj){Socket ClientSocket = obj as Socket;if (ClientSocket == null){return;}byte[] msgArr = new byte[1024];while (true){try{//客户端接受消息int Length = ClientSocket.Receive(msgArr, 0, msgArr.Length, SocketFlags.None);if (Length == 0){ClientSocket.Close();SocketPoolList.Remove(ClientSocket);return;}string clientMsg =  Encoding.UTF8.GetString(msgArr, 0, Length);Console.WriteLine("客户端发送的消息为:"+clientMsg);byte[] msg = new byte[Length];Array.Copy(msgArr, 0, msg,0, Length);for (int i = 0; i < SocketPoolList.Count; i++){if(SocketPoolList[i] == ClientSocket){continue;}SocketPoolList[i].Send(msg);}}catch (Exception e){Console.WriteLine(e.Message);ClientSocket.Close();SocketPoolList.Remove(ClientSocket);return;}}}}
}

客户端3.0

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;namespace ClientTestOne
{class Program{static Socket clientSocket;static void Main(string[] args){clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress iPAddress = IPAddress.Parse("192.168.117.1");int port = 8899;clientSocket.Connect(new IPEndPoint(iPAddress, port));Console.WriteLine("客户端开始连接服务器!");Console.WriteLine("请输入你的名字:");String name = Console.ReadLine();Thread thread = new Thread(ReceiveMsg);thread.Start();while (true){string content = name + ":"+Console.ReadLine();byte[] msg = Encoding.UTF8.GetBytes(content);clientSocket.Send(msg);}Console.ReadKey();}static void ReceiveMsg(){byte[] MsgArr = new byte[1024];while (true){int Length = clientSocket.Receive(MsgArr, 0, MsgArr.Length, SocketFlags.None);string msg = Encoding.UTF8.GetString(MsgArr, 0, Length);Console.WriteLine(msg);}}}}

运行截图:

555827aa449b4c8ea09ff242c1eeca49.png

 四:改写成为异步服务器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace AsyncChatServer
{class Program{//消息的广播:需要使用List容器存储与客户端建立的连接private static List SocketPoolList = new List();static void Main(string[] args){try{Socket Serversocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress iPAdress = IPAddress.Parse("192.168.117.1");int port = 8899;Serversocket.Bind(new IPEndPoint(iPAdress, port));Serversocket.Listen(10);Console.WriteLine("服务器启动了");Serversocket.BeginAccept(AsyncAccept,Serversocket);//开启异步接受客户端连接}catch (Exception e){Console.WriteLine(e.Message);}Console.ReadKey();}static byte[] msgArr = new byte[1024]; private static void AsyncAccept(IAsyncResult ar){Socket serverSocket = ar.AsyncState as Socket;if (serverSocket == null){return;}Socket clientSocket = serverSocket.EndAccept(ar);SocketPoolList.Add(clientSocket);clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReveive, clientSocket);serverSocket.BeginAccept(AsyncAccept, serverSocket);}private static void AsyncReveive(IAsyncResult ar){Socket clientSocket = ar.AsyncState as Socket;try{int Length = clientSocket.EndReceive(ar);if (Length ==0){clientSocket.Close();SocketPoolList.Remove(clientSocket);return;}string message = Encoding.UTF8.GetString(msgArr,0,Length);Console.WriteLine(message);//消息的广播byte[] msg = new byte[Length];Array.Copy(msgArr, 0, msg, 0, Length);for (int i = 0; i < SocketPoolList.Count; i++){if (SocketPoolList[i] == clientSocket){continue;}SocketPoolList[i].Send(msg);}//重新开启监听clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReveive, clientSocket);}catch (Exception e){Console.WriteLine(e.Message);clientSocket.Close();SocketPoolList.Remove(clientSocket);return;}}}
}

效果同上

思考:有没有问题呢?会不会出错呢?

不会出错,但是有问题,在服务器上的客户端连接接受消息间隔时间很短的情况下,那么就会导致向客户端广播的消息为同一条消息,因为连接上了服务器的客户端在接受消息的时候是异步的,而且共用堆中的数据byte[],因此在间隔时间很短的情况下,那么就会导致客户端接受的消息为同一条消息。

底层原理:多线程在执行的时候,会共享堆内存中的数据(不共享栈内存中的数据)

五、Client类的封装

解决四中的问题,可以利用面向对象的思想改进,将client连接封装成为一个类,使每一个client都能拥有自己byte[]数组来接受消息。

ClientPeer类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace AsyncChatServer
{class ClientPeer{Socket clientSocket = null;static byte[] msgArr = new byte[1024];internal ClientPeer(Socket clientSocket){this.clientSocket = clientSocket;clientSocket.BeginReceive(msgArr,0,msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);}internal void AsyncReceive(IAsyncResult ar){try{Socket clientSocket = ar.AsyncState as Socket;int length = clientSocket.EndReceive(ar);if (clientSocket == null){clientSocket.Close();NetManager.Instance.RemoveClient(this);return;}string message = Encoding.UTF8.GetString(msgArr,0,length);Console.WriteLine(message);NetManager.Instance.BoardCastMessage(message, this);clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);}catch (Exception e){clientSocket.Close();NetManager.Instance.RemoveClient(this);Console.WriteLine(e.Message);return;}}public void SendMessage(byte[] message){clientSocket.Send(message);}public void SendMessage(string message){clientSocket.Send(Encoding.UTF8.GetBytes(message));}}
}

NetManager类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace AsyncChatServer
{class NetManager{private static NetManager _instance = null;private NetManager(){}public static NetManager Instance{get{if (_instance == null){_instance = new NetManager();}return _instance;}}//消息的广播:需要使用List容器存储与客户端建立的连接private static List clientList = new List();public void AddClient(ClientPeer client){clientList.Add(client);}public void RemoveClient(ClientPeer client){clientList.Remove(client);}public void BoardCastMessage(string message,ClientPeer client = null){for (int i = 0; i < clientList.Count; i++){if (client!=null&& clientList[i] == client){continue;}clientList[i].SendMessage(message);}}public void BoardCastMessage(byte[] message, ClientPeer client = null){for (int i = 0; i < clientList.Count; i++){if (client != null && clientList[i] == client){continue;}clientList[i].SendMessage(message);}}public void SendMessage(byte[] message,ClientPeer client){client.SendMessage(message);}public void SendMessage(string message, ClientPeer client){client.SendMessage(message);}}
}

main函数类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace AsyncChatServer
{class Program{static void Main(string[] args){try{Socket Serversocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress iPAdress = IPAddress.Parse("192.168.117.1");int port = 8899;Serversocket.Bind(new IPEndPoint(iPAdress, port));Serversocket.Listen(10);Console.WriteLine("服务器启动了");Serversocket.BeginAccept(AsyncAccept, Serversocket);}catch (Exception e){Console.WriteLine(e.Message);}Console.ReadKey();}static byte[] msgarr = new byte[1024];private static void AsyncAccept(IAsyncResult ar){Socket serversocket = ar.AsyncState as Socket;if (serversocket == null){return;}Socket clientsocket = serversocket.EndAccept(ar);ClientPeer clientPeer = new ClientPeer(clientsocket);NetManager.Instance.AddClient(clientPeer);//clientsocket.beginreceive(msgarr, 0, msgarr.length, socketflags.none, asyncreveive, clientsocket);serversocket.BeginAccept(AsyncAccept, serversocket);}}
}

按理来说:ip端口应该也不应该放在Main函数中,

六、动态链接库:

ddl:包含该动态链接库实际的函数和数据。在程序运行阶段,加载该文件,并将该文件映射到进程地址空间中,然后访问该文件中的相应函数。

一般用来实现一些工具类,配置接口

f2844f166e7449e6934bf1869679e53d.png

生成.dll文件

9491a18f6c5946b581516c5d18745f2b.png

七、将网络模块移植到unity中:

代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;public class NetManager : MonoBehaviour
{//单例private static NetManager _instance = null;private NetManager(){}private static NetManager Instance{get{if(_instance == null){_instance = new NetManager();}return _instance;}}// Start is called before the first frame update//客户端连接服务器//1.连接到服务器//2.接受服务器的消息,并处理//3.向服务器发送消息byte[] msgArr = new byte[1024];Socket clientSocket = null;//消息队列,用于处理消息Queue messageQueue = new Queue();private void OnEnable(){clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);}void Start(){clientSocket.Connect(new IPEndPoint(IPAddress.Parse(Protocol.ProtocolConfig.ip), Protocol.ProtocolConfig.port));Debug.Log("客户端开始连接服务器!");try{clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);}catch (System.Exception e){Debug.Log(e.Message);}}//接受服务器的消息private void AsyncReceive(IAsyncResult ar){Socket clientSocket = ar.AsyncState as Socket;if (clientSocket == null){return;}int length = clientSocket.EndReceive(ar);string message = Encoding.UTF8.GetString(msgArr, 0, length);//接受的消息,思考为啥不在这个函数里面处理消息//异步的本质是一条多线程,很多Unity的API没有办法在支线程中使用messageQueue.Enqueue(message);clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);}private void SendMessageToServer(string message){clientSocket.Send(Encoding.UTF8.GetBytes(message));}// Update is called once per framevoid Update(){if (Input.GetKeyDown(KeyCode.W)){SendMessageToServer("helloWorld");}if (messageQueue.Count>0){string message = messageQueue.Dequeue();//处理这个消息Debug.Log(message);}}
}

运行成功:

8c22ac05086f4067b492a30d071f95ae.png

 思考:接受的消息为啥不在异步函数中执行?为啥要用到消息队列


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部