• 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("结果为:
    "+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会帮助我们很多的。

  • 相关阅读:
    Ubuntu 16.04远程登录服务器--ssh的安装和配置
    设置淘宝源
    shell 循环 read line
    apt-get update 报错 W: Unknown Multi-Arch type 'no' for package 'compiz-core'
    expdp dblink
    ubuntu apt-update NO_PUBKEY 40976EAF437D05B5 NO_PUBKEY 3B4FE6ACC0B21F32
    listener.log文件过大导致oracle假死
    rsync_ssh
    ssh多主机
    elk大纲
  • 原文地址:https://www.cnblogs.com/20165203-xyx/p/9101016.html
Copyright © 2020-2023  润新知