04刘笑维-05刘洪雨-实训一

时间:2020-11-26 11:50:05

实训一

码云地址:https://gitee.com/SoridoD/java_shixun1 洪雨码云地址

选取题目:黄金点

需求:阿超的课都是下午两点钟,这时班上不少的同学都昏昏欲睡,为了让大家兴奋起来,阿超让同学玩一个叫“黄金点”的游戏:N个同学(N通常大于10),每人写一个0~100之间的有理数 (不包括0100),交给裁判,裁判算出所有数字的平均值,然后乘以0.618(所谓黄金分割常数),得到G值。提交的数字最靠近G(取绝对值)的同学得到N分,离G最远的同学得到-2分,其他同学得0分。记录每一次游戏每名同学的数字和分数。

编程小组成员 16012104 刘笑维 16012105 刘洪雨

编程思路及运用的技术

思路:采用客户端/服务器的方式,客户端充当玩家,服务器充当裁判。

技术:运用组播,UDP通信,实现服务器和客户端的交互,数据的保存和计算由服务器完成和发布。

分工方向

刘笑维负责Client端,刘洪雨负责Server端。

实际操作的时候,和预想的并不完全一样,我们首先讨论出了具体的方向,其次是算法。随后算法的主要代码是我们共同完成的,其中核心代码是洪雨完成的,洪雨主要是编写,我主要负责测试和审查代码。其余的各种文档处理各有分工。

结对编程照片

04刘笑维-05刘洪雨-实训一04刘笑维-05刘洪雨-实训一

编程代码

Client

  1 import java.net.*;
  2 import java.util.*;
  3 public class Client {
  4     static int time;
  5     public static void main(String[] args) {
  6         BroadCast1 broadcast = new BroadCast1();
  7         System.out.println("欢迎来到黄金点游戏,您是玩家");
  8         System.out.println("请等待裁判上线...");
  9         broadcast.receive();
 10         int time = broadcast.set();
 11         for(int i = 1; i<= time;i++) {
 12             broadcast.receive();
 13             broadcast.send();
 14             broadcast.receive();
 15             broadcast.receive_ip(i);
 16         }
 17     }
 18 
 19 }
 20 class BroadCast1{
 21     int port ;
 22     Scanner read;
 23     InetAddress ip;
 24     InetAddress group;
 25     MulticastSocket socket ; //多点广播套接字
 26     BroadCast1(){
 27         try {
 28             read = new Scanner(System.in);
 29             ip = InetAddress.getByName("10.43.11.81");        //服务器地址
 30             group = InetAddress.getByName("239.255.8.0");    //组播地址
 31             port = 5000;    //组播端口
 32             socket = new MulticastSocket(port);    //多点广播套接字将在port端口广播
 33             socket.joinGroup(group);    //加入组播
 34         }catch(Exception e) {
 35             System.out.println(e);
 36         }
 37     }
 38     int set() {
 39         byte data[] = new byte[100];
 40         DatagramPacket pack = new DatagramPacket(data,data.length,group,port);
 41         while(true) {
 42             try {
 43                 socket.receive(pack);    //捕获发来的包
 44             }catch(Exception e) {
 45                 System.out.println(e);
 46             }
 47             if(pack !=null) {
 48                 break;
 49             }
 50         }
 51         String mess = new String(pack.getData(),0,pack.getLength());    //将包转为string
 52         return Integer.parseInt(mess);
 53     }
 54     void receive (){
 55         byte data[] = new byte[100];
 56         DatagramPacket pack = new DatagramPacket(data,data.length,group,port);
 57         while(true) {
 58             try {
 59                 socket.receive(pack);    //捕获发来的包
 60             }catch(Exception e) {
 61                 System.out.println(e);
 62             }
 63             if(pack !=null) {
 64                 break;
 65             }
 66         }
 67         String mess = new String(pack.getData(),0,pack.getLength());    //将包转为string
 68         System.out.println(mess);
 69     }
 70     void send() {
 71         DatagramSocket pack_out = null;    //变量声明
 72         DatagramPacket pack = null;        //用于构建包
 73         try {
 74             pack_out = new DatagramSocket();    //用于发包
 75         }catch(Exception e) {
 76             System.out.println(e);
 77         }
 78         String mess = read.next();
 79         byte data[] = mess.getBytes();    //String转byte
 80         try {
 81             pack = new DatagramPacket(data,data.length,ip,2000);    //打包
 82             pack_out.send(pack);     //发包
 83         }catch(Exception e){
 84             System.out.println(e);
 85         }
 86         System.out.println("成功发送");
 87     }
 88     void receive_ip(int i) {
 89         byte data[] = new byte[100];    //变量声明
 90         DatagramPacket pack = null;        //用于构建包
 91         DatagramSocket pack_in = null;    //用于接受包
 92         try {
 93             pack = new DatagramPacket(data, data.length);    //构建包
 94             pack_in = new DatagramSocket(2000+i);    //为防止运算过快 导致端口被占用 使用了动态端口
 95         }catch(Exception e) {
 96             System.out.println(e);
 97         }
 98         while(true) {
 99             try {
100                 pack_in.receive(pack);    //捕获发来的包
101             }catch(Exception e) {
102                 System.out.println(e);
103             }
104             if(pack !=null) {
105                 break;
106             }
107         }
108         String mess = new String(pack.getData(),0,pack.getLength());    //将包转为string
109         System.out.println(mess);
110     }
111 }

 

Server

  1 import java.net.*;
  2 import java.util.*;
  3 public class Server {
  4     public static void main(String[] args) {
  5         Scanner read = new Scanner(System.in);
  6         BroadCast broadcast = new BroadCast();
  7         System.out.println("欢迎来到黄金点游戏,您是裁判");
  8         System.out.println("请确认所有学生都已上线,输入start");
  9         while(true) {
 10             if("start".equals(read.next())) {
 11                 break;
 12             }else {}
 13         }
 14         broadcast.send("裁判上线了!!!");
 15         System.out.print("游戏开始!!!\n请输入学生数量:");
 16         Student.stu_num = read.nextInt();
 17         System.out.print("玩几轮游戏:");
 18         int time = read.nextInt();
 19         Student[] student = new Student[Student.stu_num];    //声明学生数量
 20         for(int i = 0;i < Student.stu_num;i++) {            //初始化学生数组
 21             student[i] = new Student(time);
 22         }
 23         broadcast.start(time);
 24         try {
 25             Thread.currentThread();
 26             Thread.sleep(2000);
 27         }catch(Exception e) {
 28             System.out.println(e);
 29         }
 30         for(int k = 1 ; k<=time;k++) {    //第几轮
 31             student[0].record(k, student,broadcast);    //抓包,记录发来的数值
 32             student[0].grade_update(k, student,broadcast);    //计算,并更新分数
 33             student[0].out_grade(student,k);    //公布分数
 34         }
 35         broadcast.send("Game Over!!!");
 36         System.out.println("游戏已经结束");
 37         read.close();
 38     }
 39 }
 40 class Student {
 41     static int stu_num ;    //学生数量
 42     InetAddress ip;    //学生的IP地址
 43     double num[];    //学生每轮输入的数字
 44     int grade;        //分数
 45     Student(int n){
 46         ip = null;    
 47         grade = 0;
 48         num = new double[n];    //num[0]代表第一次,num[1]代表第二次
 49     }
 50     void setIp(InetAddress ip) {    //设置学生的ip
 51         this.ip = ip;
 52     }
 53     
 54     //记录每轮输入的数字
 55     void record(int n , Student student[],BroadCast broadcast) {
 56         n--;    //对应num[]角标
 57         byte data[] = new byte[8192];    //变量声明
 58         DatagramPacket pack = null;        //用于构建包
 59         DatagramSocket pack_in = null;    //用于接受包
 60         try {
 61             pack = new DatagramPacket(data, data.length);    //构建包
 62             pack_in = new DatagramSocket(2000);    //监听udp 2000端口
 63         }catch(Exception e) {
 64             System.out.println(e);
 65         }
 66         System.out.println("游戏开始了!!!");
 67         broadcast.send("请输入数据:");
 68         if(n==0) {//第一轮需要记住ip地址
 69             for(int i = 0;i<Student.stu_num;i++) {
 70                 while(true) {
 71                     try {
 72                         pack_in.receive(pack);    //捕获发来的包
 73                     }catch(Exception e) {
 74                         System.out.println(e);
 75                     }
 76                     if(pack !=null) {
 77                         break;
 78                     }
 79                 }
 80                 student[i].setIp(pack.getAddress());    //按照捕获包的顺序 给student的ip属性赋值
 81                 String mess = new String(pack.getData(),0,pack.getLength());    //将包转为string
 82                 String ip = pack.getAddress().toString();    //将收到的包的ip转为string
 83                 System.out.println("收到来自\""+ip+"\"的:"+mess);    //提示信息
 84                 student[i].num[n] = Double.valueOf(mess);    //将string转为double并保存
 85             }
 86         }else {//第二轮往后需要根据来的包里的ip地址 确认student对象
 87             for(int i = 0;i<Student.stu_num;i++) {
 88                 try {
 89                     pack_in.receive(pack);    
 90                 }catch(Exception e) {
 91                     System.out.println(e);
 92                 }
 93                 String mess = new String(pack.getData(),0,pack.getLength());
 94                 String ip = pack.getAddress().toString();
 95                 System.out.println("收到来自\""+ip+"\"的:"+mess);
 96                 student[0].ip_S(pack.getAddress(), student).num[n] = Double.valueOf(mess); //根据ip给student对象的num赋值
 97             }
 98         }
 99         pack_in.close();
100     }
101     
102     //根据ip返回student对象
103     Student ip_S(InetAddress ip,Student student[]) {
104         int i;
105         for(i = 0;i<Student.stu_num;i++) {    
106             if(ip.toString().equals(student[i].ip.toString())) {    //对比ip是否相等
107                 break;
108             }
109         }
110         if(i == Student.stu_num) {    //如果没找到就返回null
111             return null;
112         }else {
113             return student[i];
114         }
115     }
116     
117     //分值更新
118     void grade_update(int n , Student student[],BroadCast broadcast) {    //第几轮,对象数组
119         broadcast.send("请稍等,裁判正在计算...");
120         double g = 0;    //G值
121         int neer[] = new int[Student.stu_num];    //与G值最近的同学的对象数组角标(可能会有同样距离的同学)
122         int far[] = new int[Student.stu_num];    //与G值最远的同学的对象数组角标(可能会有同样距离的同学)
123         //求G值
124         n--;    //对应num[]角标
125         for(int i = 0;i<Student.stu_num;i++) {
126             g += student[i].num[n];
127         }
128         g = g / Student.stu_num * 0.618;    //G值求出
129         //求出每位同学和G值距离
130         double abs[] = new double[Student.stu_num];    //记录每位同学与G值的距离
131         for(int i = 0;i<Student.stu_num;i++) {
132             abs[i] = Math.abs(student[i].num[n] - g);
133         }
134         //比较出最远和最近的同学
135         neer[0] = 0;    //假设最开始0号同学最近也最远
136         far[0] = 0;    
137         int j = 1;    //记录最近的有几人
138         int k = 1;    //记录最远的有几人
139         for(int i=1 ;i<Student.stu_num;i++) {    //只记录最近的和最远的 
140             if (abs[i] < abs[neer[0]] ) {    //是否比最近的近
141                 neer[0] = i ;    //记住现在最近的人
142                 j = 1 ;        //更新人数
143             }
144             else if (abs[i] == abs[neer[0]]) {    //是否和最近的相等
145                 neer [j] = i ;     //同时有多个人最近
146                 j++;
147             }
148             else if (abs[i] == abs[far[0]]) {    //是否和最远的相等
149                 far [k] = i;    //同时有多个人最远
150                 k++;
151             }
152             else if (abs[i] > abs[far[0]]) {    //是否比最远的远
153                 far [0] = i;    //记住现在最远的人
154                 k = 1;
155             }
156         }
157         //更新加分的同学的成绩
158         if(j==Student.stu_num) {}    //如果所有人都最近 那就啥也不做
159         else {
160             for(int i = 0;i < j;i++) {
161                 student[neer[i]].grade += Student.stu_num;
162             }    
163         }
164         //更新减分的同学的成绩
165         if(k==Student.stu_num) {}    //如果所有人都最远 那就啥也不做
166         else {
167             for(int i = 0;i < k;i++) {
168                 student[far[i]].grade -= 2;
169             }    
170         }
171         System.out.println("本轮游戏分数更新完成");
172     }
173     
174     //公布分数
175     void out_grade (Student student[],int j){
176         DatagramSocket pack_out = null;    //变量声明
177         DatagramPacket pack = null;        //用于构建包
178         try {
179             pack_out = new DatagramSocket();    //用于发包
180         }catch(Exception e) {
181             System.out.println(e);
182         }
183         for(int i = 0;i<Student.stu_num;i++) {    //每一个学生
184             try {
185                 Thread.currentThread();        //防止发包过快 导致部分玩家接收不到 等待1s
186                 Thread.sleep(1000);
187             }catch(Exception e) {
188                 System.out.println(e);
189             }
190             String mess = "本轮游戏结束!!!  \n你现在的分数为:"+String.valueOf(student[i].grade);    //double转String
191             byte data[] = mess.getBytes();    //String转byte
192             try {
193                 pack = new DatagramPacket(data,data.length,student[i].ip,2000+j);    //打包
194                 pack_out.send(pack);     //发包
195             }catch(Exception e){
196                 System.out.println(e);
197             }
198         }
199         System.out.println("本轮成绩发布完成");
200     }
201 }
202 
203 class BroadCast{
204     int port ;
205     InetAddress group;
206     MulticastSocket socket ; //多点广播套接字
207     BroadCast(){
208         try {
209             group = InetAddress.getByName("239.255.8.0");    //组播地址
210             port = 5000;    //组播端口
211             socket = new MulticastSocket(port);    //多点广播套接字将在port端口广播
212             socket.setTimeToLive(1);    //数据报范围 本地
213             socket.joinGroup(group);    //加入组播
214         }catch(Exception e) {
215             System.out.println(e);
216         }
217     }
218     
219     //告诉学生玩几轮
220     void start(int time) {
221         try {
222             System.out.println("请稍等,正在发号施令");
223             String mess = String.valueOf(time);
224             byte data[] = mess.getBytes();    //将玩几轮 发布给所有学生
225             DatagramPacket packet = new DatagramPacket(data,data.length,group,port);
226             socket.send(packet);
227         }catch(Exception e) {
228             System.out.println(e);
229         }
230     }
231     
232     //广播发送信息
233     void send(String mess) {
234         try {
235             String mess1 = mess;
236             byte data[] = mess1.getBytes();    
237             DatagramPacket packet = new DatagramPacket(data,data.length,group,port);
238             socket.send(packet);
239         }catch(Exception e) {
240             System.out.println(e);
241         }
242     }
243 }

第一次测试Client(四台PC

  04刘笑维-05刘洪雨-实训一04刘笑维-05刘洪雨-实训一04刘笑维-05刘洪雨-实训一04刘笑维-05刘洪雨-实训一

第一次测试Server(一台PC

运行前                                                         运行后

04刘笑维-05刘洪雨-实训一         04刘笑维-05刘洪雨-实训一

 

发现的问题:该程序无问题运行第一次,但接着运行第二次时报错

Client报错:                                               Server报错

04刘笑维-05刘洪雨-实训一        04刘笑维-05刘洪雨-实训一

改进:修改代码后,关掉road.close。

             将部分对象属性改为放法内的局部变量。

             udp发包过快,有的Client收不到udp的包,用sleep方法使系统等待一段时间。

第二次测试目的为第一次程序的优化。

第二次测试Client端(3台PC):

04刘笑维-05刘洪雨-实训一04刘笑维-05刘洪雨-实训一04刘笑维-05刘洪雨-实训一

第二次测试Server端(一台PC):

04刘笑维-05刘洪雨-实训一

第二次程序优化成功!!!!

评价

洪雨同学编程速度非常迅速,有着完整的编程思想,代码完整规范。Java和算法功底,以及操作系统,网络协议的基础都得到充分的体现。编写代码速度快,程序执行起来效率高,思维广阔,也十分努力。他是我学习的榜样。

总结

由于我们是第一次尝试结对编程,进行了前所未有的尝试,感觉比自己一个人来写代码效果更好,但是由于一开始思路不清,所以一开始很迷茫,觉得以后结对编程中要搞清楚要做什么,然后两个人要充分的交流,然后一个人负责写,一个人负责审查帮助提供建议。然后两个人的角色再进行互换。

在未来的实训中,我们会以更默契的方式去完成老师布置的任务。

学习内容 代码行数 博客字数
实训一-黄金点 243 300