zoukankan      html  css  js  c++  java
  • 黄金点游戏之客户端(homework-05)

    0. 摘要

      之前我们玩了2次黄金数游戏,我也幸运的得到了一本《代码大全》,嘿嘿。这次的作业是一个Client/Server程序,自动化完成多轮重复游戏。

    我完成了Client部分,使用C#编写。下面简要阐述。

    1. 总体设计:

      思考后,我认为这个客户端程序要能满足如下要求:

      1. 保证信息传输到服务器。如果发送的信息没有得到相应,应可以不断重试。

      2. 一定的错误恢复能力,当因网络问题错过某些回合,应该可以跳过而继续运行。

      3. 恰当的算法,提供相对准确的黄金数字预测。

      4. 具有自动获取可用端口能力,使得20个客户端同时开启能够不冲突的与服务器连接。

      除此之外,为了确保游戏执行期间不出现漏洞(如假冒身份发送数字等),客户端还具有如下考虑:

      1. 游戏开始前,使用ID和密码注册。

      2. 等待回合开始;只有当接收到服务器新回合开始的消息,且新回合的序号大于上一次的序号时,才开始计算和条过程。提交信息附带了本客户端的用户名密码。当然,服务器也可以根据客户端的IP和端口判断身份。这里考虑万一不得不更换网络并继续进行回合的情况。

      3. 每次发送消息均要等待服务器返回确认信息,并且是当前消息的确认信息。在未收到反馈时,启用一个线程不断重试发送。

      4. 当服务器停止游戏,客户端不再进入发送线程和等待线程。

      5. 客户端可以停止游戏。不论如何停止客户端,客户端需要正常告知服务器退出游戏。以避免服务器重试发送新回合开始消息。

      6. 客户端提供GUI,可自定义服务器IP和端口,不填写默认为本地。随时显示当前状态,便于观察和调试。

    2. 端口设计

      基于上述考虑,我的客户端/服务器通讯协议为:

      str = TYPE + ";" + OP1 + ";" + OP2 + ";" + OP3;

      这是一条类似指令的收发字符串。各各数值使用分好分割。其中TYPE指明消息类型,包含以下信息:

      客户端注册,

      服务器注册确认,

      服务器开始新回合,

      客户端提交数字,

      服务器确认收到提交数字,

      服务器停止游戏。

      具体来说,我们为每一个消息举个例子:

      1. 客户端以ID为11061128,密码123456向服务器注册:

          str = "1;11061128;123456"

      2.服务器收到这个注册,注册成功:

          str = "2"

      3.服务器开始第5个回合,开始提交数字。上一回合的黄金数是17:

          str = "3;5;17"

      4.客户端提交第5回合的数字11,并验证身份:

          str = "4;5;11;11061128;123456"

      5.服务器收到第5回合提交的数字,正在等待他人提交:

          str = "5;5"

      6.服务器停止游戏:

          str = "6";

    3. 线程调度

      客户端完成相关逻辑是通过一个控制线程调动其他线程的起止。线程有:

      control线程:循环的状态机,直到停止。此线程一直运行。

      receive线程:需要接受服务器确认结果时候,一直运行直到收到相应信息,完成等待。

      其他线程:完成各自信息发送的功能,与前两者同时进行。当收到相应确认信息时,线程停止,不再重试发送信息。(一遍不断的发信息,一遍看服务器收到没有)。

    4. 测试结果

      经过为服务器的测试,客户端已经可以正确运行。下图的客户端Log显示了状态机、信息收发的情况,正常。通过服务器读取客户端发来的消息,并发送正确指令,测试正常:

      客户端:

      

      服务器显示客户端发来的连接和信息:

      

    5. 主要代码

      下面是主要代码部分。该部分代码的重点是状态机、线程调度和信息收发:

      

      1 using System;
      2 using System.Collections.Generic;
      3 using System.ComponentModel;
      4 using System.Data;
      5 using System.Drawing;
      6 using System.Linq;
      7 using System.Text;
      8 using System.Threading.Tasks;
      9 using System.Windows.Forms;
     10 using System.Net;
     11 using System.Net.Sockets;
     12 using System.Threading;
     13 using System.IO;
     14 
     15 namespace AsyncTcpServer
     16 {
     17     public partial class mainForm : Form
     18     {
     19 
     20         const int STATE_REGISTER = 1;
     21         const int STATE_REGISTER_OK = 2;
     22         const int STATE_NEW_ROUND = 3;
     23         const int STATE_SUBMIT = 4;
     24         const int STATE_SUBMIT_OK = 5;
     25         const int STATE_SERVER_STOP = 6;
     26         const int INTERVAL = 3000;
     27 
     28         const string TYPE_REGISTER = "1";
     29         const string TYPE_REGISTER_OK = "2";
     30         const string TYPE_NEW_ROUND = "3";
     31         const string TYPE_SUBMIT = "4";
     32         const string TYPE_SUBMIT_OK = "5";
     33         const string TYPE_SERVER_STOP = "6";
     34 
     35         const string    DEFAULT_SERVER_IP = "192.168.1.2";    
     36         const int       DEFAULT_SERVER_PORT =  51888;
     37         const string    DEFAULT_USER_ID = "11061128";
     38         const string    DEFAULT_USER_PASSWD = "123456";
     39 
     40         private string ip_input = DEFAULT_SERVER_IP;
     41         private int port = DEFAULT_SERVER_PORT;
     42         private string id = DEFAULT_USER_ID;
     43         private string passwd = DEFAULT_USER_PASSWD;
     44 
     45         private int tstate = 2;
     46         private int State = STATE_REGISTER;
     47         private int Round = 0;
     48         private int[] PrevRslt = new int[5000];
     49         private int PrevRslt_idx = 0;
     50         private int GoldPoint;
     51         private string[] rcvs;
     52         private bool isRunning = false;
     53 
     54         private TcpClient client = null;
     55         private StreamWriter sw;
     56         private StreamReader sr;
     57         private Service service;
     58         private NetworkStream netStream;
     59 
     60         Thread threadRegister;
     61         Thread threadSubmit;
     62         Thread threadReceive;
     63         Thread threadWaitNewRound;
     64         Thread threadControl;
     65 
     66         public mainForm()
     67         {
     68             InitializeComponent();
     69             service = new Service(lb_log, sw);
     70         }
     71 
     72         private void btn_start_Click(object sender, EventArgs e)
     73         {
     74             if (isRunning == true)
     75             {
     76                 service.SetListBox("Already running, Press STOP first");
     77                 return;
     78             }
     79 
     80             if (String.Compare(tb_svr_IP.Text, "IP") != 0)
     81             {
     82                 ip_input = tb_svr_IP.Text.Trim();
     83             }
     84             if (String.Compare(tb_svr_port.Text, "PORT") != 0)
     85             {
     86                 port = Int32.Parse(tb_svr_port.Text.Trim());
     87             }
     88             if (String.Compare(tb_id.Text, "ID") != 0)
     89             {
     90                 id = tb_id.Text.Trim();
     91             }
     92             if (String.Compare(tb_passwd.Text, "PASSWORD") != 0)
     93             {
     94                 passwd = tb_passwd.Text.Trim();
     95             }
     96             IPAddress serverIP = IPAddress.Parse(ip_input);
     97             client = new TcpClient();
     98             try
     99             {
    100                 client.Connect(serverIP, port);
    101             }
    102             catch (System.Exception ex)
    103             {
    104                 service.SetListBox(ex.Message);
    105                 return;
    106             }
    107             try
    108             {
    109                 netStream = client.GetStream();
    110             }
    111             catch (System.Exception ex)
    112             {
    113                 service.SetListBox(ex.Message);
    114                 return;
    115             }
    116             sr = new StreamReader(netStream, System.Text.Encoding.UTF8);
    117             sw = new StreamWriter(netStream, System.Text.Encoding.UTF8);
    118             service = new Service(lb_log, sw);
    119             isRunning = true;
    120             threadControl = new Thread(new ThreadStart(control));
    121             threadControl.Start();
    122         }
    123 
    124         private void control()
    125         {
    126             threadRegister = new Thread(new ThreadStart(Register));
    127             //threadSubmit = new Thread(new ThreadStart(Cal_and_Submit));
    128             threadReceive = new Thread(new ThreadStart(ReceiveData));
    129             //threadWaitNewRound = new Thread(new ThreadStart(WaitNewRound));
    130             //threadWaitNewRound.Start(); threadReceive.Suspend();
    131             //threadSubmit.Start(); threadSubmit.Suspend();
    132 
    133             while (isRunning == true)
    134             {
    135                 if (tstate == 1) //wait finish
    136                 {
    137                     threadWaitNewRound.Abort();
    138                     tstate = 0;
    139                 }
    140                 else if(tstate == 0)//waiting
    141                 {
    142                     continue;
    143                 }
    144                 //nothing to wait
    145                 switch (State)
    146                 {
    147                     case STATE_REGISTER:
    148                         threadRegister.Start();
    149                         threadRegister.Join();
    150                         State = STATE_REGISTER_OK;
    151                         break;
    152                     case STATE_REGISTER_OK:
    153                         threadWaitNewRound = new Thread(new ThreadStart(WaitNewRound));
    154                         threadWaitNewRound.Start();
    155                         threadWaitNewRound.Join();
    156                         tstate = 2;
    157                         State = STATE_NEW_ROUND;/////
    158                         break;
    159                     case STATE_NEW_ROUND:
    160                         threadSubmit = new Thread(new ThreadStart(Cal_and_Submit));
    161                         threadSubmit.Start();
    162                         threadSubmit.Join();
    163                         State = STATE_SUBMIT_OK;
    164                         break;
    165                     case STATE_SUBMIT_OK:
    166                         threadWaitNewRound = new Thread(new ThreadStart(WaitNewRound));
    167                         threadWaitNewRound.Start();
    168                         threadWaitNewRound.Join();
    169                         State = STATE_NEW_ROUND;
    170                         tstate = 2;
    171                         break;
    172                     case STATE_SERVER_STOP:
    173                         service.SetListBox("Server Stop");
    174                         endMission();
    175                         break;
    176                     default:
    177                         break;
    178                 }
    179             }
    180         }
    181 
    182         private void ReceiveData()
    183         {
    184             while (isRunning == true)
    185             {
    186                 string receiveString = null;
    187                 try
    188                 {
    189                     receiveString = sr.ReadLine();
    190                 }
    191                 catch (Exception e)
    192                 {
    193                     service.SetListBox(e.Message);
    194                 }
    195 
    196                 if (receiveString == null)
    197                 {
    198                     //
    199                     service.SetListBox("wait to re receive");
    200                     //
    201                     Thread.Sleep(INTERVAL);
    202                     continue;
    203                 }
    204                 //
    205                 service.SetListBox("recieved" + receiveString);
    206                 //
    207                 rcvs = receiveString.Split(';');
    208                 switch (rcvs[0])
    209                 {
    210                     case TYPE_REGISTER_OK:
    211                         State = STATE_REGISTER_OK;
    212                         break;
    213                     case TYPE_SUBMIT_OK:
    214                         if (Round == Int32.Parse(rcvs[1]))
    215                         {
    216                             State = STATE_SUBMIT_OK;
    217                         }
    218                         break;
    219                     case TYPE_NEW_ROUND:
    220                         if (Round == Int32.Parse(rcvs[1]) - 1)
    221                         {
    222                             State = STATE_NEW_ROUND;
    223                         }
    224                         break;
    225                     case TYPE_SERVER_STOP:
    226                         State = STATE_SERVER_STOP;
    227                         service.SetListBox("Server Stop");
    228                         tstate = 2;
    229                         endMission();
    230                         break;
    231                     default:
    232                         break;
    233                 }
    234             }
    235         }
    236 
    237         private void Register()
    238         {
    239             threadReceive.Start();
    240             String str = TYPE_REGISTER + ";" + id + ";" + passwd;
    241             service.SendToServer(str);
    242             while (isRunning && State != STATE_REGISTER_OK)
    243             {
    244                 //
    245                 service.SetListBox("re register");
    246                 //
    247                 Thread.Sleep(INTERVAL);
    248                 service.SendToServer(str);
    249             }
    250             threadReceive.Suspend();
    251         }
    252 
    253         private void WaitNewRound()
    254         {
    255             threadReceive.Resume();
    256             while (isRunning && State != STATE_NEW_ROUND)
    257             {
    258                 //
    259                 service.SetListBox("waiting");
    260                 //
    261                 Thread.Sleep(INTERVAL);
    262             }
    263             Round++;
    264             PrevRslt[PrevRslt_idx++] = Int32.Parse(rcvs[1]);
    265             threadReceive.Suspend();
    266             tstate = 1;
    267         }
    268 
    269         private void Cal_and_Submit()
    270         {
    271             threadReceive.Resume();
    272             GoldPoint = calculate();
    273             string str = TYPE_SUBMIT + ";" + Round.ToString() + ";" + GoldPoint.ToString();
    274             service.SendToServer(str);
    275             while (isRunning && State != STATE_SUBMIT_OK)
    276             {
    277                 Thread.Sleep(INTERVAL);
    278                 //
    279                 service.SetListBox("submitting");
    280                 //
    281                 service.SendToServer(str);
    282             }
    283             threadReceive.Suspend();
    284         }
    285         private int calculate()
    286         {
    287             //
    288             service.SetListBox("calculation");
    289             //
    290             if (Round == 1)
    291             {
    292                 return 100;
    293             }
    294             else
    295             return (int)(PrevRslt[PrevRslt_idx] * 0.618);
    296         }
    297  
    298         private void btn_stop_Click(object sender, EventArgs e)
    299         {
    300             endMission();
    301         }
    302 
    303         private void mainForm_FormClosing(object sender, FormClosingEventArgs e)
    304         {
    305             endMission();
    306         }
    307 
    308         private void endMission()
    309         {
    310             //
    311             service.SetListBox("ending mission");
    312             //
    313             if (isRunning)
    314             {
    315                 netStream.Close();
    316                 client.Close();
    317             }
    318             isRunning = false;
    319         }
    320     }
    321 }

      下一次作业,客户端将完善线程操作(弃用已经过时的suspend和resume方法),并使用最小二乘法逼近黄金点曲线的方法来预测数值。

  • 相关阅读:
    计算机网络概述
    虚拟机网卡配置
    元类
    反射和内置方法
    面向对象的三大特性
    MYSQL中EXISTS的用法
    Guava中的常见集合操作用法
    集合操作交并补的三种Java实现
    P9 get和resize操作(Java 13)
    P8 Java 13中 HashMap的 put方法
  • 原文地址:https://www.cnblogs.com/shone/p/3406159.html
Copyright © 2011-2022 走看看