现在已经正在使用此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秒访问一次,高能不能给点意见啊

怎么样,目前采用了异步接收,同步发送的方法,暂时解决问题。

posted @ 2015-01-16 13:15  Net-Spider  阅读(2221)  评论(0)    收藏  举报