实验五《网络编程与安全》
实验目的
一、结对实现中缀表达式转后缀表达式的功能 ,从上面功能中获取的表达式中实现后缀表达式求值的功能
二、 基于Java Socket实现客户端/服务器功能,传输方式用TCP,客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
三、服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
四、客户端和服务器用DH算法进行3DES或AES算法的密钥交换
五、客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
实验内容及步骤
(一)结对实现中缀表达式转后缀表达式的功能 ,从上面功能中获取的表达式中实现后缀表达式求值的功能
1、设置IP地址
- 通过输入
ipconfig
指令来查询IP地址
2、结对实现中缀表达式转后缀表达式的功能 MyBC.java,进行测试
3、 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
4、知识点
- 中缀式转化后缀式
- 设立一个栈,存放运算符,首先栈为空;
- 从左到右扫描中缀式,若遇到操作数,直接输出,并输出一个空格作为两个操作数的分隔符;
- 若遇到运算符,则与栈顶比较,比栈顶级别高则进栈,否则退出栈顶元素并输出,然后输出一个空格作分隔符;
- 若遇到左括号,进栈;若遇到右括号,则一直退栈输出,直到退到左括号止。
当栈变成空时,输出的结果即为后缀表达式。
- 算符优先法求解表达式
建立符号运算的优先级关系表
- 设操作栈OPND、运算符栈OPTR,最低符号#压进OPTR
- 读入字符C,C若是操作数, 进OPND;若是运算符,与OPTR栈顶元素(A)比较,根据算符优先级,决定如何处理:
- A<C, C压入OPTR栈;
- A=C, A从OPTR出栈;
- A>C,A出栈,从OPND依次弹出两个操作数y、x, 计算Z=x A y,Z压入OPND栈。C压进OPTR.
重复上步操作直至表达式结果
(二)结对编程
基于Java Socket实现客户端/服务器功能,传输方式用TCP,客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器;服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端; 客户端显示服务器发送过来的结果
Client
/** *Created by xiang on 2018/5/23. */ import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; public class Client { public static final String IP_ADDR = "127.0.0.1";//服务器地址 public static final int PORT = 12345;//服务器端口号 public static void main(String[] args) { System.out.println("客户端启动..."); System.out.println("当接收到服务器端字符为 \"OK\" 的时候, 客户端将终止\n"); while (true) { Socket socket = null; try { //创建一个流套接字并将其连接到指定主机上的指定端口号 socket = new Socket(IP_ADDR, PORT); //读取服务器端数据 DataInputStream input = new DataInputStream(socket.getInputStream()); //向服务器端发送数据 DataOutputStream out = new DataOutputStream(socket.getOutputStream()); System.out.print("请输入: \t"); String str = new BufferedReader(new InputStreamReader(System.in)).readLine(); MyBC turner = new MyBC(); String str1 = turner.turn(str); int length=0,i=0; while(str1.charAt(i)!='\0'){ length++; i++; } String str2 = str1.substring(1,length-1); out.writeUTF(str2); String ret = input.readUTF(); System.out.println("服务器端返回过来的是: " + ret); /*if ("OK".equals(ret)) { System.out.println("客户端将关闭连接"); Thread.sleep(500); break; }*/ out.close(); input.close(); } catch (Exception e) { System.out.println("客户端异常:" + e.getMessage()); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { socket = null; System.out.println("客户端 finally 异常:" + e.getMessage()); } } } } } }
测试结果
(三)加密结对编程
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
- 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
client3
/** *Created by xiang on 2018/5/24. */ import java.io.*; import java.net.Socket; public class Client3{ public static void main(String[] args) { Socket s = null; try { s = new Socket("172.30.0.159", 4004); }catch (IOException e) { System.out.println("未连接到服务器"); } try { DataInputStream input = new DataInputStream(s.getInputStream()); System.out.print("请输入: \t"); String str = new BufferedReader(new InputStreamReader(System.in)).readLine(); MyBC turner = new MyBC(); String str1 = turner.turn(str); int length = 0, i = 0; while (str1.charAt(i) != '\0') { length++; i++; } String str2 = str1.substring(1, length - 1); SEnc senc = new SEnc(str2); senc.encrypt(); }catch(Exception e) { System.out.println("客户端异常:" + e.getMessage()); } File sendfile = new File("SEnc.dat"); File sendfile1 = new File("Keykb1.dat"); FileInputStream fis = null; FileInputStream fis1 = null; byte[] buffer = new byte[4096 * 5]; byte[] buffer1 = new byte[4096 * 5]; OutputStream os = null; if(!sendfile.exists() || !sendfile1.exists()){ System.out.println("客户端:要发送的文件不存在"); return; } try { fis = new FileInputStream(sendfile); fis1 = new FileInputStream(sendfile1); } catch (FileNotFoundException e1) { e1.printStackTrace(); } try { PrintStream ps = new PrintStream(s.getOutputStream()); ps.println("111/#" + sendfile.getName() + "/#" + fis.available()); ps.flush(); } catch (IOException e) { System.out.println("服务器连接中断"); } try { Thread.sleep(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { os = s.getOutputStream(); int size = 0; while((size = fis.read(buffer)) != -1){ System.out.println("客户端发送数据包,大小为" + size); os.write(buffer, 0, size); os.flush(); } } catch (FileNotFoundException e) { System.out.println("客户端读取文件出错"); } catch (IOException e) { System.out.println("客户端输出文件出错"); }finally{ try { if(fis != null) fis.close(); } catch (IOException e) { System.out.println("客户端文件关闭出错"); } } try { PrintStream ps1 = new PrintStream(s.getOutputStream()); ps1.println("111/#" + sendfile1.getName() + "/#" + fis1.available()); ps1.flush(); } catch (IOException e) { System.out.println("服务器连接中断"); } try { Thread.sleep(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { os = s.getOutputStream(); int size = 0; while((size = fis1.read(buffer1)) != -1){ System.out.println("客户端发送数据包,大小为" + size); os.write(buffer1, 0, size); os.flush(); } } catch (FileNotFoundException e) { System.out.println("客户端读取文件出错"); } catch (IOException e) { System.out.println("客户端输出文件出错"); }finally{ try { if(fis1 != null) fis1.close(); } catch (IOException e) { System.out.println("客户端文件关闭出错"); } } try{ DataInputStream input = new DataInputStream(s.getInputStream()); String ret = input.readUTF(); System.out.println("服务器端返回过来的是: " + ret); } catch (Exception e) { e.printStackTrace(); }finally { if (s != null) { try { s.close(); } catch (IOException e) { s = null; System.out.println("客户端 finally 异常:" + e.getMessage()); } } } } }
SEnc
/** *Created by xiang on 2018/5/24. */ import java.io.*; import java.security.*; import javax.crypto.*; public class SEnc{ static String s; public SEnc(String s){ this.s = s; } public void encrypt(){ try{ FileInputStream f=new FileInputStream("key1.dat"); ObjectInputStream b=new ObjectInputStream(f); Key k=(Key)b.readObject( ); Cipher cp=Cipher.getInstance("DESede"); cp.init(Cipher.ENCRYPT_MODE, k); byte ptext[]=s.getBytes("UTF8"); for(int i=0;i<ptext.length;i++){ System.out.print(ptext[i]+","); } System.out.println(""); byte ctext[]=cp.doFinal(ptext); for(int i=0;i<ctext.length;i++){ System.out.print(ctext[i] +","); } FileOutputStream f2=new FileOutputStream("SEnc.dat"); f2.write(ctext); }catch(Exception e){ System.out.println(e); } } }
结果
(四)密钥分发结对编程
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
Client4
/** *Created by xiang on 2018/5/26. */ import java.io.*; import java.net.Socket; public class Client4 { public static void main(String[] args) { Socket s = null; try { s = new Socket("172.30.0.159", 4004); }catch (IOException e) { System.out.println("未连接到服务器"); } try { DataInputStream input = new DataInputStream(s.getInputStream()); System.out.print("请输入: \t"); String str = new BufferedReader(new InputStreamReader(System.in)).readLine(); MyBC turner = new MyBC(); String str1 = turner.turn(str); int length = 0, i = 0; while (str1.charAt(i) != '\0') { length++; i++; } String str2 = str1.substring(1, length - 1); SEnc senc = new SEnc(str2);//指定后缀表达式为明文字符串 senc.encrypt();//加密 }catch(Exception e) { System.out.println("客户端异常:" + e.getMessage()); } File sendfile = new File("SEnc.dat"); File sendfile1 = new File("Keykb1.dat"); /**定义文件输入流,用来打开、读取即将要发送的文件*/ FileInputStream fis = null; FileInputStream fis1 = null; /**定义byte数组来作为数据包的存储数据包*/ byte[] buffer = new byte[4096 * 5]; byte[] buffer1 = new byte[4096 * 5]; /**定义输出流,使用socket的outputStream对数据包进行输出*/ OutputStream os = null; if(!sendfile.exists() || !sendfile1.exists()){ System.out.println("客户端:要发送的文件不存在"); return; } try { fis = new FileInputStream(sendfile); fis1 = new FileInputStream(sendfile1); } catch (FileNotFoundException e1) { e1.printStackTrace(); } try { PrintStream ps = new PrintStream(s.getOutputStream()); ps.println("111/#" + sendfile.getName() + "/#" + fis.available()); ps.flush(); } catch (IOException e) { System.out.println("服务器连接中断"); } try { Thread.sleep(2000); } catch (InterruptedException e1) { e1.printStackTrace(); }传输的关键代码 try { /**获取socket的OutputStream,以便向其中写入数据包*/ os = s.getOutputStream(); /** size 用来记录每次读取文件的大小*/ int size = 0; /**使用while循环读取文件,直到文件读取结束*/ while((size = fis.read(buffer)) != -1){ System.out.println("客户端发送数据包,大小为" + size); /**向输出流中写入刚刚读到的数据包*/ os.write(buffer, 0, size); /**刷新一下*/ os.flush(); } } catch (FileNotFoundException e) { System.out.println("客户端读取文件出错"); } catch (IOException e) { System.out.println("客户端输出文件出错"); }finally{ try { if(fis != null) fis.close(); } catch (IOException e) { System.out.println("客户端文件关闭出错"); }//catch (IOException e) }//finally try { PrintStream ps1 = new PrintStream(s.getOutputStream()); ps1.println("111/#" + sendfile1.getName() + "/#" + fis1.available()); ps1.flush(); } catch (IOException e) { System.out.println("服务器连接中断"); } try { Thread.sleep(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { /**获取socket的OutputStream,以便向其中写入数据包*/ os = s.getOutputStream(); /** size 用来记录每次读取文件的大小*/ int size = 0; /**使用while循环读取文件,直到文件读取结束*/ while((size = fis1.read(buffer1)) != -1){ System.out.println("客户端发送数据包,大小为" + size); /**向输出流中写入刚刚读到的数据包*/ os.write(buffer1, 0, size); /**刷新一下*/ os.flush(); } } catch (FileNotFoundException e) { System.out.println("客户端读取文件出错"); } catch (IOException e) { System.out.println("客户端输出文件出错"); }finally{ try { if(fis1 != null) fis1.close(); } catch (IOException e) { System.out.println("客户端文件关闭出错"); }//catch (IOException e) }//finally try{ DataInputStream input = new DataInputStream(s.getInputStream()); String ret = input.readUTF(); System.out.println("服务器端返回过来的是: " + ret); } catch (Exception e) { e.printStackTrace(); }finally { if (s != null) { try { s.close(); } catch (IOException e) { s = null; System.out.println("客户端 finally 异常:" + e.getMessage()); } } } }//public static void main(String[] args) }//public class ClientSend
结果
(五)密钥分发结对编程
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
Client5
import java.io.*; import java.net.Socket; import java.security.MessageDigest; public class Client5 { public static void main(String[] args) { Socket s = null; try { s = new Socket("172.30.0.159", 4004); }catch (IOException e) { System.out.println("未连接到服务器"); } try { DataInputStream input = new DataInputStream(s.getInputStream()); DataOutputStream out = new DataOutputStream(s.getOutputStream()); System.out.print("请输入: \t"); String str = new BufferedReader(new InputStreamReader(System.in)).readLine(); MyBC turner = new MyBC(); String str1 = turner.turn(str); int length = 0, i = 0; while (str1.charAt(i) != '\0') { length++; i++; } String str2 = str1.substring(1, length - 1); //以下对明文产生消息摘要 String x=str2; MessageDigest m=MessageDigest.getInstance("MD5"); m.update(x.getBytes("UTF8")); byte s2[ ]=m.digest( ); String result=""; for (int j=0; j<s2.length; j++){ result+=Integer.toHexString( (0x000000ff & s2[j]) | 0xffffff00).substring(6); } //System.out.println(result); out.writeUTF(result); SEnc senc = new SEnc(str2);//指定后缀表达式为明文字符串 senc.encrypt();//加密 }catch(Exception e) { System.out.println("客户端异常:" + e.getMessage()); } File sendfile = new File("SEnc.dat"); File sendfile1 = new File("Apub.dat"); /**定义文件输入流,用来打开、读取即将要发送的文件*/ FileInputStream fis = null; FileInputStream fis1 = null; /**定义byte数组来作为数据包的存储数据包*/ byte[] buffer = new byte[4096 * 5]; byte[] buffer1 = new byte[4096 * 5]; /**定义输出流,使用socket的outputStream对数据包进行输出*/ OutputStream os = null; if(!sendfile.exists() || !sendfile1.exists()){ System.out.println("客户端:要发送的文件不存在"); return; } try { fis = new FileInputStream(sendfile); fis1 = new FileInputStream(sendfile1); } catch (FileNotFoundException e1) { e1.printStackTrace(); } try { PrintStream ps = new PrintStream(s.getOutputStream()); ps.println("111/#" + sendfile.getName() + "/#" + fis.available()); ps.flush(); } catch (IOException e) { System.out.println("服务器连接中断"); } try { Thread.sleep(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { /**获取socket的OutputStream,以便向其中写入数据包*/ os = s.getOutputStream(); /** size 用来记录每次读取文件的大小*/ int size = 0; /**使用while循环读取文件,直到文件读取结束*/ while((size = fis.read(buffer)) != -1){ System.out.println("客户端发送数据包,大小为" + size); /**向输出流中写入刚刚读到的数据包*/ os.write(buffer, 0, size); /**刷新一下*/ os.flush(); } } catch (FileNotFoundException e) { System.out.println("客户端读取文件出错"); } catch (IOException e) { System.out.println("客户端输出文件出错"); }finally{ try { if(fis != null) fis.close(); } catch (IOException e) { System.out.println("客户端文件关闭出错"); }//catch (IOException e) }//finally try { PrintStream ps1 = new PrintStream(s.getOutputStream()); ps1.println("111/#" + sendfile1.getName() + "/#" + fis1.available()); ps1.flush(); } catch (IOException e) { System.out.println("服务器连接中断"); } try { Thread.sleep(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { os = s.getOutputStream(); int size = 0; while((size = fis1.read(buffer1)) != -1){ System.out.println("客户端发送数据包,大小为" + size); /**向输出流中写入刚刚读到的数据包*/ os.write(buffer1, 0, size); /**刷新一下*/ os.flush(); } } catch (FileNotFoundException e) { System.out.println("客户端读取文件出错"); } catch (IOException e) { System.out.println("客户端输出文件出错"); }finally{ try { if(fis1 != null) fis1.close(); } catch (IOException e) { System.out.println("客户端文件关闭出错"); }//catch (IOException e) }//finally try{ String yes = "向服务器发送MD5"; DataOutputStream out = new DataOutputStream(s.getOutputStream()); out.writeUTF(yes); DataInputStream input = new DataInputStream(s.getInputStream()); String ret = input.readUTF(); System.out.println("服务器端返回过来的是: " + ret); } catch (Exception e) { e.printStackTrace(); }finally { if (s != null) { try { s.close(); } catch (IOException e) { s = null; System.out.println("客户端 finally 异常:" + e.getMessage()); } } } }//public static void main(String[] args) }//public class ClientSend
结果
遇到问题及解决方案
问题1:无法进行测试
- 问题1解决方案:测试前应填入数据,再进行测试
问题2:进行测试时,报错
input exception reported
问题2解决方案:搜了一下,发现是格式的问题,字符间应有空格相连接
问题3:出现
java.net.SocketException: Connection reset
,客户端输出文件出错
问题3解决方案:该异常在客户端和服务器端均有可能发生,引起该异常的原因有两个,第一个就是如果一端的Socket被关闭(或主动关闭或者因为异常退出而引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。另一个是一端退出,但退出时并未关闭该连接,另一端如果在从连接中读数据则抛出该异常(Connection reset)。简单的说就是在连接断开后的读和写操作引起的。 在读取信息时不应该关闭客户端。
问题4:无法运行程序,报错
问题4解决方案:重新启动,或者换个端口,比如之前是
4004
,就可以换成1234
。
统计PSP(Personal Software Process)时间:
步骤 | 耗时(h) | 百分比 |
---|---|---|
设计 | 2 | 20% |
代码实现 | 5 | 50% |
测试 | 2 | 20% |
分析总结 | 1 | 10% |
实验小结
本次实验过程中出现了很多问题,反反复复折腾了一周,不过在不断地从书中网上查询解决问题的过程中,收获颇丰。其实不管是学习生活还是今后工作,有时候需要适当地逼一下自己,可能在某一方面投入90%精力但仍不见希望时,我们需要做的可能就是再努力10%。这应该是本学期最后一次实验了,虽然Java实验真的磨人,不过不论从知识的提升还是耐力的磨练,总算是有所收获,还是挺感谢娄老师。希望自己以后也能屏住最后一口气,撑住往前走。