zoukankan      html  css  js  c++  java
  • 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的嫁妆

  • 相关阅读:
    数据库字典
    目录结构-模板目录
    vuex
    innerHTML,innertext,textContent区别
    组件传值
    深拷贝和浅拷贝
    关于element ui头像上传问题
    常用的字符串截取的方法
    接口报错之number值过大问题
    node_modules?
  • 原文地址:https://www.cnblogs.com/wangergo/p/1719777.html
Copyright © 2011-2022 走看看