实训一
码云地址:https://gitee.com/SoridoD/java_shixun1 洪雨码云地址
选取题目:黄金点
需求:阿超的课都是下午两点钟,这时班上不少的同学都昏昏欲睡,为了让大家兴奋起来,阿超让同学玩一个叫“黄金点”的游戏:N个同学(N通常大于10),每人写一个0~100之间的有理数 (不包括0或100),交给裁判,裁判算出所有数字的平均值,然后乘以0.618(所谓黄金分割常数),得到G值。提交的数字最靠近G(取绝对值)的同学得到N分,离G最远的同学得到-2分,其他同学得0分。记录每一次游戏每名同学的数字和分数。
编程小组成员: 16012104 刘笑维 16012105 刘洪雨
编程思路及运用的技术:
思路:采用客户端/服务器的方式,客户端充当玩家,服务器充当裁判。
技术:运用组播,UDP通信,实现服务器和客户端的交互,数据的保存和计算由服务器完成和发布。
分工方向:
刘笑维负责Client端,刘洪雨负责Server端。
实际操作的时候,和预想的并不完全一样,我们首先讨论出了具体的方向,其次是算法。随后算法的主要代码是我们共同完成的,其中核心代码是洪雨完成的,洪雨主要是编写,我主要负责测试和审查代码。其余的各种文档处理各有分工。
结对编程照片:
编程代码
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)
第一次测试Server端:(一台PC)
运行前 运行后
发现的问题:该程序无问题运行第一次,但接着运行第二次时报错
Client报错: Server报错:
改进:修改代码后,关掉road.close。
将部分对象属性改为放法内的局部变量。
udp发包过快,有的Client收不到udp的包,用sleep方法使系统等待一段时间。
第二次测试目的为第一次程序的优化。
第二次测试Client端(3台PC):
第二次测试Server端(一台PC):
第二次程序优化成功!!!!
评价:
洪雨同学编程速度非常迅速,有着完整的编程思想,代码完整规范。Java和算法功底,以及操作系统,网络协议的基础都得到充分的体现。编写代码速度快,程序执行起来效率高,思维广阔,也十分努力。他是我学习的榜样。
总结:
由于我们是第一次尝试结对编程,进行了前所未有的尝试,感觉比自己一个人来写代码效果更好,但是由于一开始思路不清,所以一开始很迷茫,觉得以后结对编程中要搞清楚要做什么,然后两个人要充分的交流,然后一个人负责写,一个人负责审查帮助提供建议。然后两个人的角色再进行互换。
在未来的实训中,我们会以更默契的方式去完成老师布置的任务。
学习内容 | 代码行数 | 博客字数 |
实训一-黄金点 | 243 | 300 |