澳门新葡新京:Jtro的技术分享:游戏客户端与服务器(c#)通讯_同步Socket

开发Unity网络模块时,一般会有异步、多路复用两种方法。它们分别是什么,以及孰优孰劣呢?

在游戏界,独立游戏层出不穷,但是大部分都是单机,网络游戏很少,因为不会有独立开发者自己整一个服务器,除非你先去买个虚拟服务器。网络通讯一直是一个很大的学术,今天研究了一下,把服务器端的代码和unity作为客户端的代码写了下来。可能还不完善大家凑合看,有什么问题可以私我。给大家一个网络编程的思考。

服务器端:在VS中新建项目,用于服务器的搭建

///

//聊天室 服务器

在《Unity3D网络游戏实战》的网络模块中,客户端使用了异步,服务端使用了多路复用。有读者问到为什么这么做,为什么不在客户端使用多路复用?这个问题很多人会遇到,决定写一篇文章说明这个问题。

废话不多说,直接上代码,首先是服务器端的,这个不需要打开unity,直接打开mono或者是vs直接输入代码在控制台运行就行,在这里,围栏方便大家调试我把源码贴出来。大家复制就行了。

using System;
using System.Collections.Generic;     
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;   

///Socket建立TCP链接

namespace Unity聊天室_Socket_TCP_Server
{
    class Program
    {
        public static List<Client> clientList = new
List<Client>();
        public static void BroadcastMessage(string message)
        {

先简单看看异步和多路复用是什么样子。

“`

namespace Chat_Server
{
 
    class Client
    {
        private Socket clientSocket;
        private Thread t;
        private byte[] data = new byte[1024];//接收数据容器

////

            foreach (var clinet in clientList)
            {
                if (clinet.isConnect)
                    clinet.SendToClientMessage(message);
            }

异步

/*

        static List<Client> clientList = new
List<Client>();
         
        static void Main(string[] args)
        {
            Socket tcpServer = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);     
            tcpServer.Bind(new
IPEndPoint(IPAddress.Parse(“192.168.43.231”), 7788));
            tcpServer.Listen(100);

namespace Socket_TCP_服务端

        }
        static void Main(string[] args)
        {
            Socket tcpServer = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
            EndPoint e = new
IPEndPoint(IPAddress.Parse(“10.246.40.205”), 7786);
            tcpServer.Bind(e);
            tcpServer.Listen(100);//监听最多100个客户端
            Console.WriteLine(“Server is running….”);
            //一直监听客户端们来建立链接
            while (true)
            {
                Socket clientSocket =
tcpServer.Accept();//同意链接继续执行; 没链接,等待 不会往下执行
                Console.WriteLine(“one Client is connected!”);
                Client client = new Client(clientSocket);
                clientList.Add(client);
            }
        }
    }
}

异步程序的写法如下,会调用.net网络编程的API,使用BeginXXX和EndXXX这样的语法。实际上,程序内部会开启另外的线程去接收数据。

* 脚本功能:服务器

            Console.WriteLine(“server is runing”);

{
    class Program
    {
        static void Main(string[] args)
        {
            //绑定IP和端口给当前程序
            Socket TCPServer = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
            IPAddress ipaddress = new IPAddress(new byte[] { 10, 246,
40, 205 });
            EndPoint point = new IPEndPoint(ipaddress, 7788);
澳门新葡新京:Jtro的技术分享:游戏客户端与服务器(c#)通讯_同步Socket。            TCPServer.Bind(point);

namespace Unity聊天室_Socket_TCP_Server
{
   public class Client
    {
        private Socket clientSocket;
        private Thread thread;
        private byte[] data = new byte[1024];

public class Echo : MonoBehaviour {

* 作者      :张曙光

            //死循环:解决只能接收一个客户端的问题
            while (true)
            {
                Socket clientSocket =
tcpServer.Accept();//暂停,当有客户端连接时执行下面代码
                Console.WriteLine(“有一个客户端连接上了”);
                Client client = new Client(clientSocket);
                clientList.Add(client);
            }

            //开始监听
            TCPServer.Listen(100);
            Socket ClientSocket = TCPServer.Accept();//挂起不往下走 监听
知道有C来连接成功

        public bool isConnect
        {
            get { return clientSocket.Connected; }
        }
        public Client(Socket s)
        {
            clientSocket = s;
            //启动一个线程用来处理该客户端发来的数据
            thread = new Thread(ReceiveMessage);
            thread.Start();
        }

Socket socket;

* 日期      :2017.11.11

        }   
        //是否连接成功,true为成功
        public bool Connected
        {
            get { return clientSocket.Connected; }
        }  
        public Client(Socket s)
        {
            clientSocket = s;
            //启动一个线程 处理客户端的数据接收
            t = new Thread(ReceiveMessage);
            t.Start();
        }
        //接收从客户端发送的消息
        private void ReceiveMessage()
        {
            //一直接收客户端的数据
            while (true)
            {
                //在接收数据之前 
判断一下socket连接是否断开,等待10毫秒响应。断开连接时:
                if (clientSocket.Poll(10, SelectMode.SelectRead))
                {
                    clientSocket.Close();
                    break;//跳出循环 终止线程的执行
                }

            //Socket通信 
            //向客户端发送消息
            string message = “Hello 世界”;
            byte[] mesData = Encoding.UTF8.GetBytes(message);
            ClientSocket.Send(mesData);

        private void ReceiveMessage()
        {
            while (true)
            {
                //上一次收发是否完成
                //if (clientSocket.Connected == false) { break; }
                //判断TCP链接是否断开
                if (clientSocket.Poll(10, SelectMode.SelectRead)) {
                    clientSocket.Close();
                    Program.clientList.Remove(this);
                    break; }
                int receiveLength = clientSocket.Receive(data);
                string message = Encoding.UTF8.GetString(data, 0,
receiveLength);
                //TODO:接收到数据分发给客户端们
                Program.BroadcastMessage(message);
                Console.WriteLine(“收到消息:” + message);
            }

//接收缓冲区

*/

                //接收消息
                int length = clientSocket.Receive(data);
                string message = Encoding.UTF8.GetString(data, 0,
length);
                
                BroadcastMessage(message);
                Console.WriteLine(“收到了消息:” + message);
            }
        }       
        //服务器向客户端发消息
        public void SendMessage(string message)
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            clientSocket.Send(data);
        }
        //服务器端向客户端广播消息
        public static void BroadcastMessage(string message)
        {
            var notConnectedList = new List<Client>();
            foreach (var client in clientList)
            {
                if (client.Connected) //连接成功的广播消息
                    client.SendMessage(message);
                else  //连接失败的存储在另一个泛型集合里,方便后面移除
                {
                    notConnectedList.Add(client);
                }
            }
            //移除未连接的客户端
            foreach (var temp in notConnectedList)
            {
                clientList.Remove(temp);
            }
        }

            //接收客户端消息
            byte[] receivedata = new byte[2048];
            int receiveLeng = ClientSocket.Receive(receivedata);
            string receivemessge =
Encoding.UTF8.GetString(receivedata,0,receiveLeng);
            Console.WriteLine(“从客户端接收到消息:” + receivemessge);
            Console.ReadKey();
        }
    }

        }

byte[] readBuff = new byte[1024];

using System;

    }
}

}

        public void SendToClientMessage(string message)
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            clientSocket.Send(data);
        }

public Start()

using System.Net;                                               
//引入命名空间

Unity客户端:

namespace Socket_TCP_客户端_001
{
    class Program
    {
        static void Main(string[] args)
        {
            Socket tcpClient = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
            IPAddress ipaddress = IPAddress.Parse(“10.246.40.205”);
            EndPoint point = new IPEndPoint(ipaddress, 7788);

    }
}

{

using System.Net.Sockets;                                       
//引入命名空间

using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

            //建立连接
            tcpClient.Connect(point);

//聊天室 客户端 unity

//Socket

using System.Collections.Generic;

public class ChatManager : MonoBehaviour
{
    public string ipaddress = “192.168.43.231”;
    public int port = 7788;

            //从服务器接收消息
            byte[] data = new byte[1024];
            int receiveLength = tcpClient.Receive(data);//接收data
返回值为接收到的长度
            string message = Encoding.UTF8.GetString(data, 0,
receiveLength);
            Console.WriteLine(message);

public class ChatManager : MonoBehaviour
{
    public string ip = “10.246.40.205”;
    public int prot = 7786;

socket = new Socket(AddressFamily.InterNetwork,

using System.Linq;

    public UIInput textInput;
    public UILabel chatLabel;

            //向服务端发送消息
            string messageClient = Console.ReadLine();
            tcpClient.Send(Encoding.UTF8.GetBytes(messageClient));

    private Socket clinetSocket;
    private Thread thread;
    private byte[] data = new byte[1024];
    private string resevieMessgae = “”;

SocketType.Stream, ProtocolType.Tcp);

using System.Text;

    private Socket clientSocket;
    private Thread t;

            Console.ReadKey();
        }
    }
}

    public InputField textInput;
    public Text PanelText;
    // Use this for initialization
    void Start()
    {
        ConnectToServer();
    }

//Connect

using System.Threading.Tasks;

    private byte[] data = new byte[1024];//数据容器
    private string message = “”;//接收到的消息容器
    
    void Start () {
        ConnectToServer();
    }  
    void Update () {
        if (message != null && message != “”)
        {
            chatLabel.text += “n” + message;
            message = “”;//清空消息
        }
    }
    void ConnectToServer()
    {
        clientSocket = new
Socket(AddressFamily.InterNetwork,SocketType.Stream,
ProtocolType.Tcp);
        //跟服务器端建立连接
        clientSocket.Connect( new
IPEndPoint(IPAddress.Parse(ipaddress),port) );

///标准接口

    // Update is called once per frame
    void Update()
    {
        if (resevieMessgae != null && resevieMessgae != “”)
        {
            PanelText.text += “n” + resevieMessgae;
        }
        resevieMessgae = “”;
    }

socket.Connect(“127.0.0.1”, 8888);

namespace Server

        //创建一个新的线程 用来接收消息
        t = new Thread(ReceiveMessage);
        t.Start();
    }
    //接收消息
    void ReceiveMessage()
    {
        while (true)
        {
            if (clientSocket.Connected == false)
                break;
            int length = clientSocket.Receive(data);
            message = Encoding.UTF8.GetString(data, 0, length);
            
        }
    }
    //发消息
    void SendMessage(string message)
    {
        byte[] data = Encoding.UTF8.GetBytes(message);
        clientSocket.Send(data);
    }
    //发送按钮
    public void OnSendButtonClick()
    {
        string value = textInput.value;
        SendMessage(value);
        textInput.value = “”;
    }

  1. /// <summary>  
  2. /// 连接使用tcp协议的服务端  
  3. /// </summary>  
  4. /// <param name=”ip”>服务端的ip</param>  
  5. /// <param name=”port”>服务端的端口号</param>  
  6. /// <returns></returns>  
  7. public static Socket ConnectServer(string ip, int port)  
  8. {  
  9.     Socket s = null;  
  10.     try  
  11.     {  
  12.         IPAddress ipAddress = IPAddress.Parse(ip);  
  13.         IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);  
  14.         s = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);  
  15.         s.Connect(ipEndPoint);  
  16.         if (s.Connected == false) s = null;  
  17.     }  
  18.     catch (Exception)  
  19.     {      
  20.     }  
  21.     return s;  
  22. }  
  23. /// <summary>  

  24. /// 连接使用tcp协议的服务端  
  25. /// </summary>  
  26. /// <param name=”ip”>服务端的ip</param>  
  27. /// <param name=”port”>服务端的端口号</param>  
  28. /// <returns></returns>  
  29. public static Socket ConnectServer(string ip, int port)  
  30. {  
  31.     Socket s = null;  
  32.     try  
  33.     {  
  34.         IPAddress ipAddress = IPAddress.Parse(ip);  
  35.         IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);  
  36.         s = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);  
  37.         s.Connect(ipEndPoint);  
  38.         if (s.Connected == false) s = null;  
  39.     }  
  40.     catch (Exception)  
  41.     {      
  42.     }  
  43.     return s;  
  44. }  
    1. /// <summary>  
    2.    /// 向远程主机发送数据  
    3.    /// </summary>  
    4.    /// <param name=”socket”>连接到远程主机的socket</param>  
    5.    /// <param name=”buffer”>待发送的字符串</param>  
    6.    /// <param name=”outTime”>发送数据的超时时间,单位是秒(为-1时,将一直等待直到有数据需要发送)</param>  
    7.    /// <returns>0:发送数据成功;-1:超时;-2:错误;-3:异常</returns>  
    8.    public static int SendData(Socket socket, string buffer, int outTime)  
    9.    {  
    10.        if (buffer == null || buffer.Length == 0)  
    11.        {  
    12.            throw new ArgumentException(“buffer为null或则长度为0.”);  
    13.        }  
    14.        return SendData(socket, System.Text.Encoding.Default.GetBytes(buffer), outTime);  
    15.    }
       
    1. /// <summary>  
    2. /// 接收远程主机发送的数据  
    3. /// </summary>  
    4. /// <param name=”socket”>要接收数据且已经连接到远程主机的</param>  
    5. /// <param name=”buffer”>接收数据的缓冲区(需要接收的数据的长度,由 buffer 的长度决定)</param>  
    6. /// <param name=”outTime”>接收数据的超时时间,单位秒(指定为-1时,将一直等待直到有数据需要接收)</param>  
    7. /// <returns></returns>  
    8. public static int RecvData(Socket socket, byte[] buffer, int outTime)  
    9. {  
    10.     if (socket == null || socket.Connected == false)  
    11.     {  
    12.         throw new ArgumentException(“socket为null,或者未连接到远程计算机”);  
    13.     }  
    14.     if (buffer == null || buffer.Length == 0)  
    15.     {  
    16.         throw new ArgumentException(“buffer为null ,或者长度为 0”);  
    17.     }  
    18.   
    19.     buffer.Initialize();  
    20.     int left = buffer.Length;  
    21.     int curRcv = 0;  
    22.     int hasRecv=0;  
    23.     int flag = 0;  
    24.   
    25.     try  
    26.     {  
    27.         while (true)  
    28.         {  
    29.             if (socket.Poll(outTime * 1000, SelectMode.SelectRead) == true)  
    30.             {     
    31.                 // 已经有数据等待接收  
    32.                 curRcv = socket.Receive(buffer, hasRecv, left, SocketFlags.None);  
    33.                 left -= curRcv;  
    34.                 hasRecv += curRcv;  
    35.   
    36.                 // 数据已经全部接收   
    37.                 if (left == 0)  
    38.                 {                                      
    39.                     flag = 0;  
    40.                     break;  
    41.                 }  
    42.                 else  
    43.                 {  
    44.                     // 数据已经部分接收  
    45.                     if (curRcv > 0)  
    46.                     {                                  
    47.                         continue;  
    48.                     }  
    49.                     else  // 出现错误  
    50.                     {                                             
    51.                         flag = -2;  
    52.                         break;  
    53.                     }  
    54.                 }  
    55.             }  
    56.             else // 超时退出  
    57.             {                                                      
    58.                 flag = -1;  
    59.                 break;  
    60.             }  
    61.         }  
    62.     }  
    63.     catch (SocketException)  
    64.     {  
    65.         //Log  
    66.         flag = -3;  
    67.     }  
    68.     return flag;  
    69. }
       

    void ConnectToServer()
    {
        try
        {
            clinetSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
            clinetSocket.Connect(IPAddress.Parse(ip), prot);
            Debug.Log(“ConnectToServer:”);

//BeginReceive

{

    void OnDestroy()
    {
        clientSocket.Shutdown(SocketShutdown.Both);
        
        clientSocket.Close();//关闭连接
    }
}
注意:我们在客户端和服务器端都使用了线程,线程的作用在于加快出库速度,提高性能。服务器端,使用线程接收数据,客户端使用线程发送数据。

///

            //创建一个线程接收消息
            thread = new Thread(ClientResevieMessage);
            thread.Start();

socket.BeginReceive( readBuff, 0, 1024, 0, ReceiveCallback, socket);

class Program

///

        }
        catch (Exception e)
        {
            Debug.Log(“ConnectToServer Exception” + e.ToString());
        }
    }

}

{

///Socket建立UDP链接

    void ClientSendMessage(string message)
    {
        byte[] data = Encoding.UTF8.GetBytes(message);
        clinetSocket.Send(data);
        //Debug.Log(“ClientSendMessage:” + message);
    }

//Receive回调

static void Main(string[] args)

namespace Socket_UDP_Server
{
    class Program
    {
        public static Socket udpServer;
        static void Main(string[] args)
        {
            //创建UDP 绑定IP端口
            udpServer = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
            udpServer.Bind(new
IPEndPoint(IPAddress.Parse(“10.246.40.205”), 8899));

    void ClientResevieMessage()
    {
        while (true)
        {
            if (!clinetSocket.Connected)
            {
                break;
            }
            int length = clinetSocket.Receive(data);
            resevieMessgae = Encoding.UTF8.GetString(data, 0, length);
        }
    }
    public void OnSendButtonClick()
    {

public void ReceiveCallback(IAsyncResult ar){

{

            //开启线程
            new Thread(ReceiveMessage) { IsBackground = true
}.Start();

        string value = SystemInfo.deviceName + “发言:” +
textInput.text;
        ClientSendMessage(value);
        textInput.text = “”;
    }

Socket socket = ar.AsyncState;

Console.WriteLine(“这是一个服务器,你相信吗?”);

          
            Console.ReadKey();

    void OnDisale()
    {
        //  clinetSocket.Shutdown(SocketShutdown.Both);
        clinetSocket.Close();
    }

int count = socket.EndReceive;

//Socket  套接字

        }
        /// <summary>
        ///  接收数据
        /// </summary>
        static void ReceiveMessage()
        {
            while (true)
            {

    public void ExitGame()
    {
        clinetSocket.Close();
        Debug.Log(“ExitGame:”);
        Application.Quit();
    }
}

string s = System.Text.Encoding.Default.GetString(readBuff, 0, count);

Socket listenfd = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);

                EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = new byte[1024];
                int length = udpServer.ReceiveFrom(data, ref
remoteEP);

socket.BeginReceive( readBuff, 0, 1024, 0,ReceiveCallback, socket);

/*

                string message = Encoding.UTF8.GetString(data, 0,
length);
                Console.WriteLine(“从IP:” + (remoteEP as
IPEndPoint).Address + “:” + (remoteEP as IPEndPoint).Port + “收到数据:”

}


现在解释上面的一行代码,功能是实例了一个套接字,它的3个参数分别代表了地址簇、套接字类型和协议

  • message);
                }

}

*  地址簇指明用的是IPv4还是用的是IPv6,

        }
    }
}

图示如下,调用BeginReceive后,程序就开启了一条新的线程,在新的线程里阻塞等待。等有消息回来时,才往下执行。

*  SocketType指的是套接字的类型,游戏开发中用的stream居多

namespace Socket_UDP_Client
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建UDP
            Socket udpClient = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);

澳门新葡新京 1

*    ProtocolType指明使用的协议,如你所见,用的是TCP协议

            while (true)
            {

多路复用

*/

                //发送数据
                EndPoint serverPoint = new
IPEndPoint(IPAddress.Parse(“10.246.40.205”), 8899);
                string message = Console.ReadLine();
                byte[] data = Encoding.UTF8.GetBytes(message);
                udpClient.SendTo(data, serverPoint);

异步程序写起来比较麻烦,而且代码量多,其实有一种更简便的处理方法,那就是使用poll或select。使用poll的代码如下。

//Bind

                Console.ReadKey();

//省略各种using

IPAddress ipAdr = IPAddress.Parse(“127.0.0.1”);

            }
        }
    }
}

public class Echo : MonoBehaviour {

IPEndPoint ipEp = new IPEndPoint(ipAdr ,1234);

//定义套接字

listenfd.Bind(ipEp);

Socket socket;

/*

public void Start()


listenfd.Bind(ipEp)将给listenfd套接字绑定IP和端口。在这里是绑定的是本地地址127.0.0.1和1234号端口

{


127.0.0.1是回送地址,指的是本地机,一般用来测试,当然你可以设置成另外一台机的ip地址,然后在2台电

//Socket

*  脑上分别运行客户端和服务器端

socket = new Socket(AddressFamily.InterNetwork,

*/

SocketType.Stream, ProtocolType.Tcp);

//Listen

//Connect

listenfd.Listen(0);

socket.Connect(“127.0.0.1”, 8888);

Console.WriteLine(“[ 服务器 ] 启动成功”);

}

/*

public void Update(){

*
listenfd.Listen(0)开启监听器,等待客户端连接,参数backlog指定队列中最多可容纳多少人连接,0表示不限制

if(socket.Poll(0, SelectMode.SelectRead)){

*/

byte[] readBuff = new byte[1024];

while (true)

int count = socket.Receive;

{

string recvStr =

//Accept

System.Text.Encoding.Default.GetString(readBuff, 0, count);

Socket connfd = listenfd.Accept();

}

Console.WriteLine(“[服务器] Accept”);

}

/*

}

*
开启监听器后,服务器开始调用listenfd.Accept()接收客户端连接。本案例使用的所有Socket方法都是“阻塞”

无论如何,这段代码比异步要少一些。它的原理是使用socket.Poll,但socket有可读数据时,该方法返回true,如果没有返回false。那么程序只要在Update中不断去检测socket的状态,有数据的时候才去读取,也可以实现功能。

*
方法,也就是说,如果一直没有客户点连接进来的话,服务器的程序会一直卡在listenfd.Accpect()处,不往

客户端为什么使用异步

*
下执行,直到客户端连接之后Accpet返回一个新的客户端的Socket,对于服务器来说它有一个监听的Socket(案

我见过的大多数游戏程序使用异步或者多线程去处理网络模块。这就产生了个疑问,多路复用代码量少写起来更简单,但为什么不使用呢?在小型项目中其实使用哪种方式都没有太大区别,但当我们要考虑网络性能的时候,就要仔细斟酌,客户端不使用多路复用出于以下两个原因。

*
例中的listenfd)用来接收Accpet客户端的连接,对于每一个客户端来说,还有一个专门的Socket(案例中的Connfd)

1.不断遍历

* 用来处理该客户端的数据

由上述程序可知,poll模式中,程序要在Update不断检测,可能每秒要检测30到60次,增加了计算量。而异步就没有这个问题,在网络消息到达的时候,线程被唤起,不需要遍历。

*/

2.对主线程的影响

//Recv

当接收到网络数据时,例子中使用了Encoding.Default.GetString把字节流转换成字符串,在实际游戏中,可能使用protobuf或者json协议,把字节流解析成协议对象有一定的计算量。在下图中,异步程序可以在异步线程中做解码,使得程序不会因为解码而卡住主线程。而Poll程序就做不到这一点,它在主线程中解码。Unity的脚本逻辑(Awake、Start、Update、碰撞、cpu渲染部分)都依赖于主线程,网络模块对主线程的影响越小,性能就越好。

byte[] readBuff = new byte[1024];

澳门新葡新京 2

int count = connfd.Receive(readBuff);

服务端为什么使用多路复用

string str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);

客户端和服务端多面临的情况不同,客户端一般只需要维持一个连接,而服务需要维持所有客户端的连接。多路复用为何取名叫“多路”,其核心就是要解决“多个连接”的问题。

Console.WriteLine(“[ 服务器接收 ]” +str);

澳门新葡新京 3

/*

因为服务端要处理各个玩家的逻辑,玩家之间可能还有交互,比如下图中,玩家1和玩家2都在一个房间内。

*
服务器通过connfd.Receive接收客户端数据,receive也是阻塞的方法,没有接收到客户端数据时,程序卡在Receive

澳门新葡新京 4

*
处,不会向下执行,Receive带有一个byte[]类型的参数,它将储存接收到的数据,Receive的返回值则指明接收到的

如果使用了多线程,那么玩家1和玩家2的操作就有可能出现线程冲突,在处理逻辑时需要给房间对象加锁,《Unity3D网络游戏实战》使用的就是这种方式。如果避开多线程,加锁的问题也就不复存在了,逻辑会更加明了,出Bug的可能性也会减少。多线程处理并不是一个简单的事情,需要很多经验积累才能处理好。

* 数据长度之后,用string str =
System.Text.Encoding.UTF8.GetString(readBuff, 0,
count);这段代码将byte[]数

* 组转化为字符串显示在屏幕上

*/

//Send

byte[] bytes = System.Text.Encoding.Default.GetBytes(” Serv echo ” +
str);

connfd.Send(bytes);

/*

*
服务器通过Connfd.Send发送数据,它接收一个byte[]类型的参数指明要发送的内容,Send的返回值指明发送的数据长

* 度(案例中没有),服务器程序用System.Text.Encoding.Default.GetBytes(”
Serv echo ” + str)把字符串转换成

* byte[]数组,然后发送给客户端。

*/

}

}

}

}

“`

注释已经写的很明白了。

这是运行的图:

澳门新葡新京 5

服务器运行的                                        时候               
                        客户端还没链接

那么接下来就是unity客户端的了

首先新建一个unity空工程,我的unity版本是2017.2.0.f3的版本,所以2017.2.0.f3版本之下的版本复制粘贴都没问题。

新建画布添加组件,和我这个差不多就行。排版自己排就行。

澳门新葡新京 6

组件                                        H                           
            ierarchy                                        视图

澳门新葡新京 7

Game                                                                   
                                                    视图

新建一个脚本,叫NET,这里同样贴出代码,方便大家复制粘贴调试。

“`

//初创日期  :2017.11.11

//脚本功能  :客户端

//脚本挂载在:    不需要挂在任何物体上

//作者:    Jtro//第一次修改:

/**/

using System.Collections;

using System.Collections.Generic;using UnityEngine;using System.Net;   
              //引入命名空间

using System.Net.Sockets;          //引入命名空间

using UnityEngine.UI;public class net : MonoBehaviour {    Socket
socket;                  //与服务器端的套接字

  public InputField HostInput;    //服务器端的ip地址

    public InputField PortInput;   
//服务器端的端口,其实在服务器那块7777端口也能用。

    public Text RecvText;          //服务器收到的信息

    public Text ClientText;        //客户端的信息

    const int BUFFER_SIZE = 1024;  //接收缓冲区

  byte[] readBuff = new byte[BUFFER_SIZE];

    ////// 按钮点击事件

///public void Btn_Connetion()

  {        //Socket 

    socket = new Socket(AddressFamily .InterNetwork,SocketType.Stream
,ProtocolType.Tcp);     

//Connect   

  string host = HostInput.text;        string strport = PortInput.text; 
      int port = int .Parse(strport);        socket.Connect(host
,port);        ClientText.text = “客户端地址 ” +
socket.LocalEndPoint.ToString();   

  //Send   

  string str = “HELLO , I am Jtro.”;     

string time = System.DateTime.Now.ToString();   

byte[] bytes = System.Text.Encoding.Default.GetBytes(str+ ”  “+time); 
 

socket.Send(bytes);   

  //Recv     

int count = socket.Receive(readBuff);

str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);

RecvText.text = str;        //Close        socket.Close();    }

// Use this for initialization

void Start () {}

// Update is called once per framevoid Update () {

}

}

“`

然后运行时的截图

澳门新葡新京 8

输入                                                                   
                                                    服务器地址         
                              点击连接服务器

好了这样就行了,可以看到发送的信息,那么,如果是要写一个及时聊天系统该如何写呢?

下次把这个功能实现

OK,感谢简书,感谢读完的你。

然后现在的简书MARKDOEN不能发图片了吗,以前是可以的,现在这个这是用富文本写的,所以代码没有被排版,有谁知道Markdown现在怎么发图片请告诉我一下。谢啦

相关文章