任务一
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. 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
- 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
- 客户端功能:用户输入中缀表达式,调用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算法加密后通过网络把密文发送给服务器。
- 客户端工作:
- 首先运行Skey_DES.java,将在当前目录下生成key1.data保存产生的密钥。
- 运行Skey_kb.java,在当前目录下生成keykb1.dat保存上一步生成的密钥的编码。
- 改写DES加密代码SEnc.java,新添一个String类型成员变量,新添一个接受字符串参数的构造方法,并把其主函数改成加密方法,方便在客户端代码中直接调用。
- 运行客户端代码 ClientSend.java,在其中新建一个SEnc类实例,并调用该实例的加密方法,参数为后缀表达式;把密文文件和密钥编码文件发送给客户端,最后接收客户端返回的结果。
- Skey_DES.java和Skey_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
服务器新添任务
服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存)。
- 服务器工作:
- 修改解密代码 SDec.java,把其主函数改成接收两个字符串参数的解密方法,并且修改其返回类型为String。
- 服务器代码 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
- 任务五截图如下: