array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(9) "308660876" ["text"]=> string(45) "安全测试前置实践1-白盒&黑盒扫描" ["intro"]=> string(411) "本文我们将以围绕系统安全质量提升为目标,讲述在安全前置扫描上实践开展过程。希望通过此篇文章,帮助大家更深入、透彻地了解安全测试,能快速开展安全测试。 作者:京东物流 陈维 一、引言 G.J.Myers在《软件测试的艺术》中提出:从心理学角度来说,测试是一个为了寻找错误而运行程序的过程。 " ["username"]=> string(12) "jingdongkeji" ["tagsname"]=> string(39) "前端|安全|黑盒测试|白盒测试" ["tagsid"]=> string(29) "["160","2823","14120","5741"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1681206002" ["_id"]=> string(9) "308660876" } [1]=> array(10) { ["id"]=> string(9) "308660875" ["text"]=> string(24) "vulnhub靶场之ORASI: 1" ["intro"]=> string(256) "准备: 攻击机:虚拟机kali、本机win10。 靶机:Orasi: 1,下载地址:https://download.vulnhub.com/orasi/Orasi.ova,下载后直接vbox打开即可。 知识点:hex编码、ida逆向、AndroidKiller逆向、ffuf爆破、ssti漏洞、s" ["username"]=> string(6) "upfine" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1681204802" ["_id"]=> string(9) "308660875" } [2]=> array(10) { ["id"]=> string(9) "308660874" ["text"]=> string(92) "C# Kafka重置到最新的偏移量,即从指定的Partition订阅消息使用Assign方法" ["intro"]=> string(428) "在使用Kafka的过程中,消费者断掉之后,再次开始消费时,消费者会从断掉时的位置重新开始消费。 场景再现:比如昨天消费者晚上断掉了,今天上午我们会发现kafka消费的数据不是最新的,而是昨天晚上的数据,由于数据量比较多,也不会及时的消费到今天上午的数据,这个时候就需要我们对偏移量进行重置为最新的,以" ["username"]=> string(15) "Poetwithapistol" ["tagsname"]=> string(10) ".NET|Kafka" ["tagsid"]=> string(13) "["300","440"]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1681203303" ["_id"]=> string(9) "308660874" } [3]=> array(10) { ["id"]=> string(9) "308660873" ["text"]=> string(129) "迁移学习()《Attract, Perturb, and Explore: Learning a Feature Alignment Network for Semi-supervised Domain Adaptation》" ["intro"]=> string(194) "论文信息 论文标题:Attract, Perturb, and Explore: Learning a Feature Alignment Network for Semi-supervised Domain Adaptation论文作者:Taekyung Kim论文来源:2020 ECCV论文地" ["username"]=> string(12) "BlairGrowing" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1681203302" ["_id"]=> string(9) "308660873" } [4]=> array(10) { ["id"]=> string(9) "308660872" ["text"]=> string(92) "C# Kafka重置到最新的偏移量,即从指定的Partition订阅消息使用Assign方法" ["intro"]=> string(428) "在使用Kafka的过程中,消费者断掉之后,再次开始消费时,消费者会从断掉时的位置重新开始消费。 场景再现:比如昨天消费者晚上断掉了,今天上午我们会发现kafka消费的数据不是最新的,而是昨天晚上的数据,由于数据量比较多,也不会及时的消费到今天上午的数据,这个时候就需要我们对偏移量进行重置为最新的,以" ["username"]=> string(10) "goodboydcc" ["tagsname"]=> string(10) ".NET|Kafka" ["tagsid"]=> string(13) "["300","440"]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1681202402" ["_id"]=> string(9) "308660872" } [5]=> array(10) { ["id"]=> string(9) "308660870" ["text"]=> string(42) "Django怎么使用原生SQL查询数据库" ["intro"]=> string(392) "这篇文章主要介绍“Django怎么使用原生SQL查询数据库”,在日常操作中,相信很多人在Django怎么使用原生SQL查询数据库问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Django怎么使用原生SQL查询数据库”的疑惑有所帮助!接下来,请跟着小编一起来学习吧! D" ["username"]=> NULL ["tagsname"]=> string(20) "django|sql|数据库" ["tagsid"]=> NULL ["catesname"]=> string(0) "" ["catesid"]=> NULL ["createtime"]=> string(10) "1681201981" ["_id"]=> string(9) "308660870" } [6]=> array(10) { ["id"]=> string(9) "308660871" ["text"]=> string(37) "Express怎么实现定时发送邮件" ["intro"]=> string(432) "今天小编给大家分享一下Express怎么实现定时发送邮件的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。 在开发中我们有时候需要每隔 一段时间发送一次电子邮件,或者在某个特定的时间进行发" ["username"]=> NULL ["tagsname"]=> string(7) "express" ["tagsid"]=> NULL ["catesname"]=> string(0) "" ["catesid"]=> NULL ["createtime"]=> string(10) "1681201981" ["_id"]=> string(9) "308660871" } [7]=> array(10) { ["id"]=> string(9) "308660869" ["text"]=> string(29) "mysql运维------分库分表" ["intro"]=> string(412) "1. 介绍 问题分析: 随着互联网以及移动互联网的发展,应用系统的数据量也是成指数式增长,若采用单数据库进行数据存储,存在以下性能瓶颈: IO瓶颈:热点数据太多,数据库缓存不足,产生大量磁盘IO,效率较低。请求数据太多,带宽不够,网络IO瓶颈。CPU瓶颈:排序、分组、连接查询、聚合统计等SQL会耗费" ["username"]=> string(13) "qds1401744017" ["tagsname"]=> string(5) "mysql" ["tagsid"]=> string(7) "["237"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1681200304" ["_id"]=> string(9) "308660869" } [8]=> array(10) { ["id"]=> string(9) "308660868" ["text"]=> string(41) "ASP.NET Core - 缓存之内存缓存(下)" ["intro"]=> string(292) "话接上篇 [ASP.NET Core - 缓存之内存缓存(上)],所以这里的目录从 2.4 开始。 2.4 MemoryCacheEntryOptions MemoryCacheEntryOptions 是内存缓存配置类,可以通过它配置缓存相关的策略。除了上面讲到的过期时间,我们还能够设置下面这些" ["username"]=> string(6) "wewant" ["tagsname"]=> string(12) "asp.net core" ["tagsid"]=> string(7) "["179"]" ["catesname"]=> string(25) "APS.NET Core 系列总结" ["catesid"]=> string(9) "["15288"]" ["createtime"]=> string(10) "1681200302" ["_id"]=> string(9) "308660868" } [9]=> array(10) { ["id"]=> string(9) "308660867" ["text"]=> string(9) "SPI协议" ["intro"]=> string(334) "SPI协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外设接口。广泛用在ADC、LCD等设备与MCU间,要求通讯速率较高的场合。区分它与I2C协议差异以及FLASH存储器与EEPROM存储器的区别。下面我们分别对SPI协议的物理层及协议层进行讲解。" ["username"]=> string(8) "Kaelthas" ["tagsname"]=> string(5) "STM32" ["tagsid"]=> string(8) "["1311"]" ["catesname"]=> string(5) "STM32" ["catesid"]=> string(8) "["1139"]" ["createtime"]=> string(10) "1681199702" ["_id"]=> string(9) "308660867" } } ["count"]=> int(5621682) } Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七) - 爱码网

在线演示地址:Silverlight+WCF 新手实例 象棋 在线演示

 

一开始,还是截张小图过来吧,每次截图都要花很多时间上传的。

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)

好了,我们现在知道,事件区其实就是几个按钮了,本节实现第一个按钮,“开始”游戏。别看就一个按钮,要做的事情可不少:

先总体说说这按钮要做什么事情:

1。双方进入座位时,要提示我可以点“开始”游戏;

2。我点开始“开始”,要WCF通讯,提示对方。

3。对方收到“开始游戏请求”,回复“同意/拒绝”;

4。我根据对方的回复,初始化一些状态。

 

OK,以上这按钮要做的事情,后面再说,现在还是说下布局,怎么把按钮弄到界面来实惠点:

布局还是旧逻辑:

1:新建一个用户控件:EventButton.xaml

2: 界面拖一个Border到Index.xaml,现在界面上有四个Border了,第四个chatBoard就是新添加进去的了。宽和高设置为522*52了。

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
< Grid  x:Name ="LayoutRoot"  Background ="White" >
      
<! --... 省略前面几个... -- >
        
< Border  BorderBrush ="Silver"  BorderThickness ="1"  Height ="52"  HorizontalAlignment ="Left"  Margin ="10,562,0,0"  Name ="eventButtonBoard"  VerticalAlignment ="Top"  Width ="522"   />
    
</ Grid >

 

3:后台动态加载控件了,后台代码就两行,为了以后方便控件消息传递,把控件定义为全局:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
EventButton eventButtonControl;
        
public  Index()
        {
          
// 。。。省略。。。
           eventButtonControl  =   new  EventButton(); // 今天新加的
            eventButtonBoard.Child  =  eventButtonControl;

          
// 。。。省略。。。

        }

 

OK,控件加载完了。接下来的任务就是要实现EventButton控件里的内容显示了:

 

接下来我们回到EventButton.xaml里,改一下总体宽和高为522*52:

< UserControl  ..省略...    d:DesignHeight ="52"  d:DesignWidth ="522" >
    
    
< Grid  x:Name ="LayoutRoot"  Background ="White" >

    
</ Grid >
</ UserControl >

 

跟着,我们往里放五个按钮,调整好位置:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
< Grid  x:Name ="LayoutRoot"  Background ="White" >
        
< Button  IsEnabled ="False"  Content ="开始"  Height ="23"  HorizontalAlignment ="Left"  Margin ="12,12,0,0"  Name ="btnGameStart"  VerticalAlignment ="Top"  Width ="75"    />
        
< Button  IsEnabled ="False"  Content ="认输"  Height ="23"  HorizontalAlignment ="Left"  Margin ="174,12,0,0"  Name ="btnGameLose"  VerticalAlignment ="Top"  Width ="75"    />
        
< Button  IsEnabled ="False"  Content ="求和"  Height ="23"  HorizontalAlignment ="Left"  Margin ="93,12,0,0"  Name ="btnGameDeuce"  VerticalAlignment ="Top"  Width ="75"    />
        
< Button  Content ="返回大厅"  Height ="23"  HorizontalAlignment ="Right"  Margin ="0,12,179,0"  Name ="btnBackRoom"  VerticalAlignment ="Top"  Width ="75"    />
        
< Button  Content ="退出系统"  Height ="23"  HorizontalAlignment ="Left"  Margin ="359,12,0,0"  Name ="btnLogout"  VerticalAlignment ="Top"  Width ="75"    />
    
</ Grid >

 

目前布局就不截图了,和那个事件区的截图差不多一样了。

 

前台布局结束了,后台通讯就开始了:

我们回到WCF服务那,添加一个开始游戏的接口:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
[ServiceContract(CallbackContract  =   typeof (ICallBack))] // 头顶上这里写明了回调是ICallBack
     public   interface  IService
    {
       
// ...省略之前的接口代码...
        [OperationContract(IsOneWay  =   true )]
        
void  StartGame(Player player); // 游戏开始

    }

 

看到没有,我们的传递,总是传一个Player,这样代码通篇下来,看起来清爽点。

有了服务接口,同样的,需要一个回调接口,传递的还是一个Player:

interface  ICallBack
    {
       
// ...省略之前的接口代码...
        [OperationContract(IsOneWay  =   true )]
        
void  NotifyStartGame(Player player); // 通知对方开始游戏
    }

 

接口写完了,就要实现了,回到Service.svc.cs,实现接口

一行代码,把收到的消息通知给对方就可以了:

public   void  StartGame(Player player)
        {
            Notify.Game(player, GameType.Start);
        }

 

看到Notify.Game没,这方法哪来的?别急,和上一次样,下面就来实现:

还有还有,那GameType.Start是什么来的?这个啊,是定义的枚举:

我们回到Notify.cs里,Nofity类的下面添加一个枚举:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
namespace  GameService
{
    
///   <summary>
    
///  通知 by 路过秋天
    
///   </summary>
     public   class  Notify
    {
       
// ...省略N行代码
    }
    
///   <summary>
    
///  游戏类型 by 路过秋天
    
///   </summary>
     public   enum  GameType
    {
        Start,
        Move,
        End,
    }
}

 

好了,知道游戏类型了吧,那我们实现方法了:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
internal   static   void  Game(Player player, GameType type)
        {
            
switch  (type)
            {
                
case  GameType.Start: // 通知对方玩家开始游戏
                     foreach  (KeyValuePair < Guid,Player >  item  in  Service.playerList[player.RoomID])
                    {
                        
if  (item.Value.ColorValue  +  player.ColorValue  ==   3 )
                        {
                            item.Value.CallBack.NotifyStartGame(player);
                        }
                    }
                    
break ;
                
case  GameType.Move:
                    
break ;
                
case  GameType.End:
                    
break ;
            }
        }

 

代码说明:

1:遍历房间内的用户,因为红色值+黑色值=3,所以用这个来判断是对方玩家。

2:Service.playerList,这个哪来的,是我们在Service定义的全局变量了,以前定义都是私有的,不能直接拿来用,都是在定义一个很长很长的参数来传递进来,今天就直接改一个修饰符,直接调用了,再也不用传的这么辛苦了:

看一下修改的Service.cs:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
     public   class  Service : IService
    {
        
///   <summary>
        
///  所有变化的房间列表
        
///   </summary>
         internal   static  Dictionary < int , Room >  roomList  =   new  Dictionary < int , Room > ();
        
///   <summary>
        
///  玩家集合
        
///   </summary>
         internal   static  Dictionary < int , Dictionary < Guid, Player >>  playerList  =   new  Dictionary < int , Dictionary < Guid, Player >> ();
        
        
// ...下面省略N行...[上面修改了原有的修饰符为internal]
     }

 

既然开放了,大伙自觉点,以前的Notify类里几个方法都可以把长长的传参去掉了。[想保留也行,不做强制]

 

服务端代码就写完了,又要回到客户端调用了:

再说下:编绎,更新服务引用。[每次写完WCF代码我都忘了,所以这里再提示强调一下]

 

双击一下”开始“按钮,产生事件,接着往事件里写:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
public  EventButton()
        {
            InitializeComponent();
            App.client.StartGameCompleted 
+=   new  EventHandler < System.ComponentModel.AsyncCompletedEventArgs > (client_StartGameCompleted);
        }

        
void  client_StartGameCompleted( object  sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            
// 发送完请求
            MessageBox.Show( " 请求已发送,请等待对方回应! " " 系统消息 " ,MessageBoxButton.OK);
        }

        
private   void  btnGameStart_Click( object  sender, RoutedEventArgs e)
        {
            App.player.AttachInfo 
=   " 1 " ; // 请求标识位设为1
            App.client.StartGameAsync(App.player,  1 );
        }

 

我们利用了一下AttachInfo属性,来传递我们的请求信息:

我们把请求完成的事件放到构造函数里,是为了防止多次点击生成重复事件。

写完了发送,我们来写接收:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
public  EventButton()
        {
            InitializeComponent();
            App.client.StartGameCompleted 
+=   new  EventHandler < System.ComponentModel.AsyncCompletedEventArgs > (client_StartGameCompleted);
            App.client.NotifyStartGameReceived 
+=   new  EventHandler < GameService.NotifyStartGameReceivedEventArgs > (client_NotifyStartGameReceived);        
        }

        
void  client_NotifyStartGameReceived( object  sender, GameService.NotifyStartGameReceivedEventArgs e)
        {
            
// 收到消息了应该咋办
        }

 

看看,我们收到消息是咋办的:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
void  client_NotifyStartGameReceived( object  sender, GameService.NotifyStartGameReceivedEventArgs e)
        {
            
// 收到消息了应该咋办
             switch  (e.player.AttachInfo)
            {
               
case   " 1 " : // 请求开始游戏
                  MessageBoxResult result = MessageBox.Show(e.player.NickName  +   "  请求开始游戏,是否同意开始 " " 游戏开始 " , MessageBoxButton.OKCancel);
                  App.player.AttachInfo 
=  (result == MessageBoxResult.OK)  ?   " 11 "  :  " 10 " ;
                  App.client.StartGameAsync(App.player);
                    
break ;
                
case   " 10 " :
                    MessageBox.Show(
" 对方拒绝开始游戏 " " 游戏通知 " , MessageBoxButton.OK);
                    
break ;
                
case   " 11 " :
                    MessageBox.Show(
" 对方同意开始游戏,请开始下棋 " " 游戏通知 " , MessageBoxButton.OK);
                    btnGameStart.IsEnabled 
=   false ;
                    
break ;
            }
        }

 

如果收到1的请求,我们通过点确定还是取消,再发给服务端告知对方我们的答复情况。

在这来来回回的消息中,都在这switch中全解决了。

 

如果看真点,会发现我们的开始按钮默认是IsEnabled=false,根本就不能点,那啥时候能点呢?

我们等待服务器通知,让我们添加多一个Case 0命令来等:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
void  client_NotifyStartGameReceived( object  sender, GameService.NotifyStartGameReceivedEventArgs e)
        {
            
// 收到消息了应该咋办
             switch  (e.player.AttachInfo)
            {
                
case   " 0 " : // 通知可以开始游戏
                    MessageBox.Show(e.player.NickName  +   "  已进房间,请按开始按钮开始游戏 " " 游戏通知 " , MessageBoxButton.OK);
                    btnGameStart.IsEnabled 
=   true ;
                    
break ;
                  
// ...省略刚才的几个...
              }
        }

 

好了,我们是等待服务器通知了,可是服务器什么时候通知我们呢?

我们只要在进入房间的事件中,简单判断一下:

我们回到WCF的EnterRoom方法里添加两行代码,判断一下,红色和黑色都有人了,就可以通知开始了:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)
public   bool  EnterRoom(Player player,  int  roomID)
        {
           
// ...省略N行...
              if  (room.RedInChair  &&  room.BlackInChair)
            {
                player.AttachInfo 
=   " 0 " ;
                Notify.Game(player, GameType.Start);
            }
            
return   true ;
        }

 

OK,到此通知事件就差不多结束了,我们F5看一下效果[-_-...又要截图了]:

 

1:下图先登陆一个A账号,然后新开浏览器B,准备登陆B

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)

 

2:B登陆后,可以看房间1中已有人,是A在里面。

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)

 

3:B点击进去,路过秋天A会收到消息,可以开始点按钮开始了。

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)

 

4:A点击开始按钮,B收到A的请求

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)

5:B点同意,A收到同意思消息:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)

6:于是A开始下棋了:

Silverlight+WCF 新手实例 象棋 主界面-事件区-游戏开始(二十七)

 

OK,本小节就到此结束了,

下节我们来限制一下谁下谁不下[A下时B停,B下时A停]。

提供第五阶段源码:点击下载

 

转载于:https://my.oschina.net/secyaher/blog/274136

相关文章:

  • 2021-05-18
  • 2021-06-02
  • 2021-10-02
猜你喜欢
  • 2021-04-23
  • 2022-01-08
  • 2021-07-07
相关资源
相似解决方案