20165203实验五 网络编程与安全

20165203实验五 网络编程与安全

任务一

两人一组结对编程:
0. 参考(http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA)

  1. 结对实现中缀表达式转后缀表达式的功能 MyBC.java
  2. 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
  3. 上传测试代码运行结果截图和码云链接

一、实验过程:

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. 注意责任归宿,要会通过测试证明自己没有问题

  1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
  3. 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  4. 客户端显示服务器发送过来的结果
  5. 上传测试结果截图和码云链接

一、实验过程:

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. 注意责任归宿,要会通过测试证明自己没有问题

  1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
  3. 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  4. 客户端显示服务器发送过来的结果
  5. 上传测试结果截图和码云链接

一、实验过程:

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. 注意责任归宿,要会通过测试证明自己没有问题

  1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
  3. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
  4. 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  5. 客户端显示服务器发送过来的结果
  6. 上传测试结果截图和码云链接

一、实验过程:

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. 注意责任归宿,要会通过测试证明自己没有问题

  1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
  3. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
  4. 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  5. 客户端显示服务器发送过来的结果
  6. 上传测试结果截图和码云链接

一、实验过程:

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会帮助我们很多的。

posted @ 2018-05-28 17:02  I~Justice  阅读(213)  评论(1编辑  收藏  举报