2018-2019-20175329 实验五《网络编程与安全》实验报告
实验步骤
任务一
- 两人一组结对编程
- 结对实现中缀表达式转后缀表达式的功能 MyBC.java
- 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
实验过程 - 之前结队编程做过一样的题目,所以就肥肠简单啦
- 为什么要将中缀表达式转为后缀表达式
- 中缀表达式,虽然符合我们的数学计算习惯,但是并不符合计算机运算的方式
- 后缀表达式严格按照从左到右进行计算的模式 符合计算机运行方式而中缀表达式需要计算机遇到符号后向后扫描一位 若为括号或优先级更高的操作符还需要向后继续扫描
- 后缀表达式的定义
- 后缀表达式(又称为逆波兰reverse polish)就是不需要括号就可以实现调整运算顺序的一种技法。
- 比如:
ab+cde+**
,改为中缀表达式其实是(a+b)*((d+e)*c)
- 后缀表达式不含括号
- 中缀转后缀
- 重要的数据结构----栈
- 如果读入操作数,则直接放入输出字符串;如果读入一般运算符如+-*/,则放入堆栈,但是放入堆栈之前必须要检查栈顶,并确定栈顶运算符的优先级比放入的运算符的优先级低;如果放入的优先级较低,则需要将栈顶的运算符放入输出字符串
- 如果读入(,因为左括号优先级最高,因此放入栈中,但是注意,当左括号放入栈中后,则优先级最低
- 如果读入),则将栈中运算符取出放入输出字符串,直到取出(为止,注意:()不输出到输出字符串顺序读完表达式,如果栈中还有操作符,则弹出,并放入输出字符串
- 后缀表达式的计算
- 从左到右扫描后缀表达式
- 如果遇到操作数,将其压入栈中
- 如果遇到操作符,则从栈中弹出两个操作数,计算结果然后把结果入栈直到遍历完后缀表达式,则计算完成
- 此时的栈顶元素即为计算结果。
- 实验代码
/* creat by xyw,cyy
*/
import java.util.Stack;
public class MyBC {
MyBC(){}
public static String infixToSuffix(String exp){
Stack<String> s = new Stack<String>(); // 创建操作符堆栈
String suffix = ""; // 要输出的后缀表达式字符串
String suffix1 = ""; //上一次的后缀表达式
String suffix2 = "";
String str[] = exp.split(" ");
int length = str.length; // 输入的中缀表达式的长度
String temp="";
for (int i = 0; i < length; i++) { // 对该中缀表达式的每一个字符并进行判断
switch (str[i]) {
case " ":break; // 忽略空格
case "(":
s.push(str[i]); // 如果是左括号直接压入堆栈
break;
case "+":
case "-":
if(s.size() != 0){ // 碰到'+' '-',将栈中的所有运算符全部弹出去,直至碰到左括号为止,输出到队列中去
temp = s.pop();
if (temp.equals("(")) { // 将左括号放回堆栈,终止循环
s.push(temp);
s.push(str[i]);
break;
}
else{
s.push(str[i]);
suffix2 = suffix2 + temp + " ";
break;
}
}
else{
s.push(str[i]); // 说明是当前为第一次进入或者其他前面运算都有括号等情况导致栈已经为空,此时需要将符号进栈
break;
}
// 如果是乘号或者除号,则弹出所有序列,直到碰到加好、减号、左括号为止,最后将该操作符压入堆栈
case "*":
case "÷":
if(s.size()!=0){
temp = s.pop();
if(temp.equals("+")||temp.equals("-")||temp.equals("(")){
s.push(temp);
s.push(str[i]);
break;
}
else{
s.push(str[i]);
suffix2 = suffix2+temp+" ";
break;
}
}
else {
s.push(str[i]); //当前为第一次进入或者其他前面运算都有括号等情况导致栈已经为空,此时需要将符号进栈
break;
}
// 如果碰到的是右括号,则距离栈顶的第一个左括号上面的所有运算符弹出栈并抛弃左括号
case ")":
while (!s.isEmpty()) {
temp = s.pop();
if (temp.equals("(")) {
break;
} else {
suffix2 = suffix2+temp+" ";
}
}
break;
// 默认情况,如果读取到的是数字,则直接送至输出序列
default:
suffix2 = suffix2+str[i]+" ";
break;
}
}
// 如果堆栈不为空,则把剩余运算符一次弹出,送至输出序列
while (s.size() != 0) {
suffix2 = suffix2+s.pop()+" ";
}
if(suffix1.equals("")){ //第一个题目
suffix1 = suffix2;
suffix = suffix2;
}
else{
if(suffix2.equals(suffix1))
suffix = "";
else
suffix = suffix2;
}
suffix1 = suffix2;
return suffix;
}
}
-
实验截图
-
任务二
-
结对编程:1人负责客户端,一人负责服务器
-
注意责任归宿,要会通过测试证明自己没有问题
-
基于Java Socket实现客户端/服务器功能,传输方式用TCP
-
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
-
服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
-
客户端显示服务器发送过来的结果
-
实验代码
服务器
import java.io.*;
import java.net.*;
public class Server {
public static void main(String args[]) {
int answer;
ServerSocket serverForClient=null;
Socket socketOnServer=null;
DataOutputStream out=null;
DataInputStream in=null;
try { serverForClient = new ServerSocket(2010);
}
catch(IOException e1) {
System.out.println(e1);
}
try{ System.out.println("等待客户呼叫");
socketOnServer = serverForClient.accept(); //堵塞状态,除非有客户呼叫
out=new DataOutputStream(socketOnServer.getOutputStream());
in=new DataInputStream(socketOnServer.getInputStream());
String s=in.readUTF(); // in读取信息,堵塞状态
System.out.println("服务器收到客户的提问:"+s);
MyDC d=new MyDC();
answer=d.evaluate(s);
out.writeUTF(answer+"");
Thread.sleep(500);
}
catch(Exception e) {
System.out.println("客户已断开"+e);
}
}
}
**客户端**
import java.io.*;
import java.net.*;
import java.lang.*;
import java.util.Scanner;
public class Client {
public static void main(String args[]) {
Socket mysocket;
DataInputStream in=null;
DataOutputStream out=null;
try{ mysocket=new Socket("127.1.0.0",2010);
in=new DataInputStream(mysocket.getInputStream());
out=new DataOutputStream(mysocket.getOutputStream());
System.out.println("请输入算式:");
Scanner scanner=new Scanner(System.in);
String str=scanner.nextLine();
MyBC b=new MyBC();
str=b.result(str);
out.writeUTF(str);
String s=in.readUTF(); //in读取信息,堵塞状态
System.out.println("客户收到服务器的回答:"+s);
Thread.sleep(500);
}
catch(Exception e) {
System.out.println("服务器已断开"+e);
}
}
}
- 实验过程
- 使用java.net.Socket对象来表示一个套接字
- 使用Socket的构造方法创建套接字,如:
public Socket(java.lang.String host, int port)
。其中,host是远程机器名或IP地址,port是端口号,IP地址查询方法如下:
- 调用Socket类的getOutputStream方法来获取一个java.io.OutputStream对象。要向远程应用程序发送文本,通常要从返回的OutputStream对象构建一个java.io.PrintWriter对象。要接收来自连接的另一端的字节流
- 调用Socket类的getInputStream方法,它返回一个java.io.InputStream
- ServerSocket是服务器套接字的一个实现,一旦服务器套接字获得了一个连接请求,它就会创建一个Socket实例
- 任务截图
任务三 - 加密结对编程:1人负责客户端,一人负责服务器
- 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用DES或AES算法加密后通过网络把密文发送给服务器
- 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
实验原理
本部分需要参考Java密码学算法中“Java对称加密-DES算法”部分的内容,不同的是我和伙伴没有通过对象序列化方式写入文件中,而是直接将密钥进行传递,具体过程如下: - 客户端
- 获取密钥生成器
KeyGenerator kg=KeyGenerator.getInstance("DESede")
- 初始化密钥生成器
kg.init(168)
; - 生成密钥
SecretKey k=kg.generateKey( )
; - 获取主要编码格式
byte[ ] kb=k.getEncoded( )
; - 传送密钥长度及密钥内容
- 创建密码器
Cipher cp=Cipher.getInstance("DESede")
; - 初始化密码器
cp.init(Cipher.ENCRYPT_MODE, k)
; - 获取等待加密的明文
byte ptext[]=s.getBytes("UTF8")
; - 执行加密
byte []ptext=cp.doFinal(ctext)
- 服务器
- 接收密钥长度
- 接收密钥内容
- 创建密码器
Cipher cp=Cipher.getInstance("DESede")
; - 初始化密码器
cp.init(Cipher.DECRYPT_MODE, k)
; - 执行解密
byte []ptext=cp.doFinal(ctext)
- 实验代码
客户端
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class Client1 {
public static void main(String[] args) {
Scanner inn = new Scanner(System.in);
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try{
mysocket = new Socket("127.0.0.1",2010);
in = new DataInputStream(mysocket.getInputStream());
out = new DataOutputStream(mysocket.getOutputStream());
KeyGenerator kg=KeyGenerator.getInstance("DESede");
kg.init(168);
SecretKey k=kg.generateKey( );
byte[ ] kb=k.getEncoded( );
out.writeUTF(kb.length+"");
for(int i=0; i<kb.length; i++){
out.writeUTF(kb[i]+"");
}
System.out.println("请输入中缀表达式:");
String infix = inn.nextLine();
MyBC myBC = new MyBC();
String suffix = myBC.infixToSuffix(infix);
Cipher cp=Cipher.getInstance("DESede");
cp.init(Cipher.ENCRYPT_MODE, k);
byte ptext[]=suffix.getBytes("UTF8");
byte ctext[]=cp.doFinal(ptext);
out.writeUTF(ctext.length+"");
for(int i=0; i<ctext.length; i++){
out.writeUTF(ctext[i]+"");
}
String result = in.readUTF();
System.out.println("收到请回复"+result);
Thread.sleep(500);
}catch (Exception e){
System.out.println("断开连接"+e);
}
}
}
服务器
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.*;
public class Sever1 {
public static void main(String[] args) {
ServerSocket serverForClient = null;
Socket socketOnServer = null;
DataOutputStream out = null;
DataInputStream in = null;
MyDC myDC = new MyDC();
try{
serverForClient = new ServerSocket(2010);
}catch (IOException e1){
System.out.println("请客户端回复"+e1);
}
try{
System.out.println("客户端收到");
socketOnServer = serverForClient.accept();
out = new DataOutputStream(socketOnServer.getOutputStream());
in = new DataInputStream(socketOnServer.getInputStream());
String keylength = in.readUTF();
byte []kb = new byte[Integer.parseInt(keylength)];
for(int i=0; i<Integer.parseInt(keylength); i++){
String t = in.readUTF();
kb[i] = Byte.parseByte(t);
}
String clength = in.readUTF();
byte []ctext = new byte[Integer.parseInt(clength)];
for(int i=0; i<Integer.parseInt(clength); i++){
String temp = in.readUTF();
ctext[i] = Byte.parseByte(temp);
}
SecretKeySpec k=new SecretKeySpec(kb,"DESede");
Cipher cp=Cipher.getInstance("DESede");
cp.init(Cipher.DECRYPT_MODE, k);
byte []ptext=cp.doFinal(ctext);
String suffix = new String(ptext,"UTF8");
System.out.println("请提问"+suffix);
out.writeUTF(myDC.evaluate(suffix)+"");
Thread.sleep(500);
}catch (Exception e){
System.out.println("已断开连接");
}
}
}
实验截图
4.任务四
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
- 实验原理
- 本部分需要参考Java密码学算法中“使用密钥协定创建共享密钥”部分的内容,对博客中的代码进行了部分更改如下:
Key_DH
和KeyAgree
主类的参数改为由客户端和服务器传递,并修改方法名
public static void DH(String str1,String str2) throws Exception
public static void Agree(String str1,String str2)
- 客户端和服务器分别产生自己的公钥和私钥,并用过字节数组的形式分别向另一方传递自己的公钥;
- 客户端和服务器接受对方的公钥后利用自己的私钥创建共享密钥
- 创建密钥协定对象
KeyAgreement ka=KeyAgreement.getInstance("DH")
; - 初始化密钥协定对象
ka.init(prk)
; - 执行密钥协定
ka.doPhase(pbk,true)
; - 生成共享信息
byte[ ] sb=ka.generateSecret()
; - 从文件中读取信息并给出共享密钥
实验代码 - 客户端
import javax.crypto.spec.*;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client4 {
public static void main(String[] args) {
Scanner inn = new Scanner(System.in);
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try {
mysocket = new Socket("127.0.0.1", 2010);
in = new DataInputStream(mysocket.getInputStream());
out = new DataOutputStream(mysocket.getOutputStream());
KeyGenerator kg = KeyGenerator.getInstance("DESede");
kg.init(168);
SecretKey k = kg.generateKey();
byte[] kb = k.getEncoded();
System.out.println("请输入中缀表达式:");
String infix = inn.nextLine();
MyBC myBC = new MyBC();
String suffix = myBC.infixToSuffix(infix);
System.out.println(suffix);
//中缀表达式加密
Cipher cp = Cipher.getInstance("DESede");
cp.init(Cipher.ENCRYPT_MODE, k);
byte ptext[] = suffix.getBytes("UTF8");
byte ctext[] = cp.doFinal(ptext);
out.writeUTF(ctext.length + "");
for (int i = 0; i < ctext.length; i++) {
out.writeUTF(ctext[i] + "");
}
//对密钥进行加密
KeyAgree keyAgree = new KeyAgree();
SecretKeySpec k1 = keyAgree.KeyAgree("Serverpub.dat","Clientpri.dat");
cp.init(Cipher.ENCRYPT_MODE, k1);
byte ckey[] = cp.doFinal(kb);
out.writeUTF(ckey.length + "");
for (int i = 0; i < ckey.length; i++) {
out.writeUTF(ckey[i] + "");
}
String result = in.readUTF();
System.out.println("服务器收到请回去" + result);
Thread.sleep(500);
} catch (Exception e) {
System.out.println("服务器断开连接" + e);
}
}
}
- 服务器
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.*;
public class Server4 {
public static void main(String[] args) {
ServerSocket serverForClient = null;
Socket socketOnServer = null;
DataOutputStream out = null;
DataInputStream in = null;
MyDC myDC = new MyDC();
try{
serverForClient = new ServerSocket(2010);
}catch (IOException e1){
System.out.println("请提出问题"+e1);
}
try{
System.out.println("等待中");
socketOnServer = serverForClient.accept();
out = new DataOutputStream(socketOnServer.getOutputStream());
in = new DataInputStream(socketOnServer.getInputStream());
//获取密文
String clength = in.readUTF();
byte []ctext = new byte[Integer.parseInt(clength)];
for(int i=0; i<Integer.parseInt(clength); i++){
String temp = in.readUTF();
ctext[i] = Byte.parseByte(temp);
}
//获取密钥
String keylength = in.readUTF();
byte []ckey = new byte[Integer.parseInt(keylength)];
for(int i=0; i<Integer.parseInt(keylength); i++){
String temp = in.readUTF();
ckey[i] = Byte.parseByte(temp);
}
//密钥解密
SecretKeySpec k1 = KeyAgree.KeyAgree("Clientpub.dat","Serverpri.dat");
Cipher cp=Cipher.getInstance("DESede");
cp.init(Cipher.DECRYPT_MODE, k1);
byte []pkey=cp.doFinal(ckey);
//密文解密
SecretKeySpec k=new SecretKeySpec(pkey,"DESede");
cp.init(Cipher.DECRYPT_MODE, k);
byte []ptext=cp.doFinal(ctext);
String suffix = new String(ptext,"UTF8");
System.out.println("受到提问"+suffix);
out.writeUTF(myDC.evaluate(suffix)+"");
Thread.sleep(500);
}catch (Exception e){
System.out.println("已断开连接");
}
}
}
实验截图
- 任务五
- 完整性校验结对编程:1人负责客户端,一人负责服务器
- 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用
MyBC.java
的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器 - 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用
MyDC.java
的功能计算后缀表达式的值,把结果发送给客户端 - 客户端显示服务器发送过来的结果
- 实验过程
- Java摘要算法-
MD5
- 生成
MessageDigest
对象,MessageDigest m=MessageDigest.getInstance("MD5")
- 传入需要计算的字符串,
m.update(x.getBytes("UTF8" ))
- 计算消息摘要,
byte s[ ]=m.digest( )
- 处理计算结果
实验截图