现在已经正在使用此SocketAsyncEventArgs 实例进行异
使用C#socket的模型 socketAyncEventArgs时遇到了不小的问题,
"现在已经正在使用此 SocketAsyncEventArgs 实例进行异步套接字操作"
想必很多同学都遇到此类问题了。即所谓的SocketAsyncEventArgs异步通信方式不能
同时执行ReceiveAsync方法,又执行SendAsync方法
对《深入探析c# Socket》的评论部分,从中可以接触一些知识点和技巧,值得学习。
#1楼 dreamhappy 2010-09-08 18:48
希望博主能写几篇关于
c# .net环境 c/s多线程socket开发,如何及时的释放线程 关闭线程和socket的顺序应该怎样处理的博文,因为我之前socket编程时候客户端和服务器端分别有一个线程和一个socket 往往线程不知道什么时候合理的释放
#2楼 秋色
2010-09-08 19:27
线程的释放,一般是定义开关变量。让线程自己退出。
while(开关)
{
if(??)
{
开关=false;
}
}
#3楼 %admin
2010-09-09 10:36
实际测试了一下,还真出现了楼主描述的问题,我想这问题主要就出在了Buffer池的使用上,Send的时候发送的是 从e.buff
拷贝出来的真实大小的数据,SendAsyn的时候发送的是e.buff 。
楼主还真是细心啊,不过好像实际中要发送数据给客户端的时候不应该在用e.buff了, 此问题有待继续深入
#4楼[楼主] 田志良
2010-09-09 10:37
@dreamhappy
多线程在Socket开发中尤为重要,若处理不当,会严重影响效率,接下来,我会陆续写些这类博文,谢谢大家关注。
#5楼[楼主] 田志良
2010-09-09 10:42
@安度
如果你想使你的Socket服务器非常高效,池是一定用到的,如果不用池,高级消息队列也行,这样才能极大提高并发数和最大连接数。
#6楼 %admin
2010-09-09 10:42
还好SocketAsyncEventArgs 提供了SetBuffer ,遇到这种情况是,不妨在SendAsyn之前 动态的 e.SetBuffer 一下,就没问题了~~
示例:
e.AcceptSocket.Send(data);
System.Threading.Thread.Sleep(1000);
e.SetBuffer(0, data.Length);
if (!e.AcceptSocket.SendAsync(e))
{
Console.WriteLine("asynsend error");
}
#7楼[楼主] 田志良
2010-09-09 10:44
@九九
目前我开发的IM系统正在做压力测试,基本上最大连接数能上到20000,并发能上到3000。你所提的问题平时我也有遇到过,有空大家一起研究研究。
#8楼 %admin
2010-09-09 10:51
其实把下面3处代码关联起来看下就比较容易了解为什么会出现这种情况了,
if (Buffer.SetBuffer(e))
{
if (!e.AcceptSocket.ReceiveAsync(e)) //是否触发 Asyn_Commpleted事件
{
BeginReceive(e);
}
}
这段是接受连接时调用Buffer类的SetBuffer方法,实际上还是操作的SocketAsyncEventArgs.SetBuffer
internal Boolean SetBuffer(SocketAsyncEventArgs args)
{
if (this.freeIndexPool.Count > 0)
{
args.SetBuffer(this.buffer, this.freeIndexPool.Pop(), this.bufferSize);
}
else
{
if ((this.numSize - this.bufferSize) < this.currentIndex)
{
return false;
}
args.SetBuffer(this.buffer, this.currentIndex, this.bufferSize);
this.currentIndex += this.bufferSize;
}
return true;
}
看Buffer类的SetBuffer函数就很清楚了,这个Buffer池与SocketAsyncEventArgs不存在逻辑上的关联,只是外部分配缓冲区然后设置SocketAsyncEventArgs
e.AcceptSocket.Send(data);
System.Threading.Thread.Sleep(1000);
e.SetBuffer(0, data.Length);
if (!e.AcceptSocket.SendAsync(e))
{
Console.WriteLine("asynsend error");
}
到这里,SendAsyn 实际上使用的缓冲区是在Accept时候就设置好了的,所以此时如果不进行e.SetBuffer(0, data.Length); 就出现了,楼主描述的问题,
#9楼[楼主] 田志良
2010-09-09 11:10
@%admin
Buffer池主要用于集中管理SAEA的Buffer缓冲区,防止内存碎片过多影响效率。SetBuffer方法是为SAEA分配内存缓冲区,这个是跟
基础系统缓冲区是有区别的。我们用程序可以管理SAEA内存缓冲区,但不能管理基础系统缓冲区,这个是由基础系统本身的机制决定的。还有微软提供的
SendAsyn方法是鸡肋,一个SAEA在同一时间,不能既处于SendAsync状态又处于ReadAsyc状态,否则会引发"现在已经正在使用此
SocketAsyncEventArgs 实例进行异步套接字操作"异常,这对于多线程、高并发通信毫无用处,显得很苍白无力。
#10楼 %admin
2010-09-09 11:29
引用田志良:
@%admin
Buffer池主要用于集中管理SAEA的Buffer缓冲区,防止内存碎片过多影响效率。SetBuffer方法是为SAEA分配内存缓冲区,这个是跟
基础系统缓冲区是有区别的。我们用程序可以管理SAEA内存缓冲区,但不能管理基础系统缓冲区,这个是由基础系统本身的机制决定的。还有微软提供的
SendAsyn方法是鸡肋,一个SAEA在同一时间,不能既处于SendAsync状态又处于ReadAsyc状态,否则会引发"现在已经正在使用此
SocketAsyncEventArgs 实例进行异步套接字操作"异常,这对于多线程、高并发通信毫无用处,显得很苍白无力。
呵呵,你说的这问题也注意到了,我还是不够深入,对于SAEA还没有了解, 因为自己项目中代码跟你的很相似也就自己测试了下,分析的也不知道对不对,呵呵,继续学习! 去看看SAEA
#11楼 henry
2010-09-09 11:46
其实SocketAsyncEventArgs性能不错的,在新的测试中cpu E5405 的服务器,服务端接收256byte数据并返回给client(有分包处理)其秒处理数据包的能力在2.5W. 而CPU只占用了50%,内存在300M内.
补充:这样的处理方式在秒处理5000消息的时候估计会性能问题产生.
#12楼 阿三
2010-09-09 14:31
小伙子进步不错嘛。
#13楼 无为无知无欲
2010-09-09 16:27
楼主,我刚做了这个.net 3.5 完成端口方法的测试,一台普通的服务器,2G内存,可以并发接受1500条消息/秒,
这个瓶颈主要是从消息队列写进数据库的瓶颈,超过后就会造成消息队列的增长,但前端还是能不断接收数据的。所以如果数据库服务器更高效的话,能力还能大幅
提高。
最高连接我做到了20000,CPU,和内存还没怎么提高,所以应该能更高,问题是测试的时候这么多的客户端不好做啊。
#14楼[楼主] 田志良
2010-09-09 17:16
@无为无知无欲
所以你要做一个SQL池,将要执行的SQL语句放到池中,然后每隔一段时间,安排一条线程扫描SQL池,如果SQL池中有SQL语句,则批量执行,如果没
有则退出。在对SQL池管理时要尤为小心,Push操作和Ececute操作要互斥,执行SQL语句时,不能Push SQL语句,相反,Push
SQL语句时,也不能执行SQL语句。
#15楼 安度
2010-09-09 17:28
我是最近才接触Socket的,貌似SocketAsyncEventArgs我没有在网上看到,大概用的都是BeginXXX和EndXXX,服务端的
话,基本是用多线程(Accpet在一个独立的线程),然后做一个线程池的管理类(貌似.net有现成的线程池),不知道楼主这种方法有什么优点,不防解
释下
#16楼 安度
2010-09-09 17:33
在.NET
3.5里System.Net.Sockets空间下有一组增强功能的类,提供可供专用的高性能套接字应用程序使用的可选异步模
式,SocketAsyncEventArgs
类就是这一组增强功能的一部分。该类专为需要高性能的网络服务器应用程序而设计。应用程序可以完全使用增强的异步模式,也可以仅仅在目标热点区域(例如,
在接收大量数据时)使用此模式。以下是关于此类的介绍(摘自MSDN)
原来是3.5里面的!是不是就是传说中的IOCP?有机会楼主写详细点,原来不知道楼主是用的这个!
#17楼[楼主] 田志良
2010-09-09 18:07
@安度
我的做法是开通300或更多个Socket用于接受侦听Socket传递的SAEA,为什么要开通这么多?你在做压力测试时就明白了,开通多一点,会使你
的连接效率、连接速度大幅度提高。对于SAEA,它不能同时ReceiveAsync、SendAsync,所以采用双工通信,让收发数据在同一条连接上
进行,以提高效率。对于业务逻辑层上的处理,主要采用线程池、SQL池,用SQL池主要将网络层与数据访问层分离,为什么要分离?数据库操作会极大影响效
率,如果不分离,数据操作会拖垮网络层。
#18楼 Leon Weng
2010-09-11 02:07
前段时间搞视频通信时用到了sokect,顺便研究了一下,感觉效率的确比较高,但是在多线程方面自我感觉很差,所以没有使用socket完成改成WCF了,WCF封装了SOCKET,更好用了。
#19楼 ToBin
2011-03-01 11:12
双工通信时需要一个客户端连接两个端口嘛,一个收,一个发?
现在已经正在使用此 SocketAsyncEventArgs 实例进行异步套接字操作。
为什么会出现这个问题呢?
#20楼[楼主] 田志良
2011-03-01 11:30
@ToBin
当一个SAEA对象已处于StartReceive状态时,就不能用此SAEA发送消息。也就是说SAEA对象在一个时刻中只能处于
StartAccept、StartReceive、StartSend状态中的一种。解决的办法就是用双工通信,为一条连接开辟两个SAEA对象,一个
用于收,一个用于发。
#21楼 ToBin
2011-03-01 13:02
刚又把文章仔细读了一遍,很多东西还是没有理解!
刚好看到您的回复!很有帮助,我再研究研究您的文章!
还有这个saea的三种状态,我现在在发送的时候报错"
现在已经正在使用此 SocketAsyncEventArgs 实例进行异步套接字操作"
但我这个确实是clientsocket.sendasync(saeasender)是发生的。
我接受的时候用的clientsocket.receiveasync(saeareceiver),两个没有冲突啊,很奇怪!
#22楼[楼主] 田志良
2011-03-01 14:55
@ToBin
建议Receive用异步模式,Send用同步模式。
#23楼 ToBin
2011-03-01 17:31
错误“现在已经正在使用此 SocketAsyncEventArgs 实例进行异步套接字操作”是不是因为异步发送了就返回了,但实际上还没有发送到,当第二次异步发送的时候,第一次还没发送完,就出了这个错误?
我猜测!
#24楼[楼主] 田志良
2011-03-02 16:51
@ToBin
当Socket处于StartSend状态时,也不能执行发送操作,必须等到发送回调事件触发后,才能继续执行StartSend。解决的办法是:记录
Socket的当前状态,并存储在Socket的UserToken对象下,当要执行StartSend时,判断状态。不过这样效率会很慢,当并发量达到
3000时,会报很多错,推荐的方法是用同步发送。不要觉得同步发送就一定会比异步发送慢,事实证明,对于SocketAsyncEventArgs,同
步发送比异步发送快多了。
#25楼 ToBin
2011-03-02 17:17
拜读了,现在还有一个问题请教,异步的时候是“但异步发送消息的拷贝,是将Socket自带的Buffer空间内的所有数据,拷贝到基础系统发送缓冲区,
并立即返回”这个基础系统缓冲区是对应winsocket的,有点疑惑,就是只有一个缓存区,接受也从基础系统缓冲区中拷贝处来,发送也是拷贝到这,如果
我接收的时候同时发送,基础系统缓冲区里的数据还没取出来,将要取出来的时候发送,拷贝进去,会不是导致接收出来的数据不正确?导致脏读,数据错误!
有这样的问题嘛?
#26楼[楼主] 田志良
2011-03-02 18:04
@ToBin
不会导致这个问题,基础系统缓冲区为每个Socket分配发送缓冲区和接受缓冲区,这两个不冲突。
#27楼 ToBin
2011-03-02 18:33
要西,外瑞thank you !哈哈
#28楼[楼主] 田志良
2011-03-02 18:45
@ToBin
呵呵,不客气。
#29楼 ToBin
2011-03-03 17:02
呵呵,又碰到问题了,可能我太笨了,您在这篇文章“Socket服务器整体架构概述”中说到一个“消息队列调度器”,这个东西该怎么实现,能大概说说嘛?
谢谢了!呵呵
#30楼[楼主] 田志良
2011-03-04 09:03
@ToBin
呵呵,把你的邮箱发给我,这个周末我写个Demo给你。
#31楼 ToBin
2011-03-04 09:26
太感谢了,激动!我的邮箱:tuablove@126.com
辛苦您了!
#32楼 ToBin
2011-03-07 10:07
您的邮件已经收到,思路已经了解,非常感谢能得到您的帮助,希望能继续得到您的帮助,思路也行,呵呵,非常感谢!
#33楼 ToBin
2011-03-08 17:26
又碰到个问题,不知道怎么处理了,问题是这样的
entityData data=getdata();
public void getdata()
{
var data=null;
//...
socket.sendasync()
//...
data=socket.receivesync();
return data;
}
getdata方法是socket 发送命令,接收返回值的方法,因为socket 服务器发送,接受时分开的,我怎么在getdata中让方法阻塞,让服务器接收到命令,然后再把结果发送过来!类似于javascript 的ajax!这种东西该怎么写啊?
类似于使用memcache 里
MemcachedClient mc = new MemcachedClient();
mc.Get("key");
这里面这个mc.Get("key") 方法是怎么实现的啊 ?
#34楼 ToBin
2011-03-09 21:02
志良哥,您好,我在socket 开发中碰到了一些问题,想请教您,已经发送您邮箱了,思路解说在邮件里,代码在附件!等待您的回复!
#35楼 edwardxh
2011-11-16 09:33
引用田志良:
@ToBin
呵呵,把你的邮箱发给我,这个周末我写个Demo给你。
楼主您好,不知您是否可以把这个“Socket服务器整体架构概述”Demo也发给我一份,因为我最近也在研究Socket通信,很希望能得到您的技术心得分享,谢谢!
我的邮箱:79668157@qq.com
#36楼 glf
2011-11-23 17:19
现在公司要求能够接受2W左右的服务端,每5秒访问一次,高能不能给点意见啊
怎么样,目前采用了异步接收,同步发送的方法,暂时解决问题。
浙公网安备 33010602011771号