.net3.5下的Socket通信框架

1:前言

写博客时间不短了但是一直不知道怎么开头。索性就开门见山吧。

这篇文章主要给大家介绍.net3.5下的Socket通信,主要从事件池,缓冲区以及协议三个方面给大家阐述。最后附上个我调试通过的项目。怎么说那?还是来个目录吧

A:通信框架图

B:通信流程图

C:简单介绍

D:源代码

先上通信框架图

 

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

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

简单说说各个类的作用

BufferManager:缓冲区

RequestHandler:协议

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

SocketClient:客户端

SocketListener:服务端

再上通信流程图

  

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

对于服务端

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

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

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

ContractedBlock.gif ExpandedBlockStart.gif Code
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对象放回事件池

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

ContractedBlock.gif ExpandedBlockStart.gif Code
       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);

 

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

ContractedBlock.gif ExpandedBlockStart.gif Code
                    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);

 

也有同学这样写

ContractedBlock.gif ExpandedBlockStart.gif Code
                    Byte[] sendBuffer = Encoding.Unicode.GetBytes(received);

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

 

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

对于客户端

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

2:发送数据

ContractedBlock.gif ExpandedBlockStart.gif Code
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);
            }

 

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

ContractedBlock.gif ExpandedBlockStart.gif Code
        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)就是我们在发送完和启动接收后就开始下次发送而不必等接受完成。

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

ContractedBlock.gif ExpandedBlockStart.gif Code
        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");
                    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);

 

写在最后

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

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

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

源代码 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
感谢大家对北风之神SOCKET框架的支持。鼓励。下面是北风之神 3.1的更新内容: 修正BUG: 1.ZYSocketSuper 读取 配置文件的最大连接数 读错问题。 2.ZYSocketSuper 无法断开客户端的问题。 3.BuffList 数据包解析丢失问题。 4.例1,例2.客户端断开忘记释放调用SOCKET.CLOSE()的问题 新增功能 1.添加了一个ReadBytes 构造函数,此函数实现了在数据包在读取前需要回调的方法传入。(可以用来解密,解压缩 等功能) 2.添加了一个BufferFormat 类的构造,此函数实现了在数据包在生成前需要回调的方法传入。(可以用来加密,压缩 等功能) 3.添加了BufferFormat.FormatFCA(object o,FDataExtraHandle dataExtra)静态方法。可以用来在类格式化成数据包的时候进行加密压缩等功能 4.添加了ZYSocket.Security 命名空间,里面有传统的AES,DES算法的加解密类 5.添加了ZYSocket.Compression命名空间,里面有通过Deflate算法压缩类 6.开发了ReadBytes.Data属性,为ReadBytes里面的BYTE[]对象。值得注意的是 ReadBytes.Length为数据包原始长度,如果要得到解压缩后的数据包长度,请访问ReadByte.Data.length 新增代码 加解密实例测试 项目:演示了 AES DES 以及Deflate 的使用方法。 例3 - 例2的加密版 项目:就是讲例2通过DES 加密进行通讯的例子 连接测试工具 项目:很多朋友问我要连接数量测试工具。我一起的真的丢了。找不到了。所以重新写了一个 例4 项目:好多人让我写一个发送文件的例子,现在能如愿以偿了 by luyikk@126.com BLOG:http://blog.csdn.net/luyikk QQ:547386448
.net 稳定 高效 易用 可同步 TCP 通信框架 使用平台: WinXP,WIN7,WIN8,WINCE,WINPHONE。 使用.net 2.0 框架。 主要功能介绍: 1、可以代替 Oracle,Mysql客户端 在不安装Oracle,MySql客户端的情况下访问, 对数据库进行间接访问(需开始框架的服务器端)。 2、可以使本来没有网经功能的Sqlite具有网络访问的能力。(也是需要开启服务器端) 以上两点可以兼容现有代码生成器时,客户端代码仅需要特别小的改动就可以。 3、基本功能。可以实现聊天,传文件,图片。 4、使用长连接,有断线自动连接功能,心跳包。 5、使用自定义数据包协议,自建Session机制加强数据连接安全。 6、框架稳定,支持高并发。 7、简单的事件处理机制。使用更加简单。 8、支持同步处理,使程序的开发更架简单,不需要另行回调处理。 下载地址: 使用方式: 首选需要 引用 DataUtils.v1.1.dll。DataUtils 内包含客户端与服务器端 处理类。 1、服务器端 代码示例。 设置服务器端默认端口 ,不设置端口会使用默认端口 TcpSettings.DefultPort = 8511; 既可以使用静态默认对象,也可以创建服务器端对象。 SocketListener server= new SocketListener(); 对象创建后 注册一些事件,以接收客户端发送的信息。 SocketListener.Server.RegeditSession += new Feng.Net.Tcp.SocketListener.RegeditSessionEventHandler(server_RegeditSession); RegeditSession 事件用于是否允许客户端连接此服务器。可以使用用户名,密码的核对方式。 SocketListener.Server.DataReceive += new SocketListener.DataReceiveEventHandler(server_DataReceive); DataReceive 在这个事件里处理接收到的数据。 事件注册完成就可以打开监听 SocketListener.Server.StartListening(); 2、客户端 代码示例 设置服务器的IP地址 TcpSettings.DeafultIPAddress = "192.168.1.3"; TcpSettings.DefultPort = 8511;//不设置端口会使用默认端口。 这样就可以使用默认的静态客户端了。 也可以自己创建对象。 客户端创建后需要在Connected事件注册用户,以限制某些用户是否可以使此链接。用户来源可以是数据库等。 void client_Connected(object sender, SocketClient sh) { Client.RegeditSession("aaa", "bbb"); } 发送文字消息给其他用户 SocketClient.Client.SendToOtherUser(string user, string text); //USER代表发达的目白用户,text表示为发送的内容。 发送图片,音频,视屏可以使用 SocketClient..SendToOtherUser(string user, byte[] data)////USER代表发达的目白用户,data表示为发送的内容。 data数据中数据有多种类型时可以使用 using (Feng.IO.BufferWriter bw = new Feng.IO.BufferWriter()) { bw.WriteBitmap(new Bitmap(100, 100)); bw.Write(text);

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值