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

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

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

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

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

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

         

循环监听
try 

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

catch( Exception ex ) 

   Console.WriteLine ( ex.Message ); 

 

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

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

      

开始异步监听
   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函数
        }

 

 

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

 

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

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

   

处理客户端新连接
     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是比较消耗资源的。

  

对各种异常进行处理
  /// <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);
            }
        }

 

   

处理接收到的数据函数
  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的嫁妆

posted @ 2010-04-25 16:41  王传炜  阅读(3243)  评论(15编辑  收藏  举报