Unity3D网络游戏0.3

异步客户端

 上文中的同步服务端程序会一直阻塞等待某个客户端的数据,导致它同一时间只能处理一个客户端的请求,无法响应其他客户端。使用异步方法,可以让服务端同时处理多个客户端的数据,及时响应。

(1)管理客户端

 服务端需要将客户端的消息发送给所有的客户端,譬如聊天室,某个用户说了一句话,服务端需要把这句话发送给聊天室内的每一个人。因此服务端需要有个列表,保存所有连接上来的客户端信息。
 创建ClientState类保存一个客户端的信息:

public class ClientState 
{
    public Socket socket;
    public byte[] readBuff = new byte[1024];
}

 ClientState包含TCP连接所需Socket, 以及用于填充BeginReceive参数的读缓冲区readBuff。

 为了方便的获取客户端的信息,定义Dictionary类字典:

static Dictionary<Socket,ClientState> clients = new Dictionary<Socket, ClientState>();

(2)异步Accept

BeginAccept和EndAccept原型:

public IAsyncResult BeginAccept{
	AsyncCallback callback;
	object state;
}
public Socket EndAccept{
	IAsyncResult asyncResult;
}

 执行BeginAccept方法,当客户端连接上来时,回调函数AsyncCallback将被执行。在回调函数中,开发者可以使用EndAccept获取新客户端的套接字(Socket),还可以获取state参数传入的数据。

(3)异步服务端代码

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
public class ClientState 
{
    public Socket socket;
    public byte[] readBuff = new byte[1024];
}

class MainClass
{
    //监听Socket listenfd
    private static Socket listenfd;
    //连接服务端的客户端列表(使用字典是为了方便查找)
    private static Dictionary<Socket, ClientState> clients = 
        new Dictionary<Socket, ClientState>();

    public static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
        //创建监听Socket
        listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //注册Bind
        IPAddress ipAdr = IPAddress.Parse("127.0.0.1");
        IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);
        listenfd.Bind(ipEp);
        //设置并开始监听
        listenfd.Listen(0);
        Console.WriteLine("[服务器]启动成功");
        //传入回调函数的状态信息是:监听Socket
        listenfd.BeginAccept(AcceptCallback, listenfd);
        Console.ReadLine();
    }
    public static void AcceptCallback(IAsyncResult ar)
    {
        try
        {
            //根据传入的监听Socket调用End方法,返回:连接客户端的Socket
            Console.WriteLine("[服务端]Accept");
            Socket listenfd = (Socket)ar.AsyncState;
            Socket clientfd = listenfd.EndAccept(ar);
            
            //根据连接客户端的Socket创建对应的客户端信息对象
            ClientState state = new ClientState();
            state.socket = clientfd;
            //将客户端信息对象加入服务端客户列表(Socket,state)
            clients.Add(clientfd,state);
            //对连接的客户端进行监听(接收数组使用对应客户端的读缓冲区readBuff)
            clientfd.BeginReceive(state.readBuff, 0, 1024, 0,
                ReceiveCallback, state);
            //服务器继续监听:客户端连接
            listenfd.BeginAccept(AcceptCallback, listenfd);
        }
        catch (SocketException e)
        {
            Console.WriteLine("Socket Accept fail"+e.ToString());
        }
    }

    public static void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            //回调函数的状态参数为客户端信息
            ClientState state = (ClientState)ar.AsyncState;
            Socket clientfd = state.socket;
            //接收客户端发送的信息
            int count = clientfd.EndReceive(ar);
            //客户端发送信息为空则移除客户端连接并且return Receive (关闭对其信息的接收)
            if (count == 0)
            {
                clientfd.Close();
                clients.Remove(clientfd);
                Console.WriteLine("Socket Close");
                return;
            }
            //发送原文回复客户端
            string receStr = System.Text.Encoding.Default.GetString(state.readBuff, 0, count);
            byte[] sendBytes = System.Text.Encoding.Default.GetBytes("echo" + receStr);
            //可以使用同步Send方法
            clientfd.Send(sendBytes);
            //继续接收客户端的信息
            clientfd.BeginReceive(state.readBuff, 0, 1024, 0, ReceiveCallback, state);
        }
        catch (SocketException e)
        {
            Console.WriteLine("Socket Receive fail:"+e.ToString());
        }
    }
}

 在Unity中导出exe文件,运行多个客户端开始聊天吧!

(4)异步聊天服务端代码

 聊天服务端的区别在于,在发送信息时要将信息发送给每一个客户端。
 聊天客户端的区别在于,在接收信息时将新接收到的信息拼接到原始文本的上一行即可。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
public class ClientState 
{
    public Socket socket;
    public byte[] readBuff = new byte[1024];
}

class MainClass
{
    //监听Socket listenfd
    private static Socket listenfd;
    //连接服务端的客户端列表(使用字典是为了方便查找)
    private static Dictionary<Socket, ClientState> clients = 
        new Dictionary<Socket, ClientState>();

    public static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
        //创建监听Socket
        listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //注册Bind
        IPAddress ipAdr = IPAddress.Parse("127.0.0.1");
        IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);
        listenfd.Bind(ipEp);
        //设置并开始监听
        listenfd.Listen(0);
        Console.WriteLine("[服务器]启动成功");
        //传入回调函数的状态信息是:监听Socket
        listenfd.BeginAccept(AcceptCallback, listenfd);
        Console.ReadLine();
    }
    public static void AcceptCallback(IAsyncResult ar)
    {
        try
        {
            //根据传入的监听Socket调用End方法,返回:连接客户端的Socket
            Console.WriteLine("[服务端]Accept");
            Socket listenfd = (Socket)ar.AsyncState;
            Socket clientfd = listenfd.EndAccept(ar);
            
            //根据连接客户端的Socket创建对应的客户端信息对象
            ClientState state = new ClientState();
            state.socket = clientfd;
            //将客户端信息对象加入服务端客户列表(Socket,state)
            clients.Add(clientfd,state);
            //对连接的客户端进行监听(接收数组使用对应客户端的读缓冲区readBuff)
            clientfd.BeginReceive(state.readBuff, 0, 1024, 0,
                ReceiveCallback, state);
            //服务器继续监听:客户端连接
            listenfd.BeginAccept(AcceptCallback, listenfd);
        }
        catch (SocketException e)
        {
            Console.WriteLine("Socket Accept fail"+e.ToString());
        }
    }

    public static void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            //回调函数的状态参数为客户端信息
            ClientState state = (ClientState)ar.AsyncState;
            Socket clientfd = state.socket;
            //接收客户端发送的信息
            int count = clientfd.EndReceive(ar);
            //客户端发送信息为空则移除客户端连接并且return Receive (关闭对其信息的接收)
            if (count == 0)
            {
                clientfd.Close();
                clients.Remove(clientfd);
                Console.WriteLine("Socket Close");
                return;
            }
            //发送原文回复客户端
            string receStr = System.Text.Encoding.Default.GetString(state.readBuff, 0, count);
            byte[] sendBytes = System.Text.Encoding.Default.GetBytes("echo" + receStr);
            //可以使用同步Send方法
            foreach (ClientState s in clients.Values)
            {
                s.socket.Send(sendBytes);
            }
            //继续接收客户端的信息
            clientfd.BeginReceive(state.readBuff, 0, 1024, 0, ReceiveCallback, state);
        }
        catch (SocketException e)
        {
            Console.WriteLine("Socket Receive fail:"+e.ToString());
        }
    }
}

你可能感兴趣的:(网络,服务器,c#,unity,游戏)