读书人

.net3.5上的Socket通信框架

发布时间: 2012-10-08 19:54:56 作者: rapoo

.net3.5下的Socket通信框架

?

首先声明这个源代码工程地址是http://www.codeproject.com/KB/IP/socketasynceventargssampl.aspx

其中用到的协议是http://www.cnblogs.com/JimmyZhang/archive/2008/09/16/1291854.html

简单说说各个类的作用

BufferManager:缓冲区

RequestHandler:协议

SocketAsyncEventArgsPool:事件池{可重复使用的套接字对象}

SocketClient:客户端

SocketListener:服务端

再上通信流程图

.net3.5上的Socket通信框架??

接下来就是本文的重点了,看看这些代码是如何工作的。

对于服务端

1:初始化一些对象,初始化的对象包括事件池和缓冲区。?

2:阻塞端口开始侦听客户端的链接。如果侦听到就readEventArgs.UserToken = e.AcceptSocket,因为我们在初始化事件池的时候只是简单的创建了SocketAsyncEventArgs对象并准备开始接收数据。在得到UserToken后就可以接收数据了。这里需要注意的是侦听到后不会等待接收过程结束才开始下一次侦听而是立即开始侦听{前提是如果事件池中有空闲对象的话}

3:接收过程。网上的例子由于没有考虑到协议所以客户端发送的数据可能会被截断。这里我们加上协议这一部分

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->private?void?ProcessReceive(SocketAsyncEventArgs?e)?{
????????????//客户端是否关闭连接
????????????if?(e.BytesTransferred?>?0)?{
????????????????if?(e.SocketError?==?SocketError.Success)?{
????????????????????Socket?s?=?e.UserToken?as?Socket;

????????????????????Int32?bytesTransferred?=?e.BytesTransferred;
????????????????????//获取数据????????????????????
????????????????????String?received?=?Encoding.ASCII.GetString(e.Buffer,?e.Offset,?bytesTransferred);

????????????????????//增加接收字节总量
????????????????????Interlocked.Add(ref?this.totalBytesRead,?bytesTransferred);
????????????????????//Console.WriteLine("Received:?\"{0}\".?服务共接受{1}字节.",?received,?this.totalBytesRead);

????????????????????
????????????????????//Byte[]?sendBuffer?=?Encoding.ASCII.GetBytes(received);

????????????????????//?获取实际的字符串
????????????????????string[]?msgArray?=?handler.GetActualString(received);
????????????????????//?清空缓存,避免脏读
????????????????????Array.Clear(e.Buffer,?e.Offset,?bytesTransferred);????????

????????????????????
????????????????????Boolean?willRaiseEvent?=?false;

????????????????????//如果接收到完整的消息则发送回客户端否则继续接受
????????????????????foreach?(string?m?in?msgArray)?{
????????????????????????byte[]?temp?=?Encoding.ASCII.GetBytes(m);
????????????????????????e.SetBuffer(temp,?0,?temp.Length);
????????????????????????willRaiseEvent?=?s.SendAsync(e);
????????????????????}

????????????????????if?(!willRaiseEvent)?{
????????????????????????this.ProcessSend(e);
????????????????????}

????????????????}
????????????????else?{
????????????????????this.CloseClientSocket(e);
????????????????}
????????????}

?

4:接收完成的时候我们把SocketAsyncEventArgs对象放回事件池

服务端简单说两点。第一就是阻塞和就收都是异步的互不关联的,有的同学可能会这样

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->???????private?void?StartAccept()?{
???????????SocketAsyncEventArgs?accept?=?this.readWritePool.Pop();
???????????this.semaphoreAcceptedClients.WaitOne();
???????????listenSocket.AcceptAsync(accept);
???????}

????????void?accept_Completed(object?sender,?SocketAsyncEventArgs?e)
????????{

???????????StartAccept();

????????????var?client?=?e.AcceptSocket;
????????????e.UserToken?=?e.AcceptSocket;

????????????e.Completed?-=?accept_Completed;
????????????e.Completed?+=?receive_Completed;

????????????var?buffer?=?new?byte[1024];
????????????e.SetBuffer(buffer,?0,?buffer.Length);

????????????client.ReceiveAsync(e);

?

这样写也行不过没有从代码级别把阻塞和接受分开。 我说的第二点是

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->????????????????????Boolean?willRaiseEvent?=?false;

????????????????????//如果接收到完整的消息则发送回客户端否则继续接受
????????????????????foreach?(string?m?in?msgArray)?{
????????????????????????byte[]?temp?=?Encoding.ASCII.GetBytes(m);
????????????????????????e.SetBuffer(temp,?0,?temp.Length);
????????????????????????willRaiseEvent?=?s.SendAsync(e);
????????????????????}

????????????????????if?(!willRaiseEvent)?{
????????????????????????this.ProcessSend(e);

?

也有同学这样写

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->????????????????????Byte[]?sendBuffer?=?Encoding.Unicode.GetBytes(received);

????????????????????s.Send(sendBuffer,?sendBuffer.Length,?SocketFlags.None);
????????????????????s.ReceiveAsync(e);?? ? ? ? ? ? ? ? ?
?? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ?

?

这样写的话就变成同步的了。效率不高{没有测试}?

对于客户端

1:异步连接服务器并用autoConnectEvent.WaitOne();等待连接完成。

2:发送数据

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->public?void?Send(String?message)?{
????????????if?(this.connected)?{

????????????????//将信息转化为协议
????????????????message?=?String.Format("[length={0}]{1}",?message.Length,?message);
????????????????Byte[]?sendBuffer?=?Encoding.ASCII.GetBytes(message);

????????????????SocketAsyncEventArgs?completeArgs?=?new?SocketAsyncEventArgs();
????????????????completeArgs.SetBuffer(sendBuffer,?0,?sendBuffer.Length);
????????????????completeArgs.UserToken?=?this.clientSocket;
????????????????completeArgs.RemoteEndPoint?=?this.hostEndPoint;
????????????????completeArgs.Completed?+=?new?EventHandler<SocketAsyncEventArgs>(OnSend);

????????????????clientSocket.SendAsync(completeArgs);

????????????????//等待本次发送接收时间{不必完成仅开始就好}
????????????????AutoResetEvent.WaitAll(autoSendReceiveEvents);

????????????????//return?Encoding.ASCII.GetString(completeArgs.Buffer,?completeArgs.Offset,?completeArgs.BytesTransferred);
????????????}
????????????else?{
????????????????throw?new?SocketException((Int32)SocketError.NotConnected);
????????????}

?

这里我们把自己的协议加上。这里我把发送端改了一下,因为我们要在外部获取返回的数据。

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->????????private?void?OnReceive(object?sender,?SocketAsyncEventArgs?e)?{
????????????string?msg?=?Encoding.ASCII.GetString(e.Buffer,?0,?e.BytesTransferred);
????????????recInfo(msg);
????????????autoSendReceiveEvents[SendOperation].Set();

?

这里需要说的是AutoResetEvent.WaitAll(autoSendReceiveEvents)就是我们在发送完和启动接收后就开始下次发送而不必等接受完成。

最后送上客户端调用代码{服务端的没有改变}。?

Code
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->????????public?static?void?Main(string[]?args)?{
????????????try?{
????????????????String?host?=?"192.168.70.100";
????????????????Int32?port?=?1232;
????????????????Int16?iterations?=?10;

????????????????using?(SocketClient?sa?=?new?SocketClient(host,?port))?{
????????????????????sa.Connect();
????????????????????sa.recInfo?+=?new?SocketClient.GetReceive(InfoChange);

????????????????????for?(Int32?i?=?0;?i?<?iterations;?i++)?{
????????????????????????if?(i?%?2?==?0)
????????????????????????????sa.Send("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
????????????????????????else
????????????????????????????sa.Send("222");
????????????????????}
????????????????????sa.Disconnect();

????????????????????Console.WriteLine("Press?any?key?to?terminate?the?client?process.net3.5上的Socket通信框架");
????????????????????Console.Read();
????????????????}
????????????}
????????????catch?(IndexOutOfRangeException)?{
????????????????Console.WriteLine("Usage:?SocketAsyncClient?<host>?<port>?[iterations]");
????????????}
????????????catch?(FormatException)?{
????????????????Console.WriteLine("Usage:?SocketAsyncClient?<host>?<port>?[iterations]."?+
????????????????????"\r\n\t<host>?Name?of?the?host?to?connect."?+
????????????????????"\r\n\t<port>?Numeric?value?for?the?host?listening?TCP?port."?+
????????????????????"\r\n\t[iterations]?Number?of?iterations?to?the?host.");
????????????}
????????????catch?(Exception?ex)?{
????????????????Console.WriteLine("ERROR:?"?+?ex.Message);
????????????}
????????}

????????static?void?InfoChange(string?info)?{
????????????Console.WriteLine("收到信息:{0}",?info);

?

写在最后

本来想放上代码的。不过由于代码大部分来自于网上放上来有点。。。

如果哪位同学想要我调试过的就留个邮箱,我挨个给你们发。?

考虑到要的同学太多就放上源代码

源代码?

读书人网 >Web前端

热点推荐