鲸鱼的抽屉  

任务一

1.结对实现中缀表达式转后缀表达式的功能 MyBC.java

  • 问题一:如何建立符号运算的优先级关系表?
  • 解决:将问题转化为采用方法Precede(char a,char b)判断a与b的优先级关系。
  • 问题二:最后一个元素输入完毕后怎么将栈中元素弹出?
  • 解决:在while (tokenizer.hasMoreTokens())循环已经遍历完所有根据输入和空格拆分的字符串后,再进行一个循环,把栈中元素退栈直到出现'#'。
  • 问题三:刚开始把'(',')','#'都划归为运算符类,在进行是否是运算符判断时,上述字符出现也返回true为什么不行?
  • 解决:原因在于对运算符和括号进行的操作是不同的,虽然判断运算符优先级的方法完全符合“遇到'('则进栈,遇到')'则出栈指导出现"("”的要求,但是这样需要在判断完栈顶元素和当前字符的优先关系后,再进行分类讨论,因为1.后缀表达式中不能出现'('和')',2.普通运算符小于栈顶元素则弹出栈顶元素即可,而)小于栈顶元素需要一直弹栈直到出现第一个'(',这样运行时会抛出栈空异常。
  • 问题四:为什么最初编写完程序运行测试代码时会一直抛出NoPointer的异常?
  • 解决:在MyBC中添加了一个主函数,并指定运算表达式为“3 + 2”,调试运行MyBC后,发现问题在于最初我用于存放后缀表达式的数组char[] exper1 = null;修改代码为char[] exper1 = new char[100];后问题解决。
  • 问题五:转成后缀表达式的turn函数返回类型为String,但是定义的存放后缀表达式的exper1是char[]类型,怎么转化为字符串返回呢?
  • 解决:最开始,我直接使用了toString()的方法,但是打印返回的字符串并不是想要的后缀表达式,后来,我覆盖了Object中的toString方法,重写了一个将字符数组元素拼成字符串的方法,最终解决了类型转换的问题。
  • 问题六:MyBC实现了转换后缀表达式的功能但是调用MyDC却没法对转换后的表达式求值呢?
  • 解决:通过在MyDC的while (tokenizer.hasMoreTokens())循环中添加一个打印token的语句,我发现经MyBC转换的后缀表达式比客户直接输入的表达式多执行了一次打印语句,原因在于转换后的后缀表达式多了一个空字符'\0',所以在测试类MyBCTest.java中,我又添加了一段代码去掉这个空字符将后缀表达式转换成MyDC识别的形式。
  • MyBC.java代码如下:
  1 import java.util.StringTokenizer;
  2 import java.util.Stack;
  3 
  4 public class MyBC{
  5     private Stack<Character> stack1;
  6     public char Precede(char a,char b)
  7     {
  8         if(a=='#')
  9             if(b!='#')
 10                 return '<';
 11             else
 12                 return '=';
 13         if(a==')')
 14             return '>';
 15         if(a=='(')
 16             if(b!=')')
 17                 return '<';
 18             else
 19                 return '=';
 20         if(a=='/'||a=='*')
 21             if(b!='(')
 22                 return '>';
 23             else
 24                 return '<';
 25         if(a=='-'||a=='+')
 26             if(b!='*'&&b!='/'&&b!='(')
 27                 return '>';
 28             else
 29                 return '<';
 30         return '>';
 31     }
 32     public MyBC() {
 33         stack1 = new Stack<Character>();
 34         stack1.push('#');
 35     }
 36     public String turn(String expr) {
 37         int  result = 0;
 38         String token;
 39         char topelem,optr;
 40         char[] exper1 = new char[100];
 41         int i = 0;
 42 
 43         StringTokenizer tokenizer = new StringTokenizer (expr);
 44         /*while (tokenizer.hasMoreTokens()) {
 45             System.out.println(tokenizer.nextToken());
 46         }*/
 47         while (tokenizer.hasMoreTokens())
 48         {
 49             token = tokenizer.nextToken();
 50 
 51             //如果是运算符,调用isOperator
 52 
 53             if (isOperator(token))
 54             {
 55                 //调用Precede比较优先级
 56 
 57                 topelem=stack1.peek();
 58 
 59                 optr = token.charAt(0);
 60 
 61                 if(Precede(topelem,optr)=='<')
 62                 {
 63 
 64                     stack1.push(optr);
 65 
 66                 }
 67                 else if(Precede(topelem,optr)=='=')
 68                 {
 69                     /*if(optr==')'){
 70                         optr=stack1.pop();
 71                         while(optr!='('){
 72                             exper1[i++]=optr;
 73                             exper1[i++]=' ';
 74                             optr=stack1.pop();
 75                         }
 76                     }*/
 77                     optr =stack1.pop();
 78                     exper1[i++] = optr;
 79                     exper1[i++] = ' ';
 80                 }
 81                 else if(Precede(topelem,optr)=='>')
 82                 {
 83                     optr =stack1.pop();
 84                 //从运算符栈中退出栈顶元素并放入后缀表达式
    exper1
 85                     exper1[i++] = optr;
 86                     exper1[i++] = ' ';
 87                 }
 88             }//如果是(则入栈
 89             else if(token.equals("(")) {
 90                 optr = token.charAt(0);
 91                 stack1.push(optr);
 92             }//如果是)则退栈直到出现第一个(
 93             else if(token.equals(")")) {
 94                 optr = stack1.pop();
 95                 while(optr!='(')
 96                 {
 97                     exper1[i++] = optr;
 98                     exper1[i++] = ' ';
 99                     optr = stack1.pop();
100                 }
101             }
102             else//如果是操作数
103             //操作数放入后缀表达式exper1
104             {
105                 optr = token.charAt(0);
106                 //System.out.println(optr);
107                 exper1[i++]=optr;
108                 exper1[i++] = ' ';
109 
110             }
111         }
112             while(!stack1.isEmpty())
113             {
114                 optr = stack1.pop();
115                 if(optr!='#'){
116                     exper1[i++] = optr;
117                     exper1[i++] = ' ';
118                 }
119             }
120             //System.out.println(exper1);
121 
122         return ToString(exper1);
123     }
124     //@Override
125     private boolean isOperator(String token)
126     {
127 
128         return (token.equals("+") || token.equals("-") ||t    oken.equals("*") || token.equals("/") );
129     }
130     public  static String ToString(char[] exper1){
131         int length = exper1.length;
132         String str=" ";
133         for(int i=0;i<length;i++)
134         {
135             str=str+exper1[i];
136         }
137         //System.out.println(str);
138         return str;
139     }
140     /*public static void main(String[] args) {
141         String pp=null;
142         MyBC turner = new MyBC();
143         
144         pp=turner.turn("( 1 + 2 ) * ( ( 8 - 2 ) / ( 7 - 4     ) )");
145         System.out.println(pp);
146     }*/
147 }

2.结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java

  • MyDC.java代码与5月3日实践是撰写的内容相同,此处省略。
  • MyBCText.java代码如下:
  1 import java.util.Scanner;
  2 
  3 public class MyBCTest  {
  4     public static void main (String[] args) {
  5 
  6         String expression1=null,expression2=null, again=null;
  7 
  8         int result,i=0,length=0;
  9 
 10         try
 11         {
 12             Scanner in = new Scanner(System.in);
 13 
 14             do
 15             {
 16                 MyDC evaluator = new MyDC();
 17                 MyBC turner = new MyBC();
 18                 System.out.println ("Enter a valid midfix     expression: ");
 19                 expression1 = in.nextLine();
 20                 //System.out.println(expression1);
 21                 expression2 = turner.turn(expression1);
 22                 while(expression2.charAt(i)!='\0'){
 23                     length++;
 24                     i++;
 25                 }
 26                 //length 表示字符串包括‘\0’的字符个数
 27                 expression2 = expression2.substring(1,leng    th-1);
 28                 //System.out.println(expression2);
 29                 result = evaluator.evaluate (expression2);
 30                 System.out.println();
 31                 System.out.println ("That expression equals " + result);
 32 
 33                 System.out.print ("Evaluate another expression [Y/N]? ");
 34                 again = in.nextLine();
 35                 System.out.println();
 36             }
 37             while (again.equalsIgnoreCase("y"));
 38         }
 39         catch (Exception e)
 40         {
 41             e.printStackTrace();
 42         }
 43     }
 44 }
  • 任务一运行结果如下:

任务二

结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题

  1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
  2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
  3. 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
  4. 客户端显示服务器发送过来的结果
  5. 上传测试结果截图和码云链接
  • 客户端功能:用户输入中缀表达式,调用MyBC转换为后缀表达式,再将后缀表达式去除空字符'\0'后传给服务器,最后接收服务器传回的计算结果。
  • 服务器功能:接收客户端传来的后缀表达式,调用MyDC求值,再把整数转换为字符串传回给客户端。
  • 客户端Client.java代码如下:


import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Client {
    public static final String IP_ADDR = "127.0.0.1";//服务器地址
    public static final int PORT = 12345;//服务器端口号

    public static void main(String[] args) {
        System.out.println("客户端启动...");
        System.out.println("当接收到服务器端字符为 \"OK\" 的时候, 客户端将终止\n");
        while (true) {
            Socket socket = null;
            try {
                //创建一个流套接字并将其连接到指定主机上的指定端口号
                socket = new Socket(IP_ADDR, PORT);

                //读取服务器端数据
                DataInputStream input = new DataInputStream(socket.getInputStream());
                //向服务器端发送数据
                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                System.out.print("请输入: \t");
                String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
                MyBC turner = new MyBC();
                String str1 = turner.turn(str);
                int length=0,i=0;
                while(str1.charAt(i)!='\0'){
                    length++;
                    i++;
                }
                String str2 = str1.substring(1,length-1);
                out.writeUTF(str2);

                String ret = input.readUTF();

                System.out.println("服务器端返回过来的是: " + ret);
                // 如接收到 "OK" 则断开连接
                /*if ("OK".equals(ret)) {
                    System.out.println("客户端将关闭连接");
                    Thread.sleep(500);
                    break;
                }*/

                out.close();
                input.close();
            } catch (Exception e) {
                System.out.println("客户端异常:" + e.getMessage());
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        socket = null;
                        System.out.println("客户端 finally 异常:" + e.getMessage());
                    }
                }
            }
        }
    }
}
  • 服务器Sever.java代码如下:


import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static final int PORT = 12345;//监听的端口号     

    public static void main(String[] args) {
        System.out.println("服务器启动...\n");
        Server server = new Server();
        server.init();
    }

    public void init() {
        try {
            ServerSocket serverSocket = new ServerSocket(PORT);
            while (true) {
                // 一旦有堵塞, 则表示服务器与客户端获得了连接    
                Socket client = serverSocket.accept();
                // 处理这次连接    
                new HandlerThread(client);
            }
        } catch (Exception e) {
            System.out.println("服务器异常: " + e.getMessage());
        }
    }

    private class HandlerThread implements Runnable {
        private Socket socket;
        public HandlerThread(Socket client) {
            socket = client;
            new Thread(this).start();
        }

        public void run() {
            try {
                // 读取客户端数据    
                DataInputStream input = new DataInputStream(socket.getInputStream());
                String clientInputStr = input.readUTF();//这里要注意和客户端输出流的写方法对应,否则会抛 EOFException  
                // 处理客户端数据    
                System.out.println("客户端发过来的内容:" + clientInputStr);
                MyDC evalute = new MyDC();
                int result = evalute.evaluate(clientInputStr);
                // 向客户端回复信息    
                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                System.out.print("计算结果:\t");
                // 发送键盘输入的一行    
                //String s = new BufferedReader(new InputStreamReader(System.in)).readLine();
                out.writeUTF(String.valueOf(result));

                out.close();
                input.close();
            } catch (Exception e) {
                System.out.println("服务器 run 异常: " + e.getMessage());
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (Exception e) {
                        socket = null;
                        System.out.println("服务端 finally 异常:" + e.getMessage());
                    }
                }
            }
        }
    }
}
  • 任务二运行结果如下:

任务三

  • 问题:服务器对从客户端传来的密文进行解密时,抛出 BadPaddingException错误。
  • 解决:用室友的电脑运行自己的代码没有问题,怀疑是自己的IDEA配置上有有问题。此外我还咨询了老师,了解到这个问题根源在于类型之间的转换,另一个解决方法:http://bywyu.blog.163.com/blog/static/189993186201092955043834

客户端新添任务

把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器。

  • 客户端工作:
    1. 首先运行Skey_DES.java,将在当前目录下生成key1.data保存产生的密钥。
    2. 运行Skey_kb.java,在当前目录下生成keykb1.dat保存上一步生成的密钥的编码。
    3. 改写DES加密代码SEnc.java,新添一个String类型成员变量,新添一个接受字符串参数的构造方法,并把其主函数改成加密方法,方便在客户端代码中直接调用。
    4. 运行客户端代码 ClientSend.java,在其中新建一个SEnc类实例,并调用该实例的加密方法,参数为后缀表达式;把密文文件和密钥编码文件发送给客户端,最后接收客户端返回的结果。
  • Skey_DES.javaSkey_kb.java内容同老师提供的代码,此处省略。
  • 经过修改的SEnc.java代码如下:
import java.io.*;
import java.security.*;
import javax.crypto.*;
public class SEnc{
    static String s;
    public SEnc(String s){
        this.s = s;
    }
    public void  encrypt(){
        try{
        //SEnc senc = new SEnc("Hello World");
        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);
    }catch(Exception e){
            System.out.println(e);
        }
    }
}
  • 客户端ClientSend.java代码如下:
import java.io.*;
import java.net.Socket;
public class ClientSend {
   // public static final String IP_ADDR = "127.0.0.1";//服务器地址
   // public static final int PORT = 12345;//服务器端口号
    public static void main(String[] args) {
        /**与服务器建立连接的通信句柄*/
        Socket s = null;
/**与服务器建立连接*/
        try {
            s = new Socket("127.0.0.1", 4004);
        }catch (IOException e) {
            System.out.println("未连接到服务器");
        }
    //读取服务器端数据
    try

    {
        DataInputStream input = new DataInputStream(s.getInputStream());
        //向服务器端发送数据

        System.out.print("请输入: \t");
        String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
        MyBC turner = new MyBC();
        String str1 = turner.turn(str);
        int length = 0, i = 0;
        while (str1.charAt(i) != '\0') {
            length++;
            i++;
        }
        String str2 = str1.substring(1, length - 1);
        SEnc senc = new SEnc(str2);//指定后缀表达式为明文字符串
        senc.encrypt();//加密
    }catch(Exception e) {
        System.out.println("客户端异常:" + e.getMessage());
    }

        /**定义文件对象,即为要发送的文件
         * 如果使用绝对路径,不要忘记使用'/'和'\'的区别
         * 具体区别,请读者自行查询
         * */

        File sendfile = new File("SEnc.dat");
        File sendfile1 = new File("Keykb1.dat");
        /**定义文件输入流,用来打开、读取即将要发送的文件*/
        FileInputStream fis = null;
        FileInputStream fis1 = null;
        /**定义byte数组来作为数据包的存储数据包*/
        byte[] buffer = new byte[4096 * 5];
        byte[] buffer1 = new byte[4096 * 5];
        /**定义输出流,使用socket的outputStream对数据包进行输出*/
        OutputStream os = null;


        /**检查要发送的文件是否存在*/
        if(!sendfile.exists() || !sendfile1.exists()){
            System.out.println("客户端:要发送的文件不存在");
            return;
        }

        /**用文件对象初始化fis对象
         * 以便于可以提取出文件的大小
         * */
        try {
            fis = new FileInputStream(sendfile);
            fis1 = new FileInputStream(sendfile1);
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }


        /**首先先向服务器发送关于文件的信息,以便于服务器进行接收的相关准备工作
         * 具体的准备工作,请查看服务器代码。
         *
         * 发送的内容包括:发送文件协议码(此处为111)/#文件名(带后缀名)/#文件大小
         * */
        try {
            PrintStream ps = new PrintStream(s.getOutputStream());
            ps.println("111/#" + sendfile.getName() + "/#" + fis.available());
            ps.flush();
        } catch (IOException e) {
            System.out.println("服务器连接中断");
        }


        /**
         * 此处睡眠2s,等待服务器把相关的工作准备好
         * 也是为了保证网络的延迟
         * 读者可自行选择添加此代码
         * */
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }



        /**之前的准备工作结束之后
         * 下面就是文件传输的关键代码
         * */
        try {

            /**获取socket的OutputStream,以便向其中写入数据包*/
            os = s.getOutputStream();

            /** size 用来记录每次读取文件的大小*/
            int size = 0;

            /**使用while循环读取文件,直到文件读取结束*/
            while((size = fis.read(buffer)) != -1){
                System.out.println("客户端发送数据包,大小为" + size);
                /**向输出流中写入刚刚读到的数据包*/
                os.write(buffer, 0, size);
                /**刷新一下*/
                os.flush();
            }
        } catch (FileNotFoundException e) {
            System.out.println("客户端读取文件出错");
        } catch (IOException e) {
            System.out.println("客户端输出文件出错");
        }finally{

            try {
                if(fis != null)
                    fis.close();
            } catch (IOException e) {
                System.out.println("客户端文件关闭出错");
            }//catch (IOException e)
        }//finally
        try {
            PrintStream ps1 = new PrintStream(s.getOutputStream());
            ps1.println("111/#" + sendfile1.getName() + "/#" + fis1.available());
            ps1.flush();
        } catch (IOException e) {
            System.out.println("服务器连接中断");
        }


        /**
         * 此处睡眠2s,等待服务器把相关的工作准备好
         * 也是为了保证网络的延迟
         * 读者可自行选择添加此代码
         * */
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }



        /**之前的准备工作结束之后
         * 下面就是文件传输的关键代码
         * */
        try {

            /**获取socket的OutputStream,以便向其中写入数据包*/
            os = s.getOutputStream();

            /** size 用来记录每次读取文件的大小*/
            int size = 0;

            /**使用while循环读取文件,直到文件读取结束*/
            while((size = fis1.read(buffer1)) != -1){
                System.out.println("客户端发送数据包,大小为" + size);
                /**向输出流中写入刚刚读到的数据包*/
                os.write(buffer1, 0, size);
                /**刷新一下*/
                os.flush();
            }
        } catch (FileNotFoundException e) {
            System.out.println("客户端读取文件出错");
        } catch (IOException e) {
            System.out.println("客户端输出文件出错");
        }finally{
            try {
                if(fis1 != null)
                    fis1.close();
            } catch (IOException e) {
                System.out.println("客户端文件关闭出错");
            }//catch (IOException e)
        }//finally
        try{
            DataInputStream input = new DataInputStream(s.getInputStream());
            String ret = input.readUTF();
            System.out.println("服务器端返回过来的是: " + ret);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (s != null) {
                try {
                    s.close();
                } catch (IOException e) {
                    s = null;
                    System.out.println("客户端 finally 异常:" + e.getMessage());
                }
            }
        }
    }//public static void main(String[] args)
}//public class ClientSend

服务器新添任务

服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存)。

  • 服务器工作:
    1. 修改解密代码 SDec.java,把其主函数改成接收两个字符串参数的解密方法,并且修改其返回类型为String。
    2. 服务器代码 ServerReceive.java接收从客户端发送的密文文件和密钥编码文件,在当前目录下创建同名文件将其分别保存,新建一个SDec类实例,并调用其解密方法,参数为密文文件名和密钥编码文件名,用一个字符串接收解密后返回的明文,再调用MyDC计算该字符串的值并返回客户端。
  • 经过修改的SDec.java代码如下:
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class SDec{

    public static String decipher(String str1,String str2)throws Exception{
        // 获取密文
        FileInputStream f=new FileInputStream(str1);
        int num=f.available();
        byte[ ] ctext=new byte[num];
        f.read(ctext);
        // 获取密钥
        FileInputStream  f2=new FileInputStream(str2);
        int num2=f2.available();
        byte[ ] keykb=new byte[num2];
        f2.read(keykb);
        SecretKeySpec k=new  SecretKeySpec(keykb,"DESede");
        // 解密
        Cipher cp=Cipher.getInstance("DESede");
        cp.init(Cipher.DECRYPT_MODE, k);
        byte []ptext=cp.doFinal(ctext);
        // 显示明文
        String p=new String(ptext,"UTF8");
        System.out.println(p);
        return p;
    }
}
  • 服务器ServerReceive.java代码如下:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.zip.ZipFile;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class ServerReceive {

    public static void main(String[] args) {

        /**与服务器建立连接的通信句柄*/
        ServerSocket ss = null;
        Socket s = null;

        /**定义用于在接收后在本地创建的文件对象和文件输出流对象*/
        File file = null;
        File file1 = null;
        FileOutputStream fos = null;
        FileOutputStream fos1 = null;

        /**定义输入流,使用socket的inputStream对数据包进行输入*/
        InputStream is = null;

        /**定义byte数组来作为数据包的存储数据包*/
        byte[] buffer = new byte[4096 * 5];
        byte[] buffer1 = new byte[4096 * 5];
        /**用来接收文件发送请求的字符串*/
        String comm = null;
        String comm1 = null;


        /**建立socekt通信,等待服务器进行连接*/
        try {
            ss = new ServerSocket(4004);
            s = ss.accept();
        } catch (IOException e) {
            e.printStackTrace();
        }


        /**读取一行客户端发送过来的约定信息*/
        try {
            InputStreamReader isr = new InputStreamReader(s.getInputStream());
            BufferedReader br = new BufferedReader(isr);
            comm = br.readLine();
        } catch (IOException e) {
            System.out.println("服务器与客户端断开连接");
        }

        /**开始解析客户端发送过来的请求命令*/
        int index = comm.indexOf("/#");

        /**判断协议是否为发送文件的协议*/
        String xieyi = comm.substring(0, index);
        if(!xieyi.equals("111")){
            System.out.println("服务器收到的协议码不正确");
            return;
        }

        /**解析出文件的名字和大小*/
        comm = comm.substring(index + 2);
        index = comm.indexOf("/#");
        String filename = comm.substring(0, index).trim();
        String filesize = comm.substring(index + 2).trim();


        /**创建空文件,用来进行接收文件*/
        file = new File(filename);
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                System.out.println("服务器端创建文件失败");
            }
        }else{
            /**在此也可以询问是否覆盖*/
            System.out.println("本路径已存在相同文件,进行覆盖");
        }

        /**【以上就是客户端代码中写到的服务器的准备部分】*/


        /**
         * 服务器接收文件的关键代码*/
        try {
            /**将文件包装到文件输出流对象中*/
            fos = new FileOutputStream(file);
            long file_size = Long.parseLong(filesize);
            is = s.getInputStream();
            /**size为每次接收数据包的长度*/
            int size = 0;
            /**count用来记录已接收到文件的长度*/
            long count = 0;

            /**使用while循环接收数据包*/
            while(count < file_size){
                /**从输入流中读取一个数据包*/
                size = is.read(buffer);

                /**将刚刚读取的数据包写到本地文件中去*/
                fos.write(buffer, 0, size);
                fos.flush();

                /**将已接收到文件的长度+size*/
                count += size;
                System.out.println("服务器端接收到数据包,大小为" + size);
            }

        } catch (FileNotFoundException e) {
            System.out.println("服务器写文件失败");
        } catch (IOException e) {
            System.out.println("服务器:客户端断开连接");
        }finally{
            /**
             * 将打开的文件关闭 
             * 如有需要,也可以在此关闭socket连接 
             * */
            try {
                if(fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }//catch (IOException e)  
        }//finally

        /**读取一行客户端发送过来的约定信息*/
        try {
            InputStreamReader isr1 = new InputStreamReader(s.getInputStream());
            BufferedReader br1 = new BufferedReader(isr1);
            comm1 = br1.readLine();
        } catch (IOException e) {
            System.out.println("服务器与客户端断开连接");
        }

        /**开始解析客户端发送过来的请求命令*/
        int index1 = comm1.indexOf("/#");

        /**判断协议是否为发送文件的协议*/
        String xieyi1 = comm1.substring(0, index1);
        if(!xieyi1.equals("111")){
            System.out.println("服务器收到的协议码不正确");
            return;
        }

        /**解析出文件的名字和大小*/
        comm1 = comm1.substring(index1 + 2);
        index1 = comm1.indexOf("/#");
        String filename1 = comm1.substring(0, index1).trim();
        String filesize1 = comm1.substring(index1 + 2).trim();


        /**创建空文件,用来进行接收文件*/
        file1 = new File(filename1);
        if(!file1.exists()){
            try {
                file1.createNewFile();
            } catch (IOException e) {
                System.out.println("服务器端创建文件失败");
            }
        }else{
            /**在此也可以询问是否覆盖*/
            System.out.println("本路径已存在相同文件,进行覆盖");
        }

        /**【以上就是客户端代码中写到的服务器的准备部分】*/


        /**
         * 服务器接收文件的关键代码*/
        try {
            /**将文件包装到文件输出流对象中*/
            fos1 = new FileOutputStream(file1);
            long file_size = Long.parseLong(filesize1);
            is = s.getInputStream();
            /**size为每次接收数据包的长度*/
            int size = 0;
            /**count用来记录已接收到文件的长度*/
            long count = 0;

            /**使用while循环接收数据包*/
            while(count < file_size){
                /**从输入流中读取一个数据包*/
                size = is.read(buffer1);

                /**将刚刚读取的数据包写到本地文件中去*/
                fos1.write(buffer1, 0, size);
                fos1.flush();

                /**将已接收到文件的长度+size*/
                count += size;
                System.out.println("服务器端接收到数据包,大小为" + size);
            }

        } catch (FileNotFoundException e) {
            System.out.println("服务器写文件失败");
        } catch (IOException e) {
            System.out.println("服务器:客户端断开连接");
        }finally{
            /**
             * 将打开的文件关闭
             * 如有需要,也可以在此关闭socket连接
             * */
            try {
                if(fos1 != null)
                    fos1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }//catch (IOException e)
        }//finally
        try {

            SDec sdec = new SDec();
            String str = sdec.decipher("SEnc.dat","Keykb1.dat");
        
            System.out.println("客户端发过来的内容:" + str);
            MyDC evalute = new MyDC();
            int result = evalute.evaluate(str);
            // 向客户端回复信息
            DataOutputStream out = new DataOutputStream(s.getOutputStream());
            System.out.print("计算结果:\t");
            // 发送键盘输入的一行
            out.writeUTF(String.valueOf(result));
            out.close();
        } catch (Exception e) {
            System.out.println("服务器 run 异常: " + e.getMessage());
        } finally {
            if (s != null) {
                try {
                    s.close();
                } catch (Exception e) {
                    s = null;
                    System.out.println("服务端 finally 异常:" + e.getMessage());
                }
            }
        }

    }//public static void main(String[] args)  
}//public class ServerReceive
  • 任务三运行结果截图如下:

任务四

新增任务:客户端和服务器用DH算法进行3DES或AES算法的密钥交换。

  • 服务器和客户端的工作:
    • 服务器和客户端分别运行Key_DH.java代码产生各自的公钥和私钥,然后将公钥通过网络发送给对方,(可以先进行一次通信传过去经过DES加密的密文和密钥,再把文件名改成Apub.dat或Bpub.dat把公钥传过去),客户端和服务器利用对方发送的公钥和自己私钥运行 KeyAgree.java生成相同的共享密钥,各自利用共享密钥进行加解密工作。
  • 任务四运行结果如下:

任务五

新增任务:客户端把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器,服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端。

  • 客户端代码如下:
import java.io.*;
import java.net.Socket;
import java.security.MessageDigest;

public class Client5 {
    // public static final String IP_ADDR = "127.0.0.1";//服务器地址
    // public static final int PORT = 12345;//服务器端口号
    public static void main(String[] args) {
        /**与服务器建立连接的通信句柄*/
        Socket s = null;
/**与服务器建立连接*/
        try {
            s = new Socket("127.0.0.1", 4004);
        }catch (IOException e) {
            System.out.println("未连接到服务器");
        }
        //读取服务器端数据
        try

        {
            DataInputStream input = new DataInputStream(s.getInputStream());
            DataOutputStream out = new DataOutputStream(s.getOutputStream());
            //向服务器端发送数据

            System.out.print("请输入: \t");
            String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
            MyBC turner = new MyBC();
            String str1 = turner.turn(str);
            int length = 0, i = 0;
            while (str1.charAt(i) != '\0') {
                length++;
                i++;
            }
            String str2 = str1.substring(1, length - 1);
            //以下对明文产生消息摘要
            String x=str2;
            MessageDigest m=MessageDigest.getInstance("MD5");
            m.update(x.getBytes("UTF8"));
            byte s2[ ]=m.digest( );
            String result="";
            for (int j=0; j<s2.length; j++){
                result+=Integer.toHexString( (0x000000ff & s2[j]) | 0xffffff00).substring(6);
            }
            //System.out.println(result);
            out.writeUTF(result);
            SEnc senc = new SEnc(str2);//指定后缀表达式为明文字符串
            senc.encrypt();//加密

        }catch(Exception e) {
            System.out.println("客户端异常:" + e.getMessage());
        }

        /**定义文件对象,即为要发送的文件
         * 如果使用绝对路径,不要忘记使用'/'和'\'的区别
         * 具体区别,请读者自行查询
         * */

        File sendfile = new File("SEnc.dat");
        File sendfile1 = new File("Apub.dat");
        /**定义文件输入流,用来打开、读取即将要发送的文件*/
        FileInputStream fis = null;
        FileInputStream fis1 = null;
        /**定义byte数组来作为数据包的存储数据包*/
        byte[] buffer = new byte[4096 * 5];
        byte[] buffer1 = new byte[4096 * 5];
        /**定义输出流,使用socket的outputStream对数据包进行输出*/
        OutputStream os = null;


        /**检查要发送的文件是否存在*/
        if(!sendfile.exists() || !sendfile1.exists()){
            System.out.println("客户端:要发送的文件不存在");
            return;
        }

        /**用文件对象初始化fis对象
         * 以便于可以提取出文件的大小
         * */
        try {
            fis = new FileInputStream(sendfile);
            fis1 = new FileInputStream(sendfile1);
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }


        /**首先先向服务器发送关于文件的信息,以便于服务器进行接收的相关准备工作
         * 具体的准备工作,请查看服务器代码。
         *
         * 发送的内容包括:发送文件协议码(此处为111)/#文件名(带后缀名)/#文件大小
         * */
        try {
            PrintStream ps = new PrintStream(s.getOutputStream());
            ps.println("111/#" + sendfile.getName() + "/#" + fis.available());
            ps.flush();
        } catch (IOException e) {
            System.out.println("服务器连接中断");
        }


        /**
         * 此处睡眠2s,等待服务器把相关的工作准备好
         * 也是为了保证网络的延迟
         * 读者可自行选择添加此代码
         * */
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }



        /**之前的准备工作结束之后
         * 下面就是文件传输的关键代码
         * */
        try {

            /**获取socket的OutputStream,以便向其中写入数据包*/
            os = s.getOutputStream();

            /** size 用来记录每次读取文件的大小*/
            int size = 0;

            /**使用while循环读取文件,直到文件读取结束*/
            while((size = fis.read(buffer)) != -1){
                System.out.println("客户端发送数据包,大小为" + size);
                /**向输出流中写入刚刚读到的数据包*/
                os.write(buffer, 0, size);
                /**刷新一下*/
                os.flush();
            }
        } catch (FileNotFoundException e) {
            System.out.println("客户端读取文件出错");
        } catch (IOException e) {
            System.out.println("客户端输出文件出错");
        }finally{

            try {
                if(fis != null)
                    fis.close();
            } catch (IOException e) {
                System.out.println("客户端文件关闭出错");
            }//catch (IOException e)
        }//finally
        try {
            PrintStream ps1 = new PrintStream(s.getOutputStream());
            ps1.println("111/#" + sendfile1.getName() + "/#" + fis1.available());
            ps1.flush();
        } catch (IOException e) {
            System.out.println("服务器连接中断");
        }


        /**
         * 此处睡眠2s,等待服务器把相关的工作准备好
         * 也是为了保证网络的延迟
         * 读者可自行选择添加此代码
         * */
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }



        /**之前的准备工作结束之后
         * 下面就是文件传输的关键代码
         * */
        try {

            /**获取socket的OutputStream,以便向其中写入数据包*/
            os = s.getOutputStream();

            /** size 用来记录每次读取文件的大小*/
            int size = 0;

            /**使用while循环读取文件,直到文件读取结束*/
            while((size = fis1.read(buffer1)) != -1){
                System.out.println("客户端发送数据包,大小为" + size);
                /**向输出流中写入刚刚读到的数据包*/
                os.write(buffer1, 0, size);
                /**刷新一下*/
                os.flush();
            }
        } catch (FileNotFoundException e) {
            System.out.println("客户端读取文件出错");
        } catch (IOException e) {
            System.out.println("客户端输出文件出错");
        }finally{
            try {
                if(fis1 != null)
                    fis1.close();
            } catch (IOException e) {
                System.out.println("客户端文件关闭出错");
            }//catch (IOException e)
        }//finally
        try{

            String yes = "向服务器发送MD5";
            DataOutputStream out = new DataOutputStream(s.getOutputStream());
            out.writeUTF(yes);
           DataInputStream input = new DataInputStream(s.getInputStream());
            String ret = input.readUTF();
            System.out.println("服务器端返回过来的是: " + ret);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (s != null) {
                try {
                    s.close();
                } catch (IOException e) {
                    s = null;
                    System.out.println("客户端 finally 异常:" + e.getMessage());
                }
            }
        }
    }//public static void main(String[] args)
}//public class Client5

  • 服务器代码如下:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.zip.ZipFile;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class Server5 {
    static String perpetual;
    public static void main(String[] args) {

        /**与服务器建立连接的通信句柄*/
        ServerSocket ss = null;
        Socket s = null;

        /**定义用于在接收后在本地创建的文件对象和文件输出流对象*/
        File file = null;
        File file1 = null;
        FileOutputStream fos = null;
        FileOutputStream fos1 = null;

        /**定义输入流,使用socket的inputStream对数据包进行输入*/
        InputStream is = null;

        /**定义byte数组来作为数据包的存储数据包*/
        byte[] buffer = new byte[4096 * 5];
        byte[] buffer1 = new byte[4096 * 5];
        /**用来接收文件发送请求的字符串*/
        String comm = null;
        String comm1 = null;


        /**建立socekt通信,等待服务器进行连接*/
        try {
            ss = new ServerSocket(4004);
            s = ss.accept();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            DataInputStream input = new DataInputStream(s.getInputStream());
            String ret = input.readUTF();
            perpetual = ret;
            System.out.println("明文的MD5值是: " + ret);
        } catch (IOException e) {
            e.printStackTrace();
        }
        /**读取一行客户端发送过来的约定信息*/
        try {
            InputStreamReader isr = new InputStreamReader(s.getInputStream());
            BufferedReader br = new BufferedReader(isr);
            comm = br.readLine();
        } catch (IOException e) {
            System.out.println("服务器与客户端断开连接");
        }

        /**开始解析客户端发送过来的请求命令*/
        int index = comm.indexOf("/#");

        /**判断协议是否为发送文件的协议*/
        String xieyi = comm.substring(0, index);
        if(!xieyi.equals("111")){
            System.out.println("服务器收到的协议码不正确");
            return;
        }

        /**解析出文件的名字和大小*/
        comm = comm.substring(index + 2);
        index = comm.indexOf("/#");
        String filename = comm.substring(0, index).trim();
        String filesize = comm.substring(index + 2).trim();


        /**创建空文件,用来进行接收文件*/
        file = new File(filename);
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                System.out.println("服务器端创建文件失败");
            }
        }else{
            /**在此也可以询问是否覆盖*/
            System.out.println("本路径已存在相同文件,进行覆盖");
        }

        /**【以上就是客户端代码中写到的服务器的准备部分】*/


        /**
         * 服务器接收文件的关键代码*/
        try {
            /**将文件包装到文件输出流对象中*/
            fos = new FileOutputStream(file);
            long file_size = Long.parseLong(filesize);
            is = s.getInputStream();
            /**size为每次接收数据包的长度*/
            int size = 0;
            /**count用来记录已接收到文件的长度*/
            long count = 0;

            /**使用while循环接收数据包*/
            while(count < file_size){
                /**从输入流中读取一个数据包*/
                size = is.read(buffer);

                /**将刚刚读取的数据包写到本地文件中去*/
                fos.write(buffer, 0, size);
                fos.flush();

                /**将已接收到文件的长度+size*/
                count += size;
                System.out.println("服务器端接收到数据包,大小为" + size);
            }

        } catch (FileNotFoundException e) {
            System.out.println("服务器写文件失败");
        } catch (IOException e) {
            System.out.println("服务器:客户端断开连接");
        }finally{
            /**
             * 将打开的文件关闭
             * 如有需要,也可以在此关闭socket连接
             * */
            try {
                if(fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }//catch (IOException e)
        }//finally

        /**读取一行客户端发送过来的约定信息*/
        try {
            InputStreamReader isr1 = new InputStreamReader(s.getInputStream());
            BufferedReader br1 = new BufferedReader(isr1);
            comm1 = br1.readLine();
        } catch (IOException e) {
            System.out.println("服务器与客户端断开连接");
        }

        /**开始解析客户端发送过来的请求命令*/
        int index1 = comm1.indexOf("/#");

        /**判断协议是否为发送文件的协议*/
        String xieyi1 = comm1.substring(0, index1);
        if(!xieyi1.equals("111")){
            System.out.println("服务器收到的协议码不正确");
            return;
        }

        /**解析出文件的名字和大小*/
        comm1 = comm1.substring(index1 + 2);
        index1 = comm1.indexOf("/#");
        String filename1 = comm1.substring(0, index1).trim();
        String filesize1 = comm1.substring(index1 + 2).trim();


        /**创建空文件,用来进行接收文件*/
        file1 = new File(filename1);
        if(!file1.exists()){
            try {
                file1.createNewFile();
            } catch (IOException e) {
                System.out.println("服务器端创建文件失败");
            }
        }else{
            /**在此也可以询问是否覆盖*/
            System.out.println("本路径已存在相同文件,进行覆盖");
        }

        /**【以上就是客户端代码中写到的服务器的准备部分】*/


        /**
         * 服务器接收文件的关键代码*/
        try {
            /**将文件包装到文件输出流对象中*/
            fos1 = new FileOutputStream(file1);
            long file_size = Long.parseLong(filesize1);
            is = s.getInputStream();
            /**size为每次接收数据包的长度*/
            int size = 0;
            /**count用来记录已接收到文件的长度*/
            long count = 0;

            /**使用while循环接收数据包*/
            while(count < file_size){
                /**从输入流中读取一个数据包*/
                size = is.read(buffer1);

                /**将刚刚读取的数据包写到本地文件中去*/
                fos1.write(buffer1, 0, size);
                fos1.flush();

                /**将已接收到文件的长度+size*/
                count += size;
                System.out.println("服务器端接收到数据包,大小为" + size);
            }

        } catch (FileNotFoundException e) {
            System.out.println("服务器写文件失败");
        } catch (IOException e) {
            System.out.println("服务器:客户端断开连接");
        }finally{
            /**
             * 将打开的文件关闭
             * 如有需要,也可以在此关闭socket连接
             * */
            try {
                if(fos1 != null)
                    fos1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }//catch (IOException e)
        }//finally
        try {

            SDec sdec = new SDec();
            String str = sdec.decipher("SEnc.dat", "Keykb1.dat");
            //以下对解密的密文生成信息摘要,并与从客户端传过来的值比对
            String x = str;
            MessageDigest m = MessageDigest.getInstance("MD5");
            m.update(x.getBytes("UTF8"));
            byte s2[] = m.digest();
            String result1 = "";
            for (int j = 0; j < s2.length; j++) {
                result1 += Integer.toHexString((0x000000ff & s2[j]) | 0xffffff00).substring(6);
            }
            boolean whether = result1.equals(perpetual);
            System.out.println("客户端发过来的内容:" + str);

            DataOutputStream out = new DataOutputStream(s.getOutputStream());
            if (whether) {
                MyDC evalute = new MyDC();
                int result = evalute.evaluate(str);
                String yes = "MD5检验成功,计算结果为" + String.valueOf(result);
                out.writeUTF(yes);
                System.out.print("计算结果:\t" + result);
            }

            // 发送键盘输入的一行
            out.close();



        } catch (Exception e) {
            System.out.println("服务器 run 异常: " + e.getMessage());
        } finally {
            if (s != null) {
                try {
                    s.close();
                } catch (Exception e) {
                    s = null;
                    System.out.println("服务端 finally 异常:" + e.getMessage());
                }
            }
        }

    }//public static void main(String[] args)  
}//public class Server5
  • 任务五截图如下:

码云链接

posted on 2017-06-03 11:59  鲸鱼的抽屉  阅读(253)  评论(0编辑  收藏  举报