小白初识Unity Socket编程,对一些疑惑代码的测试。(二)

学习中我坚信要知其然,也要知其所以然,不能只当API的搬运工。

复习

过了一天我居然都有点忘了Socket双端连接发送的操作步骤了,在这里先复习一下。
客户端
1.创建Soeket对象。
2.socket对象连接。
3.进行你的操作。
服务端
1.创建监听对象,还是Socket类型
2.绑定Bind地址和端口号
3.开启监听
4.客户端连接时应答Accept

多人聊天室

复习完后开始今天的学习,多人聊天室
使用异步代码+不断的委托,可以做到一个事情做完后继续等待下一个事情。
这样当有多个客户端请求连接时,就可以一个一个排好顺序Accept然后等待收数据。

服务器也多出来一个监听列表Clients,用来监听Clientstate。
当服务器收到消息时就会遍历列表给他们发送消息。
服务端代码:

foreach(ClientState s in clients.values)
{
	s.socket.send(sendBytes);
}

ClientState是自定义的一个类,拥有一个Socket以及一个缓冲区。

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

Poll

Poll会检查Socket的状态其参数分别表示,等待回应的时间,以及当前Socket的状态。
read表示可读,就是当前的Socket有数据可以读取,及可以调用Receive。
write表示可写,就是反过来表示当前的Socket可以写入数据,就可以调用Send。
erro表示错误。
这里展示了一个客户端接收的代码演示

        if (socket.Poll(0, SelectMode.SelectRead))
        {
            byte[] readBuff = new byte[1024];
            int count = socket.Receive(readBuff);
            string recvStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
        }

不难看出只有Ture就是Socket为可读的情况下才会执行读取的操作,相较于异步代码简洁了不少。

服务端这里的话

 while(true){
			//检查listenfd
			if(listenfd.Poll(0, SelectMode.SelectRead)){
				ReadListenfd(listenfd);
			}
			//检查clientfd
			foreach (ClientState s in clients.Values){
				Socket clientfd = s.socket;
				if(clientfd.Poll(0, SelectMode.SelectRead)){
					if(!ReadClientfd(clientfd)){
						break;
					}
				}
			}
			//防止cpu占用过高
			System.Threading.Thread.Sleep(1);
		}
public static void ReadListenfd(Socket listenfd){
		Console.WriteLine("Accept");
		Socket clientfd = listenfd.Accept();
		ClientState state = new ClientState();
		state.socket = clientfd;
		clients.Add(clientfd, state);
	}

先初始化一个Socket类型的监听对象,
然后开始判断当对象有数据可读时,就代表有客户端进来了。
执行ReadListenfd,接收客户端,创建一个ClientState,并把ClientState加入到监听列表当中。
遍历客户端,当有客户端可读时就代表有客户端发送数据了,我们需要给其他客户端发消息

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;
}

这里会先判断客户端有没有关闭,如果没有就返回True并且发送数据,如果有关闭就是False并且要将他逐出列表。
这里因为foreach对列表操作了会报错,所以我们收到False就要及时打断循环。

这里我就想到一个问题,那我发到一半突然发现有人断开了,那后面的人岂不是都收不到了?

最后让程序挂起1毫秒防止卡死。

多路复用

多路复用相较于上面的Poll他更近一步,做到了可以检测多个Socket的状态。
然后一次遍历找出符合条件的Socket修改列表。

while(true)
{
            checkeRead.Clear();
            checkeRead.Add(listenfd);
            foreach (ClientState s in clients.Values)
            {
                checkeRead.Add(s.socket);
            }
            Socket.Select(checkeRead,null,null,1000);
            foreach (var s in checkeRead)
            {
                if (s == listenfd)
                    ReadListenfd(s);
                else
                    ReadClientfd(s);
            }
}

这里主体就是先清空检查列表,然后把监听对象放进列表,然后遍历已经在监听的列表,放进检查列表,
然后Select,最后读取修改后的可Read的列表,如果监听对象可读说明有客户端进来了,如果不是说明有消息了,那么全体广播一下。
最后是这个挂起的1000毫秒,在1秒内如果如果没有任何可读信息,就会把检查列表设为空,然后返回。
我这里断点测试了下可以理解为就是程序被挂起了1000毫秒后再继续执行。

客户端与程序端差不多这里就不再实验了。

今天的学习到这里就结束了,基础打下来,后面就是实战了,后面就有机会再来了~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值