代码改变世界

一封给“X教授”的回信(讨论Socket通信)

2011-03-02 18:44  田志良  阅读(5542)  评论(6编辑  收藏  举报

  前几天“X教授”发Email与我讨论Socket通信方面的问题,主要涉及4个方面内容,现将回信公布出来,希望园友们能积极参与讨论,提出更好的解决方案。

  下面是“X教授”的来信:

===========================================================

  您好,我看了您写的几篇博文,想请教你几个.net Socket通信方面的问题,这些问题以前一直困扰着我。 
  1、按照你深入探析C# Socket的文中的说法。 
  对于Socket的同步操作来说,Socket的RceiveBuffer和SendBuffer没有用处,因我发送时是从应用程序缓存(这里指字节数据)直接拷贝到基础系统发送缓冲区,接收时是从基础系统接收缓冲区拷贝到应用程序的缓存。 
  Socket本身的接收缓存与发送缓存,是否只与异步Socket操作有关。在异步操作中又分为两种,一种是以BeginXXX系列操作,以BeginSend为例,对这种操作我的理解是,从线程池中取一个线程,把数据拷贝到Socket的自带的SendBuffer中,调用BenginSend的主线程然后立刻返回,该发送线程挂起,由操作系统把SendBuffer中的数据拷贝到基础系统缓冲区,发送完成后,挂起的线程激活,调用EndSend进行相应的操作,释放异步对象等资源。 
以上我的理解是否正确。 
  2、除XXXAsync系列操作外, BeginXXX系统的Socket操作是否也是用的完成端口机制。 
  3、.net与C/C++写的结构体通信。目前我熟悉的就两种: 
  第一种是了MarShal类进行结构体的转换操作,但托管与非拖管之间交互时的封送开销,使得其效率不高。 
  第二种是用流的方式去解析数据包。然后按包结构,一个字段一个字段的去读取。比如比流出读取一个浮点型,binr.ReadSingle();此种效率还可以。接收与解析好几M的数据没有问题。 
  我想问一下,有没有效率更高的方法。 
  4、服务器给客户端转发数据,当客户端接收包的地方堵塞(可能是socket.Receive),有没有什么好的方案,让服务器自动丢包。

=========================================================== 

  下面是我的回信:

=========================================================== 

X教授,您好: 
  非常抱歉,最近比较忙,耽误给您回复了。对于您提出的几个问题,我只能作浅层次的回答,更深的研究,我们以后慢慢探讨,共同学习。 
  第一个问题: 
  你的理解是正确的,对于同步发送,是直接拷贝发送数据到基础系统的发送缓冲区,与Socket本身的Buffer无关。对于异步发送,是将Socket自带的Buffer空间内的所有数据,拷贝到基础系统发送缓冲区,并立即返回。 
  在实际开发中,个人觉得采用异步接受、同步发送。也许你会感到很惊讶,很明显,异步操作明显比同步操作效率高很多,为什么发送要采用同步呢?不知道你在开发过程中有没有遇到过“现在已经正在使用此 SocketAsyncEventArgs 实例进行异步套接字操作”错误,这个错误是由于SAEA对象在一个时刻中只能处于StartAccept、StartReceive、StartSend状态中的一种,试想,当Socket服务器处于高压力情形下,一条Socket连接在很短的时间内要发送大量数据,当Socket处于StartSend状态下尚未触发回调函数时,又一次调用此Socket的StartSend方法,便会抛出该异常。这也有改进方案:记录Socket的当前状态,并存储在Socket的UserToken对象下,当要执行StartSend时,判断状态。不过这样效率会很慢,远远低于同步发送的效率,当高并发时,还要用大量的锁机制来维护线程的同步,得不偿失。不知道你有没有更好的改进方案,如果有,请告知我,不胜感激。 
  第二个问题: 
  BeginXXX系统的Socket操作采用的是完成端口机制,在初始化SAEA对象时,会为SAEA对象设定回调函数。 
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs(); 
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed); 
  第三个问题: 
  建议使用流的方式解析数据包。 
  在解析数据包的过程中,不知道您有没有遇到过粘包的现象,我觉得对粘包的处理是影响解析效率的关键,请参见博文:http://www.cnblogs.com/tianzhiliang/archive/2010/08/31/1813659.html。 
  第四个问题: 
  我觉得采用“心跳”是一个不错的解决方案。 
  “心跳”分为两种,第一种是客户端发起的心跳,第二种是服务端发起的心跳。 
  客户端发起的心跳:客户端每隔一段时间发送策略消息给Socket服务器,Socket服务器原路返回策略消息,如果客户端在设定时间段内没有收到Socket服务器的返回消息,经重试机制后,判定Socket服务器已Down,关闭连接。 
  服务端发起的心跳:服务端实时记录每条Socket的IO操作时间,每隔一段时间获取所有Socket列表的快照,扫描每条Socket,如果该Socket的IO操作时间距当前时间已超出设定值,则判定客户端Down,关闭连接。

===========================================================