上个项目虽然用Unity开发,但是客户端的网络库却是以前项目用C写的一套,新项目不想客户端使用lua了,主要原因是:
- 开发速度慢
- 不易调试
所以C模块的存在也就没有必要了,花了一天时间,用C#的Socket重写了一遍
。
(Unity版本 4.6.7,操作系统 MacOSX)
.Net源码
的 Socket
Unity Mono源码
的 Socket
使用非阻塞(non-blocking)的Socket,而非异步操作(asynchronous operation)
.Net Socket,其异步操作接口的实现使用了线程池和完成端口。
Mono Socket,其异步操作接口实现使用了线程池。
对比两种Sokcet封装,我比较喜欢非阻塞的,首先利用了系统的异步特性,而非应用层拿多线程模拟的,其次是对C API的简单封装,
封装越简单,代码越稳定。
Mono的 Socket.Connected 实现有问题
当发现这个问题时,首先我看的是.Net Socket,因为当时Mono代码还在下载中。
MS .Net实现
Mono 实现
对比以上代码可以得出,Mono版本没有针对非阻塞的Socket执行Poll进行再次判断,.Net的Poll只是对select的简单封装,
于是尝试直接执行 Poll(0, SelectMode.SelectWrite) 来判断Connect是否成功,结果发现Poll(0, SelectMode.SelectWrite)
在非阻塞Socket无法Connect的时候依旧返回true, 于是查看
Mono Socket的Poll函数
对比.Net版本,Mono版本有几个不同点:
- Mono版本是在Poll函数中更新了connected的状态,也就是说,如果想查询非阻塞的Socket是否connected,
.Net版本执行 Socket.Connected 即可,Mono版本每次执行前,要先执行 Socket.Poll(…)
- Poll函数返回值的含义不同,当用于判断非阻塞Socket是否Connect成功时,.Net Poll返回true时,即代表Connect成功,但Mono版本需要再判断GetSocketOption(…)
- Poll的实现不同,.Net的Poll只是对select的简单封装,但是Mono的实现是poll或者select
这里应当是 Unity的Mono
出现了bug,对照 Mono官方最新版
2016.03.01补充
因为Dns.GetHostEntry解析太慢,改用了 public void Connect (string host, int port)
接口,发现还是慢,不过这次慢在了 Poll (-1, SelectMode.SelectWrite)
, 也就是说,对于非阻塞的Socket在Connect时,阻塞等待了。详细代码如下:
再尝试以前使用的接口 public void Connect (IPAddress address, int port)
,发现它是非阻塞的,其代码如下,但我没有找到 public void Connect (IPEndPoint)
的实现。
由此可以推断出为什么存在 上一段说的 Socket.Connected
的实现问题。
发送队列
以前Send其实是阻塞的,Send失败了,循环继续Send,这次增加了发送队列,虽然可能效率上降低了,但也算用对了吧。
以前的问题记录:当send错误码为EAGAIN时
功能性扩展
Socket存在断开但是应用层需要一段时间才能到的问题,以前都是放在逻辑层发Ping包来解决这个问题,想想还是放在这个类中扩展了吧。
解决dns解析慢的问题
Dns.GetHostEntry 函数执行很慢,可以考虑使用 DnsPod提供的DNS解析服务
,用起来还是蛮简单的,我的 httpdns的简单实现
本文链接, 未经许可,禁止转载