1. 绪论(可以包括实验目的、实验内容)
1、实验目的:
要求学生掌握Socket技术及令牌总线工作过程
2、实验内容:
i. 要求学生掌握利用Socket进行编程的技术
ii. 8台主机(用进程来实现),主机号为1-8
iii. 程序模拟主机发送过程,发送内容由人来输入
iv. 产生一个令牌,绕着8台主机依照主机号进行轮转(即1号发给2号,2号发给3号,...,8号发给1号),一秒钟转移一次
v. 程序模拟主机随机发送(6秒内选择随机数),(内容格式为:源目主机号+“:”+目的主机号+“:”+HelloBaby)
vi. 发送前,查看当前是否有有令牌,如果没有令牌,则等待
vii. 如果有令牌,可以发送数据(目的主机号随机产生),假定数据传输过程为2秒,此时不再发送令牌,只发送数据
viii. 数据依然按照主机号发送
ix. 接收方接受后,显示内容
x. 发送主机最后收到自己的数据,重新发送令牌
xi. 必须采用图形界面,每个程序可以设置自己的主机号,查看运行过程
2. 设计思路
每台主机都有自己的发送端和接收端,接收端一直开启,在创建完第八个主机的时候让第八个主机发送一次令牌给第一个主机这样令牌就可以一直循环。主机接收到消息后判断是否是令牌帧,如果是则判断缓存该主机缓存数据是否为空,如果是空则将令牌转发,如果不是空则进行数据发送,并在发送结束收关闭发送表示一组发送完成;如果不是令牌帧,则将数据分割,判断目的主机是否是自己,是则显示,不是则判断源主机号是不是自己,是则等数据接受完毕后发送令牌,不是则转发数据。
3. 算法实现及分析(重点)
3.1主机:每台主机用一个进程表示,就要求每个主机类都包含一个可以接收消息的ServerSocket和一个可以发送数据的socket
收消息函数 | publicstaticvoid setServer(int x,JTextArea jt,JLabel jl) throws IOException { System.out.println("服务器端启动。。。。");
ServerSocket ss= new ServerSocket(x-1); new Thread(new myServer(ss,jt,jl)).start(); } |
发消息函数 | publicstaticvoid send() throws UnknownHostException, IOException{ System.out.println("客户端启动。。。。"); //1.创建客户端对象,明确目的地址和端口 Socket s= new Socket("192.168.138.1",10000); //2.获取socket流中的输出流,将数据发送给服务器 PrintWriter out = new PrintWriter(s.getOutputStream(),true); //3.通过输出流写数据 out.println("Token"); s.shutdownOutput(); s.close(); } |
3.2每台主机服务器都必须是一直开启监听状态,所以需要把服务器端放在线程里,这样可以在不停接收数据时发送数据
publicstaticvoid setServer(int x,JTextArea jt,JLabel jl) throws IOException { System.out.println("服务器端启动。。。。");
ServerSocket ss= new ServerSocket(x-1); new Thread(new myServer(ss,jt,jl)).start(); } |
3.3发送数据时需要判断是否有令牌,所以先将数据缓存到文件中,等令牌来时判断文件是否为空,如果为空则转发令牌,否则发送文件数据。发送数据时,文件内容发送完之后再将发送一个终止符表示这个文件是一组文件。
publicvoid send(String str,Socket s,int num) throws IOException{ File filename = new File(str); // 要读取以上路径的input。txt文件 InputStreamReader reader = new InputStreamReader( new FileInputStream(filename)); // 建立一个输入流对象reader BufferedReader br = new BufferedReader(reader); // 建立一个对象,它把文件内容转成计算机能读懂的语言 String line = ""; line = br.readLine(); PrintWriter out = new PrintWriter(s.getOutputStream(),true); while(line != null) { if(line.split(":").length!=3){ int x=0; Random ran1 = new Random(); x=ran1.nextInt(8); while(x==num) { x=ran1.nextInt(8); System.out.println(x); } line=String.valueOf(num)+":"+String.valueOf(x)+":"+line; this.jt.append(line+"\r\n"); System.out.println(line); } out.println(line); line = br.readLine(); } s.shutdownOutput(); //终止 File filename1 = new File(str); BufferedWriter out1 = new BufferedWriter(new FileWriter(filename1)); out1.write(""); out1.close();
s.close(); |
3.4每台主机对经过自己的数据进行判断,因为每条数据的格式为源目主机号+“:”+目的主机号+“:”+内容,所以对进过自己的字符串进行分解,判断数组的第二个数据是否是自己的主机号,如果是,则进行显示并向后转发,否则直接转发。如果数组的第一个数据是自己的主机号则说明自己的数据发送完成,等待发送的数据全部收回后发送令牌帧。
while((str=bufIn.readLine())!=null) { System.out.println(str); if(str.equals("Token")){ this.jl.setBorder(BorderFactory.createLineBorder(Color.green,2)); String name="data"+String.valueOf(x)+".txt"; System.out.println(name); File filename = new File(name); // 要读取以上路径的input。txt文件 InputStreamReader reader = new InputStreamReader( new FileInputStream(filename)); // 建立一个输入流对象reader BufferedReader br = new BufferedReader(reader); // 建立一个对象,它把文件内容转成计算机能读懂的语言 String line = ""; line = br.readLine(); if(line==null) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } out.println("Token"); //s1.shutdownOutput(); //s1.close(); System.out.println(String.valueOf(port)+"send Token"); this.jl.setBorder(BorderFactory.createLineBorder(Color.blue,2)); }else{ send(name,s1,x); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }else{ String []info=str.split(":"); if(info[0].equals(String.valueOf(x))){ flag=true; //sendToken(x); }elseif(info[1].equals(String.valueOf(x))){ this.jt.append(info[0]+":"+info[2]+"\r\n"); out.println(str); }else{ out.println(str); } } } if(flag){ out.println("Token"); //s1.shutdownOutput(); this.jl.setBorder(BorderFactory.createLineBorder(Color.blue,2)); flag=false; //s1.close(); } s1.shutdownOutput(); |
4. 设计体会与小结
最开始的难点在于服务端要一直开启,这样开启服务端后就不能进行其他操作,因为服务端是一个死循环,加入线程解决了这个问题。
其次难点在于数据的发送,因为自己的服务器端不知道自己发送的数据是否接受完毕,所以加入了s1.shutdownOutput();来在一组数据发送完成后起到一个终止符的作用。
最后遇到的问题在于主机发送数据且没有令牌时的数据缓存,用了文件来解决这一问题。
通过实验让我对socket编程有了更好的掌握,对于进程和线程有了更深的理解。
下载链接:https://github.com/qiuzhikai/GUI.git
src里面有些文件是我测试功能时加的类,主要的类就是有文中的三个