20172303 2017-2018-2 《程序设计与数据结构》实验五报告
- 课程:《程序设计与数据结构》
- 班级: 1723
- 姓名: 范雯琪
- 学号:20172303
- 实验教师:王志强
- 助教:张旭升/刘伟康
- 实验日期:2018年6月13日
- 必修/选修: 必修
实验内容
- 任务一:实现中缀转后缀并计算结果的功能
- 参考数据结构应用
- 结对实现中缀表达式转后缀表达式的功能 MyBC.java
- 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
- 任务二:基于Java Socket实现客户端/服务器功能
- 一人负责客户端,一人负责服务器
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
- 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 任务三:基于任务二,使用3DES或AES算法将后缀表达式进行加密
- 一人负责客户端,一人负责服务器
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
- 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 任务四:基于任务三,使用DH进行密钥交换
- 一人负责客户端,一人负责服务器
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 任务五:基于任务四,使用MD5将明文加密
- 一人负责客户端,一人负责服务器
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
实验过程及结果
任务一
过程
- 因为在之前的结对编程中已经实现了这两个功能,并且老师说可以直接使用,我就直接使用了之前的
InfixToSuffix
类和Calculator
类进行中缀转后缀和计算,在ArithmeticTest
类中添加代码后实现。
运行结果
任务二
原理
TCP协议
- TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议.通过TCP协议传输,得到的是一个顺序的无差错的数据流。
- 发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。
- TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽
Java.net.Socket
- 此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
套接字的实际工作由SocketImpl
类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以配置它自身,以创建适合本地防火墙的套接字。 - java在包
java.net
中提供了两个类Socket
和ServerSocket
,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用很方便。 - 构造方法
其中address、host和port分别是双向连接中另一方的IP地址、主机名和端口号,stream指明socket是流socket还是数据报socket,localPort表示本地主机的端口号,localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址),impl是socket的父类,既可以用来创建serverSocket又可 以用来创建Socket。count则表示服务端所能支持的最大连接数。例如:
Socket client = new Socket("127.0.01.", 80);
ServerSocket server = new ServerSocket(80);
- 注意,在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23, 所以我们在选择端口号时,最好选择一个大于1023的数以防止发生冲突。
过程
- 使用
ipconfig
在cmd中查看自己的IP地址,要注意的是,在创建Socket的时候,如果是和结对伙伴的话,host
要填结对伙伴的IP,而如果是自己既做客户端又做服务器时,host
可以填127.0.0.1或localhost。
- 客户端
- 创建Socket
//1.建立客户端Socket连接,指定服务器位置和端口 Socket socket = new Socket("172.16.43.217",8800);
- 打开连接到Socket的输入流
//2.得到socket读写流 OutputStream outputStream = socket.getOutputStream(); PrintWriter(outputStream); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); //输入流 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
- 按照一定的协议对Socket进行读/写操作
Scanner scanner = new Scanner(System.in); System.out.println("请输入计算式:(以空格隔开每个运算符和运算数)"); String expr = scanner.nextLine(); InfixToSuffix infixToSuffix = new InfixToSuffix(); infixToSuffix.conversion(expr); System.out.println("我传送给服务器的信息为:"+infixToSuffix.getMessage()); String info = new String(infixToSuffix.getMessage().getBytes("utf-8")); outputStreamWriter.write(info); outputStreamWriter.flush(); socket.shutdownOutput(); //接收服务器的响应 String reply = null; while (!((reply = bufferedReader.readLine()) == null)){ System.out.println("接收服务器的信息为:" + reply); }
- 关闭Socket
bufferedReader.close(); inputStream.close(); outputStreamWriter.close(); outputStream.close(); socket.close();
- 服务器
- 服务器部分是我的结对伙伴完成的,这里放他的码云链接,其大致过程仍然是:
- 创建Socket
- 打开连接到Socket的输入/出流
- 按照一定的协议对Socket进行读/写操作
- 关闭Socket
运行结果
任务三
原理
DES加密算法
- DES加密是一种对称加密算法,它是分组密钥的代表算法,算法和密钥都需要良好的加密。
- 实现DES对称密钥的步骤有三步:
- 生成对称密钥;
- 使用对称密钥进行加密和解密;
- 从文件中获取加密时使用的密钥,使用密钥进行解密。
- 关键代码
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);
过程
- 刚开始我们在任务二的基础上进行代码增加,但是在运行时服务器端显示的是乱码,后来发现是因为传输的格式有问题,娄老师的博客里给的解密方式解密的必须是byte数组,之后我们就想办法把传过来的信息的格式改成byte数组,但是尝试了很多次都失败了。
- 而在在上网查的时候发现一篇博客中java的OutputStream.writeUTF()函数介绍了另一种方法:
DataOutputStream/DataInputStream
,其中有一个方法叫wrtieUTF
,它将 UTF-8 字符串写入字节流。先写入以字节表示的 UTF-8 字符串长度(作为 16 位整数),然后写入表示字符串字符的字节。使用这个方法,在传输之后转化成byte数组就很方便了。
- 客户端
- 服务器
运行结果
任务四
原理
DH算法
- 在DH算法中,主要数据通过对称密钥加密,而使用非对称加密来分发对称密钥,这样就将两者的优势结合了起来。
- DH算法是建立在DH公钥和私钥的基础上的,A需要和B共享密钥时,A和B各自生成DH公钥和私钥,公钥对外公布而私钥各自秘密保存。
过程
- 1.修改原先的
key_DH
类和keyAgree
类中的main方法,使其能够被引用。
- 2.创建DH公钥和私钥,将生成的公钥文件传给对方
客户端
服务器
运行结果
任务五
原理
- 使用Java计算指定字符串的消息摘要。java.security包中的MessageDigest类提供了计算消息摘要的方法。
- 运行代码
String x=args[0];
MessageDigest m=MessageDigest.getInstance("MD5");
m.update(x.getBytes("UTF8"));
byte s[ ]=m.digest( );
String result="";
for (int i=0; i<s.length; i++){
result+=Integer.toHexString((0x000000ff & s[i]) |
0xffffff00).substring(6);
}
System.out.println(result);
过程
在任务四的代码基础上将运行代码加入即可。
- 客户端
- 服务器
运行结果
实验过程中遇到的问题和解决过程
- 问题1:在第一次运行Socket时抛出IOException错误。
- 问题1解决方法:通过在网上查找发现这个错误好像是避免不了的,必须进行捕获或者抛出。
在创建socket时如果发生错误,将产生IOException,在程序中必须对之作出处理。所以在创建Socket或ServerSocket是必须捕获或抛出例外。
- 问题2:在传输的过程中产生错误
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
- 问题2解决方法:通过翻译,产生的原因应该是因为服务器和客户端产生的密钥不同,在把我的密钥文件复制给他之后就解决了问题。
- 问题3:在任务三传输的过程中,传输过去的一直显示的是乱码
- 问题3解决方法:在刚刚的任务三的实验过程中有写到,我们找到了另外一种方法来解决这个问题。
其他(感悟、思考等)
- 本学期的最后一次实验了,感觉java socket真的非常有意思,最后悔的是当时实验三的最后一个节点做的不是很好,所以这回又去复习了半天密码学的相关内容。
回顾一个学期的内容发现自己真的学到了很多,很开心。 - 下学期继续加油。