20165203实验五 网络编程与安全
20165203实验五 网络编程与安全
任务一
两人一组结对编程:
0. 参考(http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA)
- 结对实现中缀表达式转后缀表达式的功能 MyBC.java
- 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
- 上传测试代码运行结果截图和码云链接
一、实验过程:
1.根据娄老师的博客,我们先了解前缀表示法、中缀表示法和后缀表示法的概念:
表达式Exp = S1 + OP + S2
(S1 ,S2是两个操作数,OP为运算符)有三种标识方法:
OP + S1 + S2
为前缀表示法
S1 + OP + S2
为中缀表示法
S1 + S2 + OP
为后缀表示法
例如:Exp = a * b + (c - d / e) * f
前缀式: + * a b * - c / d e f
中缀式: a * b + c - d / e * f
后缀式: a b * c d e / - f * +
2.中缀表达式转化成后缀表达式的过程:
- 设立一个栈,存放运算符,首先栈为空;
- 从左到右扫描中缀式,若遇到操作数,直接输出,并输出一个空格作为两个操作数的分隔符;
- 若遇到运算符,则与栈顶比较,比栈顶级别高则进栈,否则退出栈顶元素并输出,然后输出一个空格作分隔符;
- 若遇到左括号,进栈;若遇到右括号,则一直退栈输出,直到退到左括号止。
- 当栈变成空时,输出的结果即为后缀表达式。
3.中缀表达式转化成后缀表达式代码实现:
import java.util.*;
public class NewMyBC {
String beforeExpression;//未转化的中缀表达式
String afterExpression = "";//转化后的后缀表达式
int left = 0;//定义左括号数
int right = 0;//定义右括号数
//判断符号的级别
public int judgeGrade(char chi) {
int grade = 0;
switch (chi) {
//左括号是第一级
case '(':
grade = 1;
break;
//+和-是第二级
case '+':
case '-':
grade = 2;
break;
//*和÷是第三级
case '*':
case '÷':
grade = 3;
break;
// "/"是第四级
case '/':
grade = 4;
break;
//右括号是第五级
case ')':
grade = 5;
break;
default:
grade = 0;
}
return grade;
}
public void setBeforeExpression(String exp) {
beforeExpression=exp;
}
public String transformWay() {
Stack stack = new Stack();//新建一个空栈
int i = 0;
char op;
while (i < beforeExpression.length()) {
op = beforeExpression.charAt(i);//op存放从中缀表达式中取到的元素
if (op >= '0' && op <= '9') {
afterExpression = afterExpression + op;//如果是数字,则直接输出至后缀表达式
} else if (op == '+' || op == '-' || op == '*' || op == '÷') {
afterExpression = afterExpression + ' ';//有运算符,在数字之间加空格
// 如果栈为空,则运算符直接入栈
if (stack.empty()) {
stack.push(op);
}
//根据符号的级别判断操作
else if (judgeGrade(op) > judgeGrade((char) stack.peek())) {
stack.push(op);//比栈顶级别高或相等,直接入栈
} else {
afterExpression = afterExpression + String.valueOf(stack.pop()) + ' ';//否则直接输出
i--;
}
} else if (op == '(') {
left++;
stack.push(op);//左括号直接入栈
} else if (op == ')') {
afterExpression += " ";
right++;
while ((char) stack.peek() != '(') {//右括号,出栈,直到左括号为止
afterExpression = afterExpression + String.valueOf(stack.pop()) + " ";
}
stack.pop();
}
i++;
}
afterExpression += " ";
while(!stack.empty()){
afterExpression=afterExpression+String.valueOf(stack.pop())+" ";
}
if (left != right) {
System.out.println("括号有误");
System.exit(0);
}
return afterExpression;
}
}
4.后缀表达式求值的步骤:
- 设置一个操作数栈,开始栈为空;
- 从左到右扫描后缀表达式,遇操作数,进栈;
- 若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符左边,运算后的结果再进栈,直到后缀表达式扫描完毕。
此时,栈中仅有一个元素,即为运算的结果。
5.求值代码:
import java.util.Stack;
public class MyDC {
/**
* constant for addition symbol
*/
private final char ADD = '+';
/**
* constant for subtraction symbol
*/
private final char SUBTRACT = '-';
/**
* constant for multiplication symbol
*/
private final char MULTIPLY = '*';
/**
* constant for division symbol
*/
private final char DIVIDE = '÷';
/**
* the stack
*/
private Stack<Integer> stack;//存放操作数的栈
public MyDC() {
stack = new Stack<Integer>();
}
public int evaluate(String expr) {
int op1, op2, result = 0;
String token;
StringTokenizer tokenizer = new StringTokenizer(expr);//划分表达式
while (tokenizer.hasMoreTokens()) {
token = tokenizer.nextToken();//将算数表达式分解的
if (isOperator(token))//见下方isOperateor方法,当是运算符的时候进入if语句
{
op2 = (stack.pop()).intValue();
op1 = (stack.pop()).intValue();//弹出最上面两个操作数
result = evalSingleOp(token.charAt(0), op1, op2);//见下方evaSingleOp方法
stack.push(new Integer(result));//将计算结果压栈
} else{
stack.push(new Integer(Integer.parseInt(token)));//操作数入栈
}
}
return result;//输出结果
}
private boolean isOperator(String token)//判断是否为运算符,注意用equal语句比较字符串
{
return (token.equals("+") || token.equals("-") ||
token.equals("*") || token.equals("÷"));
}
private int evalSingleOp(char operation, int op1, int op2) {
int result = 0;
switch (operation) {
case ADD:
result = op1 + op2;
break;
case SUBTRACT:
result = op1 - op2;
break;
case MULTIPLY:
result = op1 * op2;
break;
case DIVIDE:
result = op1 / op2;
}
return result;
}
}
6.测试代码如下:
import java.util.Scanner;
public class MyDCTest {
public static void main(String[] args) {
String expression, again;
int result;
try {
Scanner in = new Scanner(System.in);
do {
MyDC evaluator = new MyDC();
System.out.println("Enter a valid postfix expression: ");
expression = in.nextLine();
result = evaluator.evaluate(expression);
System.out.println();
System.out.println("That expression equals " + result);
System.out.print("Evaluate another expression [Y/N]? ");
again = in.nextLine();
System.out.println();
}
while (again.equalsIgnoreCase("y"));
} catch (Exception IOException) {
System.out.println("Input exception reported");
}
}
}
二、实验截图:
三、码云链接
任务二
结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
- 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
一、实验过程:
1.经过与搭档协商,我负责客户端(Client.java)部分,搭档负责服务端(Server.java)部分。
2.客户端部分的代码如下:
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.*;
import java.net.*;
import java.util.*;
public class Client {
public static void main(String[] args) {
Socket socket;//套接字对象
DataInputStream in=null;//输入流
DataOutputStream out=null;//输出流
//中缀表达式的输入
String str;
Scanner scanner=new Scanner(System.in);
System.out.println("请输入中缀表达式:");
str=scanner.nextLine();
try {
socket=new Socket("localhost",5203);
in=new DataInputStream(socket.getInputStream());
out=new DataOutputStream(socket.getOutputStream());
out.writeUTF(str);
String s=in.readUTF();
System.out.println("结果为:\n"+s);
}
catch (Exception e) {
System.out.println("服务器已断开"+e);
}
}
}
二、实验截图:
三、码云链接
任务三
加密结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
- 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
一、实验过程:
1.我负责客户端,负责编写Client.java
.
2.根据娄老师的密码学算法博客,我和搭档所共同完成的DES算法代码如下:
import java.io.*;
import javax.crypto.*;
public class Skey_DES{
public static void main(String args[]) throws Exception{
KeyGenerator kg=KeyGenerator.getInstance("DESede");
kg.init(168);
SecretKey k=kg.generateKey( );
FileOutputStream f=new FileOutputStream("key1.dat");
ObjectOutputStream b=new ObjectOutputStream(f);
b.writeObject(k);
}
}
3.Client.java代码如下:
在之前的代码上,增加如下代码:
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
String mess;
Scanner scanner=new Scanner(System.in);
System.out.println("请输入问题");
mess=scanner.nextLine();
String key="";
int n=-1;
byte [] a=new byte[128];
try{ File f=new File("key1.dat");
InputStream in = new FileInputStream(f);
while((n=in.read(a,0,100))!=-1) {
key=key+new String (a,0,n);
}
in.close();
}
catch(IOException e) {
System.out.println("File read Error"+e);
}
System.out.println("客户端提出的问题为:"+mess);
NewMyBC mybc=new NewMyBC();
String str;
mybc.setBeforeExpression(mess);
str=mybc.transformWay();
String mi=EncryptDecrypt.AESEncode(key,str);
System.out.println("客户端加密后密文为:"+mi);
Socket socket;
DataInputStream in=null;
DataOutputStream out=null;
try{ socket=new Socket("localhost",5006);
in=new DataInputStream(socket.getInputStream());
out=new DataOutputStream(socket.getOutputStream());
BufferedReader in1=new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer=new PrintWriter(socket.getOutputStream());
out.writeUTF(mi);
String s=in.readUTF(); //in读取信息,堵塞状态
System.out.println("客户收到服务器的回答为:"+s);
Thread.sleep(500);
}
catch(Exception e) {
System.out.println("服务器已断开"+e);
}
}
}
二、实验截图:
三、码云链接
任务四
密钥分发结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
一、实验过程:
1.我负责客户端,搭档负责服务器端。
2.根据娄老师的密码学算法博客,我和搭档共同完成的DES算法代码如下:
import java.security.PublicKey;
import java.security.PrivateKey;
import java.io.*;
import javax.crypto.KeyAgreement;
import javax.crypto.spec.*;
public class KeyAgree{
public static void main(String args[ ]) throws Exception{
// 读取对方的DH公钥
File file=new File("Ckey.dat");
FileInputStream f1=new FileInputStream("Apub.dat");
ObjectInputStream b1=new ObjectInputStream(f1);
PublicKey pbk=(PublicKey)b1.readObject( );
//读取自己的DH私钥
FileInputStream f2=new FileInputStream("Bpri.dat");
ObjectInputStream b2=new ObjectInputStream(f2);
PrivateKey prk=(PrivateKey)b2.readObject( );
// 执行密钥协定
KeyAgreement ka=KeyAgreement.getInstance("DH");
ka.init(prk);
ka.doPhase(pbk,true);
//生成共享信息
byte[ ] sb=ka.generateSecret();
for(int i=0;i<sb.length;i++){
System.out.print(sb[i]+",");
}
OutputStream out=new FileOutputStream(file);
out.write(sb);
out.close();
SecretKeySpec k=new SecretKeySpec(sb,"AES");
}
}
public class Dapart {
String dapartstring(String str){
String s="";
char c[]=str.toCharArray();
for(int i=0;i<c.length;i++){
s=s+c[i]+" ";
}
return s;
}
}
部分代码,具体代码见码云链接
3.客户端的代码在实验二的基础上增加如下代码:
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class Client {
public static void main(String[] args)throws IOException{
String key="";
int n=-1;
byte [] a=new byte[128];
try{ File f=new File("Ckey.dat");
InputStream in = new FileInputStream(f);
while((n=in.read(a,0,100))!=-1) {
key=key+new String (a,0,n);
}
in.close();
}
catch(IOException e) {
System.out.println("File read Error"+e);
}
String mess;
Scanner scanner=new Scanner(System.in);
System.out.println("请输入问题:");
mess=scanner.nextLine();
NewMyBC mybc=new NewMyBC();
String str;
mybc.setBeforeExpression(mess);
str=mybc.transformWay();
String m=EncryptDecrypt.AESEncode(key, str);
System.out.println("客户加密之后的密文为:"+m);
Socket mysocket;
DataInputStream in=null;
DataOutputStream out=null;
try{ mysocket=new Socket("localhost",6006);
in=new DataInputStream(mysocket.getInputStream());
out=new DataOutputStream(mysocket.getOutputStream());
out.writeUTF(m);
String s=in.readUTF(); //in读取信息,堵塞状态
System.out.println("客户收到服务器的回答为:"+s);
Thread.sleep(500);
}
catch(Exception e) {
System.out.println("服务器已断开"+e);
}
}
}
二、实验截图:
三、码云链接
任务五
实验五 网络编程与安全-5
完整性校验结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
一、实验过程:
1.我负责客户端,搭档负责服务器端。
2.根据娄老师的密码学算法博客,我和搭档共同完成的算法代码如下:
import java.security.*;
public class DigestPass{
static String md5(String str) throws Exception{
MessageDigest m=MessageDigest.getInstance("MD5");
m.update(str.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);
}
return result;
}
}
3.客户端代码如下:
就之前实验二的代码添加如下代码:
import java.net.*;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
String key = "";
int n = -1;
byte[] a = new byte[128];
try {
File f = new File("Ckey.dat");
InputStream in = new FileInputStream(f);
while ((n = in.read(a, 0, 100)) != -1) {
key = key + new String(a, 0, n);
}
in.close();
} catch (IOException e) {
System.out.println("File read Error" + e);
}
String mess;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入问题为:");
mess = scanner.nextLine();
NewMyBC mybc = new NewMyBC();
String str;
mybc.setBeforeExpression(mess);
str = mybc.transformWay();
String md5 = DigestPass.md5(str);
String mw = EncryptDecrypt.AESEncode(key, str);
System.out.println("客户端提供的MD—5为:" + md5);
System.out.println("客户加密之后的密文为:" + mw);
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try {
mysocket = new Socket("localhost", 2010);
in = new DataInputStream(mysocket.getInputStream());
out = new DataOutputStream(mysocket.getOutputStream());
out.writeUTF(mw);
out.writeUTF(md5);
String answer = in.readUTF(); //in读取信息,堵塞状态
System.out.println("客户收到服务器的回答为:" + answer);
Thread.sleep(500);
} catch (Exception e) {
System.out.println("服务器已断开" + e);
}
}
}
部分代码,具体代码见码云链接
二、实验截图:
三、码云链接
实验过程中遇到的问题及解决办法
Q:在运行代码时,提示Error:(1, 1) java: 非法字符: '\ufeff'
,该怎么做?
A:我百度了一下,找到了一篇博客,根据博客里的步骤,解决了问题。
步骤 | 耗时(h) | 百分比 |
---|---|---|
设计 | 2 | 20% |
代码实现 | 5 | 50% |
测试 | 5 | 20% |
分析总结 | 1 | 10% |
实验体会总结
本次实验,我主要负责客户端代码,搭档负责服务器端代码,许多算法也是我们根据娄老师的博客共同研究出来的。本次实验是最后一次实验了,本学期的确受Java折磨不少,但是还是感谢娄老师给我们提供了很多新的学习方法,相信以后Java会帮助我们很多的。