Silverlight MMORPG WebGame游戏设计(四)-----Client:Server!房子啥时候装好?我急嫁人啊!...

本文深入探讨了使用异步Socket编程优化服务器端数据接收的过程,通过对比传统的死循环监听方式,展示了异步回调函数如何提高效率并减少资源消耗。文章详细解释了如何利用BeginAccept和EndAccept方法处理客户端连接,以及如何在不使用死循环和try-catch的情况下处理数据接收和异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

          上周有点杂事这篇文章就耽搁下来了。还有上篇文章涉及到我所在的“深蓝WPF/Silverlight群”里的“开心”的代码版权问题,去年我在网上搜到silverlight服务端的源代码,以此基础写了web传奇的服务端。由于不知道是“开心”的源码,还由于里面的bug,虽然我尽我的能力做了修正,还是有处明显的bug没有修正,所以我重写了服务端的代码,特在次感谢“开心”的开源精神,他鼓励我多写下文章。

          废话不说了,我们言归正传了。上次说到client急着住新房和server百年好合,但好事多磨,这Server的房子装修不是一天两天,Client的嫁妆也不是很容易就弄好的,谁让这是个物质的社会呢!

          Server就找来了装修包工头Socket,说:“你看我这毛坯房也拿到手了,两个仆人也雇来了,我们什么时候开工啊?”Socket说:“有钱好办事,你买本winSocket高级编程贿赂我下。”Server囧了:“房子首付都七姑八爷得借个遍,装修也是买血弄点钱,你就行行好,开工吧。”

         Socket被磨得不行了,就开工了。Socket找了个徒弟叫做listener,让他负责房子装修的事.

         这个listener不像以前的老工人,有事没事就跑个死循环,日复一日低效干活。

         

ExpandedBlockStart.gif 循环监听
try  

    TcpListener listener 
=   new  TcpListener(nPortListen ); 
    listener.Start(); 
    do  
   { 
       byte  [] myBuff  =   new   byte [ 128 ]; 
       if ( listener.Pending() ) 
       { 
            client 
=  listener.AcceptSocket(); 
            ......
       } 
       else  
      { 
            Thread.Sleep( 
100  ); 
      } 
   } 
while true  );  

catch ( Exception ex ) 

   Console.WriteLine ( ex.Message ); 

 

       大家可以看到以前的老工人只要没接到指令(!listener.Pending()),就去睡懒觉(Thread.Sleep( 100 ); ),搞得雇主火冒三丈,这一睡一醒多费事啊,耽搁时间,还搞得身心疲惫,处于假死状态。

       这个新的工人listener比较能接受新事物,听说了有异步这个事。觉得很好,我不用全年无休,我动态上班,有活我立马干,没活我就做其他的事。

      

ExpandedBlockStart.gif 开始异步监听
    private   void  runServer()
        {
            listener 
=   new  Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            listener.Bind(
new  IPEndPoint(IPAddress.Any, Sport)); 
            listener.Listen(
100 );
            listener.BeginAccept(OnConnectRequest, listener);//当有客户端连接进入时,开始调用OnConnectRequest函数
        }

 

 

ExpandedBlockStart.gif 当有新连接时
    public   void  OnConnectRequest(IAsyncResult ar)
        {
            Socket listener 
=  (Socket)ar.AsyncState;
            NewConnection(listener.EndAccept(ar));//接受这个连接发送的数据
            listener.BeginAccept(
new  AsyncCallback(OnConnectRequest), listener);//数据接收完毕后,继续异步监听,等待下一次连接
        }

 

       大家仔细看OnConnectRequest函数,会发现他取代了do while 死循环,在函数最后又异步回调了自己,这样就保持了连续接收客户端连接的状态。

      接收每一个客户端传来的数据在NewConnection函数里.

   

ExpandedBlockStart.gif 处理客户端新连接
      public   void  NewConnection(Socket clientSock)
        {
            SetupRecieveCallback(clientSock);//接收到客户端数据时回调函数
            ServerMessage ClientMessage 
=   new  ServerMessage();
            ClientMessage.Socket 
=  clientSock;
            ui.Post(
this .uiDisPlay.userEnter, ClientMessage);//通知界面线程有客户端连入

        }
     
        public void SetupRecieveCallback(Socket sock)
        {
                AsyncCallback recieveData = new AsyncCallback(OnRecievedData);//收到数据时回调数据处理函数
                sock.BeginReceive(listenerMess.Buffer, 0, listenerMess.Buffer.Length, SocketFlags.None, out socketerror, recieveData, sock);//把接收到的数据预先分配给服务端byte[]来储存
                CatchWithSocketError(socketerror, listenerMess);//处理接收数据时各种异常
        }

 

   小tips:这里没有用try catch处理异常,而是用的错误号来处理,这里的socketerror是输出参数,带出各种错误号。这样的好处是对错误号进行处理能比try catch减少系统资源消耗,大家都知道try catch是比较消耗资源的。

  

ExpandedBlockStart.gif 对各种异常进行处理
   ///   <summary>
        
///  根据错误号作出处理
        
///   </summary>
        
///   <param name="error"></param>
        
///   <param name="client"></param>
         private   void  CatchWithSocketError(SocketError error, ServerMessage client)
        {
            
#region  各种错误号
            
// AccessDenied已试图通过被其访问权限禁止的方式访问 Socket。 
            
// ConnectionAborted此连接由 .NET Framework 或基础套接字提供程序中止。 
            
//  Disconnecting正常关机正在进行中。
            
// Fault 基础套接字提供程序检测到无效的指针地址。  
             
// HostDown 由于远程主机被关闭,操作失败。  
             
// HostNotFound 无法识别这种主机。该名称不是正式的主机名或别名。  
             
// HostUnreachable 没有到指定主机的网络路由。  
             
// InProgress 阻止操作正在进行中。  
             
// Interrupted 已取消阻止 Socket 调用的操作。  
             
// InvalidArgument 给 Socket 成员提供了一个无效参数。  
             
// IOPending 应用程序已启动一个无法立即完成的重叠操作。  
             
// IsConnected Socket 已连接。  
             
// MessageSize 数据报太长。  
             
// NetworkDown 网络不可用。  
             
// NetworkReset 应用程序试图在已超时的连接上设置 KeepAlive。  
             
// NetworkUnreachable 不存在到远程主机的路由。  
             
// NoBufferSpaceAvailable 没有可用于 Socket 操作的可用缓冲区空间。  
             
// NoData 在名称服务器上找不到请求的名称或 IP 地址。  
             
// NoRecovery 错误不可恢复或找不到请求的数据库。  
             
// NotConnected 应用程序试图发送或接收数据,但是 Socket 未连接。  
             
// NotInitialized 尚未初始化基础套接字提供程序。  
             
// NotSocket 对非套接字尝试 Socket 操作。  
             
// OperationAborted 由于 Socket 已关闭,重叠的操作被中止。  
             
// OperationNotSupported 协议族不支持地址族。  
             
// ProcessLimit 正在使用基础套接字提供程序的进程过多。  
             
// ProtocolFamilyNotSupported 未实现或未配置协议族。  
             
// ProtocolNotSupported 未实现或未配置协议。  
             
// ProtocolOption 对 Socket 使用了未知、无效或不受支持的选项或级别。  
             
// ProtocolType 此 Socket 的协议类型不正确。  
             
// Shutdown 发送或接收数据的请求未得到允许,因为 Socket 已被关闭。  
             
// SocketError 发生了未指定的 Socket 错误。  
             
// SocketNotSupported 在此地址族中不存在对指定的套接字类型的支持。  
             
// Success Socket 操作成功。  
             
// SystemNotReady 网络子系统不可用。  
             
// TimedOut 连接尝试超时,或者连接的主机没有响应。  
             
// TooManyOpenSockets 基础套接字提供程序中打开的套接字太多。  
             
// TryAgain 无法解析主机名。请稍后重试。  
             
// TypeNotFound 未找到指定的类。  
             
// VersionNotSupported 基础套接字提供程序的版本超出范围。  
            
// WouldBlock 对非阻止性套接字的操作不能立即完成。 
             #endregion

            
if  (error  ==  SocketError.Disconnecting  ||  error  ==  SocketError.Fault  ||  error  ==  SocketError.IsConnected  ||  error  ==  SocketError.SocketError)
            {
                client.Socket.Close();
                ui.Post(
this .uiDisPlay.delUser, client);
            }
            
if  (error  ==  SocketError.MessageSize  ||  error  ==  SocketError.NotConnected  ||  error  ==  SocketError.ProcessLimit  ||  error  ==  SocketError.TooManyOpenSockets)
            {
                client.Socket.Close();
                ui.Post(
this .uiDisPlay.delUser, client);
            }
        }

 

   

ExpandedBlockStart.gif 处理接收到的数据函数
   public   void  OnRecievedData(IAsyncResult ar)
        {
            Socket sock 
=  (Socket)ar.AsyncState;
            listenerMess.Socket 
=  sock;
            
int  nBytesRec  =  sock.EndReceive(ar, out  socketerror);
            CatchWithSocketError(socketerror, listenerMess);
            
if  (nBytesRec  >   0 )
            {
                listenerMess.Stream.Write(listenerMess.Buffer, 
0 , nBytesRec);//把服务端byte[]中接收到的数据交给Stream类的write方法,写入到Stream类的buffer byte[]中。
                //...对接收到的数据进行分析处理
                SetupRecieveCallback(sock);//继续异步等待客户端发送过来的数据
            }
            
else
            {
                sock.Shutdown(SocketShutdown.Both);
                sock.Close();
            }
        }

 

      以上代码没有用一个 do while 循环,也没有用一个try catch,就靠异步回调函数和错误号就完成了服务端接收数据的处理,listener干完这些活后,很得意,“知识就是力量啊!”。

       listener还意犹未尽,换了一副画来纪念一下:

      

 

         Server家装修进展还不错,client那里的嫁妆准备得如何呢,下一篇文章:

         Silverlight MMORG WebGame游戏设计(五)-----Client的嫁妆

转载于:https://www.cnblogs.com/wangergo/archive/2010/04/25/1719777.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值