unity(select)

  1. select 服务端

服 务 端 调 用 S e l e c t , 等 待 可 读 取 的 S o c k e t , 流 程 如 下。
初始化 listenfd
初 始化 clients列 表

while (true) {
	checktist = 待 检 测 s o c k e t 列 表
	Select (checkList ...)
	f o r (遍 历可 读 c h e c k L i s t 列 表 ){
	   i f ( l i s t e n f d 可 读 ) Ac c e p t ;
	   i f ( 这个客户端可读 ) 消息处理;
	}
}

服务端使用主循环结构while(true){…}子,不断地调用Select 检测Socket状态,其步骤 如 下:

  • 将监听Socket( listenfd)和客户端Socket(遍历clients 列表) 添加到 待检测Socket 可读状态的列表checkList 中。
  • 调用Select,程序中设置超时时间为1秒,若1秒内没有任何可读信息,Select 方法 将checkList 列表变成空列表,然后返回。
  • 对Select处理后的每个Socket做处理,如果监听Socket( listenfd)可读,说明有客 户 端 连 接 , 需 调 用 A c c e p t 。 如 果 客 户 端 Socket
    可 读, 说明客户端发送了消息 (或 关 闭 ), 将消息广播 给所 有客 户 端。
    上述过 程的 示 例代码 如 下:
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Net.WebSockets;

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

	class MainClass
	{
		// 监听Socket
		static Socket listenfd;

        // 客户端Socket及状态信息
		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);

			//Listen
			listenfd.Listen(0);
			Console.WriteLine("[服务器]启动成功");


            // 方案四: select方案
		    List<Socket> checkRead = new List<Socket>();
			//主循环
			while(true) {
                // 填充checkRead列表
				checkRead.Clear();
				checkRead.Add(listenfd);
				foreach(ClientState s in clients.Values) {
					checkRead.Add(s.socket);
				}

                //select
				Socket.Select(checkRead, null, null, 1000);

                //检查可读对象
				foreach(Socket s in checkRead) {
					if (s == listenfd) {
                         ReadListendfd(s);
					} else {
                         ReadClientfd(s);
					}
				}
			}
		}

		//读取Clientfd
		public static bool ReadClientfd(Socket clientfd) {
                ClientState state = clients[clientfd];
		        int count = clientfd.Receive(state.readBuff);
			    //客户端关闭
				if(count == 0){
					clientfd.Close();
					clients.Remove(clientfd);
					Console.WriteLine("Socket Close");
					return false;
				}
				//广播
				string recvStr = System.Text.Encoding.Default.GetString(state.readBuff, 0, count);
				Console.WriteLine("Receive " + recvStr);
				string sendStr = clientfd.RemoteEndPoint.ToString() + ":" + recvStr;
				byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);
				foreach (ClientState cs in clients.Values){
					cs.socket.Send(sendBytes);
				}
				return true;
		}

		//读取Listenfd
		public static void ReadListendfd(Socket listenfd) {
			Console.WriteLine("Accept");
			Socket clientfd = listenfd.Accept();
			ClientState state = new ClientState();
			state.socket = clientfd;
			clients.Add(clientfd, state);
		}
	}
}
  1. select 客户端
    使用Select 方法的客户端和使用Poll 方法的客户端极其相似,因为只需检测一个 Socket 的状态,将连接服务端的socket输人到checkRead列表即可。为了不卡住客户端, Select的超时时间设置为 0 , 永不阻塞。 示例代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using UnityEngine.UI;
using System.Threading;
using System;

public class Echo : MonoBehaviour
{
    //定义套接字
    Socket socket;
    //UGUI
    public InputField InputField;
    public Text text;

    //接收缓冲区
    byte[] readBuff = new byte[1024];
    string recvStr = "";

    //checkRead列表
	List<Socket> checkRead = new List<Socket>();

    //点击连接按钮
    public void Connection()
    {
        //Socket
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 
        //Connect 这个是同步方法
        socket.Connect("127.0.0.1", 8888);
        //socket.BeginConnect("127.0.0.1", 8888, ConnectionCallback, socket);
    }



    //点击发送按钮
    public void Send()
    {
        //Send
        string sendStr = InputField.text;
        byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);

        //改用异步发送
        socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, SendCallback, socket);
    }

    //Send回调
    public void SendCallback(IAsyncResult ar) {
        try {
            Debug.Log("异步发送测试");
            Socket socket = (Socket) ar.AsyncState;
            int count = socket.EndSend(ar);
            Debug.Log("Socket Send succ" + count);
        } catch (SocketException ex) {
            Debug.Log("Socket Send fail " + ex.ToString());
        }
    }

    public void Update() {
        if(socket == null) {
            return;
        }

        //填充checkRead列表
        checkRead.Clear();
        checkRead.Add(socket);

        //select
        Socket.Select(checkRead, null, null, 0);
        //check
        foreach(Socket s in checkRead) {
           byte[] readBuff = new byte[1024];
           int count = socket.Receive(readBuff);
           string recvStr = System.Text.Encoding.Default.GetString(readBuff,0, count);
           text.text = recvStr;
        }
    }
}

由于程 序 在Update 中 不停 地 检 测 数 据, 性 能 较 差。 商 业 上 为 了做 到 性 能 上的 极 致, 大 多使用异步( 或使用多线程模拟异步程序)。 本书将 会使用异步客户端、Select 服务端演示 程 序。

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值