学号 2019-2020-182321 《数据结构与面向对象程序设计》实验四报告
课程:《程序设计与数据结构》
班级: 1823
姓名: 杨凯涵
学号:20182321
实验教师:王志强
实验日期:2019年10月7日
必修/选修: 必修
1.实验内容
- 实验一: Java Socket编程
1.学习蓝墨云上教材《Java和Android编程》“第16章 输入/输出 ”和“第22章 网络”,学习JavaSocket编程
2.结对编程。结对伙伴A编写客户端SocketClient.java,结对伙伴B编写服务器端。
3.截图加学号水印上传蓝墨云,代码push到码云,并撰写实验报告。 - 实验二:Java和密码学,参考http://www.cnblogs.com/rocedu/p/6683948.html,以结对的方式完成Java密码学相关内容的学习(帖子中所有代码和相关知识点需要学习)。
- 实验三:编写有理数/复数计算器,结对编程,结对伙伴A编写有理数计算器。结对伙伴B编写复数计算器
- 实验四:远程有理数计算器,结对编程,结对伙伴A编程实现客户端,结果伙伴B实现服务器端。
客户端通过键盘输入一个有理数计算的公式(例如:1/4 + 1/6 = ),并把该公式以字符串的形式发送给伙伴B(服务器端),服务器端根据字符串计算出结果为5/12,并把结果返回给客户端A,A收到结果后输出结果。截图加水印上传蓝墨云,代码push码云。 - 实验五:远程有理数复数计算器,结对编程,结对伙伴B编程实现客户端,结果伙伴A实现服务器端。
客户端通过键盘输入一个有理数计算的公式(例如:1/4 + 1/6 = ),并把该公式以字符串的形式发送给伙伴A(服务器端),服务器端根据字符串计算出结果为5/12,并把结果返回给客户端B,B收到结果后输出结果。截图加水印上传蓝墨云,代码push码云
2实验过程及结果
实验一
学习javasocket编程,并与结对小伙伴一个实现服务端,一个实现客户端。
通过以上的学习,我们知道,socket无外乎就是一个借口,SSocket就像是发动机,提供了网络通信的能力。由此,我们利用老师给我们的代码
服务器:
package HttpSocket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by besti on 2019/9/29.
*/
public class Server1823 {
public static void main(String[] args) throws IOException {
//1.建立一个服务器Socket(ServerSocket)绑定指定端口
ServerSocket serverSocket=new ServerSocket(8800);
//2.使用accept()方法阻止等待监听,获得新连接
Socket socket=serverSocket.accept();
//3.获得输入流
InputStream inputStream=socket.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
//获得输出流
OutputStream outputStream=socket.getOutputStream();
PrintWriter printWriter=new PrintWriter(outputStream);
//4.读取用户输入信息
String info=null;
System.out.println("服务器已经建立......");
while(!((info = bufferedReader.readLine()) ==null)){
System.out.println("我是服务器,用户信息为:" + info);
}
//给客户一个响应
String reply="welcome";
printWriter.write(reply);
printWriter.flush();
//5.关闭资源
printWriter.close();
outputStream.close();
bufferedReader.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
客户端:
package HttpSocket;
import com.sun.corba.se.impl.orbutil.ObjectUtility;
import java.io.*;
import java.net.Socket;
/**
* Created by besti on 2018/6/9.
*/
public class SocketClient {
public static void main(String[] args) throws IOException {
//1.建立客户端Socket连接,指定服务器位置和端口
Socket socket = new Socket("localhost",8800);
// Socket socket = new Socket("172.16.43.187",8800);
//2.得到socket读写流
OutputStream outputStream = socket.getOutputStream();
// PrintWriter printWriter = new PrintWriter(outputStream);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
//输入流
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
//3.利用流按照一定的操作,对socket进行读写操作
String info1 = " 用户名:Tom,密码:123456";
// String info = new String(info1.getBytes("GBK"),"utf-8");
// printWriter.write(info);
// printWriter.flush();
outputStreamWriter.write(info1);
outputStreamWriter.flush();
socket.shutdownOutput();
//接收服务器的响应
String reply = null;
while (!((reply = bufferedReader.readLine()) ==null)){
System.out.println("接收服务器的信息为:" + reply);
}
//4.关闭资源
bufferedReader.close();
inputStream.close();
outputStreamWriter.close();
//printWriter.close();
outputStream.close();
socket.close();
}
}
在读取用户信息之前,我们是要建立一个接口,绑定一个端口,并且创建输出流和输入流的对象,这样两个程序之间的连接就算是做好了,接着作为服务器,就要从客户端出接受信息,这里用bufferedReader.readLine()来进行获取,而客户端是用outputStreamWriter.write来进行输出(这两个输入输出的对象在此之前已经建立),往下走,服务器再返回客户端一个信息(使用printWriter.write()),而客户端接受信息后就显示出来,最后再关闭我们的客户端和服务器。(运行结果中的乱码只需要把UTF-8挑为GBK就好了)。
实验二
Java和密码学:
根据楼老师的博客操作:
- 凯撒密码:先约定好将明文往后移动多少个字母,然后对明文进行加密。如:YKH加密后为AOJ。我们的代码如下
public static void main(String args[]) throws Exception{
String s=args[0];
int key=Integer.parseInt(args[1]);
String es="";
for(int i=0;i<s.length( );i++)
{ char c=s.charAt(i);
if(c>='a' && c<='z') // 是小写字母
{ c+=key%26; //移动key%26位
if(c<'a') c+=26; //向左超界
if(c>'z') c-=26; //向右超界
}
else if(c>='A' && c<='Z') // 是大写字母
{ c+=key%26;
if(c<'A') c+=26;
if(c>'Z') c-=26;
}
es+=c;
}
System.out.println(es);
}
运行截图:
在这个地方输入我们要加密的内容,然后往后移动四位的意思。
- Java对称加密-DES算法
运行截图:
运行DES程序,得到密钥key1.dat
保存密钥编码格式,在程序中输入java Skey_kb,在程序的当前目录中将产生文件名为keykb1.dat的文件,运行结果如下:
处理加密结果:
最终进行解密:
- Java非对称加密-RSA算法:
在凯撒密码里提到的老地方输入java Skey_RSA,运行RSA从而生成公钥和私钥。
继续输入java Skey_RSA,运行Enc RsA程序,得到如下结果:![]
这都显示了公钥中的参数以及加密的结果c。
接着运行程序输入java Dec_RSA运行DecRsa程序,得到如下结果:
其中显示了私钥中的参数以及解密的结果,其中整型的明文转换后显示出字符串“Hello World!”。
- 使用密钥协定创建共享密钥
建立A、B两个文件夹,然后分别在里面输入“java Key_DH Apub.dat Apri.dat”和“java Key_DH Bpub.dat Bpri.dat”运行,运行结果如图所示:
将程序KeyAgree编译后分别拷贝在A和B两个目录,首先在A目录输入“java KeyAgree Bpub.dat Apri.dat”运行程序,它使用文件Bpub.dat中对方的公钥和文件Apri.dat中自己的私钥创建了一段共享的字节数组。
实验三
根据要求,我编写了一个计算器如图:
实验四
编写远程有理数计算器,我们编写了两个类,一个rational类,一个script类,rational用来进行有理数计算,而script用来讲客户输入的字符串转换成double输入到rational中。以下为代码
rational.java
package com.company;
public class Rational {
private int numerator ,denominator;
public Rational(int numer,int denom)
{
if(denom == 0)
denom = 1;
if(denom < 0)
{
numer = numer * -1;
denom = denom * -1;
}
numerator = numer;
denominator = denom;
reduce();
}
public int getNumerator()
{
return numerator;
}
public int getDemoninator()
{
return denominator;
}
public Rational reciprocal()
{
return new Rational(denominator,numerator);
}
public Rational add (Rational op2)
{
int commonDenominator = denominator*op2.getDemoninator();
int numerator1 = numerator*op2.getDemoninator();
int numerator2 = op2.getNumerator()*denominator;
int sum = numerator1 + numerator2;
return new Rational(sum,commonDenominator);
}
public Rational subtract (Rational op2)
{
int commonDenominator = denominator*op2.getDemoninator();
int numerator1 = numerator*op2.getDemoninator();
int numerator2 = op2.getNumerator()*denominator;
int difference = numerator1 - numerator2;
return new Rational(difference,commonDenominator);
}
public Rational multipy(Rational op2)
{
int numer = numerator* op2.getNumerator();
int denom = denominator*op2.getDemoninator();
return new Rational(numer,denom);
}
public Rational divide (Rational op2)
{
return multipy(op2.reciprocal());
}
public String toString()
{
String result;
if (numerator == 0)
{
result = "0";
}
else
if(denominator == 1)
{
result = numerator + "";
}
else
result = numerator +"/"+denominator;
return result;
}
private void reduce()
{
if (numerator != 0)
{
int common =gcd (Math.abs(numerator),denominator);
numerator = numerator/common;
denominator = denominator / common;
}
}
private int gcd (int num1, int num2)
{
while (num1 != num2)
if(num1>num2)
num1 = num1 - num2;
else
num2= num2-num1;
return num1;
}
}
package com.company;
public class script
{
private String s ;
public script(String s)
{
this.s=s;
}
public String getresult()
{
String[] a1 = s.split("\*|\+|-|/");
String reply = "";
int a, b, c, d;
a = Integer.parseInt(a1[0]);
b = Integer.parseInt(a1[1]);
c = Integer.parseInt(a1[2]);
d = Integer.parseInt(a1[3]);
Rational rational = new Rational(a,b );
Rational rational1 = new Rational(c,d);
int i = 0;
int n = 0;
char[] e = s.toCharArray();
for (i = 0; i < e.length; i++) {
if (e[i] == '+') {
reply = rational.add(rational1).toString();
} else if (e[i] == '-') {
reply = rational.subtract(rational1).toString();
} else if (e[i] == '*') {
reply = rational.multipy(rational1).toString();
} else if (e[i]=='/')
{
n++;
}
else if (e[i]=='/'||n==3)
{
reply = rational.divide(rational1).toString();
}
}
return reply;
}
}
服务器
package com.company;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
* Created by besti on 2019/9/29.
*/
public class Server1823 {
public static void main(String[] args) throws IOException, ScriptException {
//1.建立一个服务器Socket(ServerSocket)绑定指定端口
ServerSocket serverSocket=new ServerSocket(8800);
//2.使用accept()方法阻止等待监听,获得新连接
Socket socket=serverSocket.accept();
//3.获得输入流
InputStream inputStream=socket.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
//获得输出流
OutputStream outputStream=socket.getOutputStream();
PrintWriter printWriter=new PrintWriter(outputStream);
//4.读取用户输入信息
String info=null;
System.out.println("服务器已经建立......");
info = bufferedReader.readLine();
System.out.println("我是服务器,用户信息为:" + info);
String reply = "";
script s = new script(info);
reply = s.getresult();
printWriter.write(reply);
printWriter.flush();
//5.关闭资源
printWriter.close();
outputStream.close();
bufferedReader.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
客户端
package com.company;
import java.io.*;
import java.net.Socket;
/**
* Created by besti on 2018/6/9.
*/
public class SocketClient {
public static void main(String[] args) throws IOException {
//1.建立客户端Socket连接,指定服务器位置和端口
Socket socket = new Socket("192.168.43.229",8800);
// Socket socket = new Socket("172.16.43.187",8800);
//2.得到socket读写流
OutputStream outputStream = socket.getOutputStream();
// PrintWriter printWriter = new PrintWriter(outputStream);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
//输入流
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
//3.利用流按照一定的操作,对socket进行读写操作
String info1 = "1/4+1/5";
// String info = new String(info1.getBytes("GBK"),"utf-8");
// printWriter.write(info);
// printWriter.flush();
outputStreamWriter.write(info1);
outputStreamWriter.flush();
socket.shutdownOutput();
//接收服务器的响应
String reply = null;
reply = bufferedReader.readLine();
System.out.println("接收服务器的信息为:" + reply);
//4.关闭资源
bufferedReader.close();
inputStream.close();
outputStreamWriter.close();
//printWriter.close();
outputStream.close();
socket.close();
}
}
运行结果如图所示:
实验五
同实验四相同,我们编写了一个Complex类,一个hello类,前者用来计算复数,后者用来实现将用户的字符串转换成数字输入到complex中。两次实验我们都使用了slipt来对字符串进行分割。
复数服务器
package com.company;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
* Created by besti on 2019/9/29.
*/
public class ServerComplex {
public static void main(String[] args) throws IOException, ScriptException {
//1.建立一个服务器Socket(ServerSocket)绑定指定端口
ServerSocket serverSocket=new ServerSocket(8800);
//2.使用accept()方法阻止等待监听,获得新连接
Socket socket=serverSocket.accept();
//3.获得输入流
InputStream inputStream=socket.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
//获得输出流
OutputStream outputStream=socket.getOutputStream();
PrintWriter printWriter=new PrintWriter(outputStream);
//4.读取用户输入信息
String info1=null;
System.out.println("服务器已经建立......");
info1 = bufferedReader.readLine();
System.out.println("第一个复数为" + info1);
String[] reply = info1.split("i");
hello hi = new hello(reply[0]);
hello hii = new hello(reply[1]);
double a,b,c,d;
a = hi.getrealpart();
b = hi.getimagepart();
c = hii.getrealpart();
d = hii.getimagepart();
char g = reply[2].charAt(0);
Complex c1 =new Complex(a,b);
Complex c2 =new Complex(c,d);
String e="";
if (g=='+')
{
e = c1.ComplexAdd(c2).toString();
}
else if(g=='-')
{
e = c1.ComplexSub(c2).toString();
}
else if(g=='*')
{
e = c1.ComplexMulti(c2).toString();
}
else if(g=='/')
{
e = c1.ComplexDiv(c2).toString();
}
printWriter.write(e);
printWriter.flush();
//5.关闭资源
printWriter.close();
outputStream.close();
bufferedReader.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
复数客户端
package com.company;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* Created by besti on 2018/6/9.
*/
public class SocketClient1823 {
public static void main(String[] args) throws IOException {
//1.建立客户端Socket连接,指定服务器位置和端口
Socket socket = new Socket("192.168.43.174",8800);
// Socket socket = new Socket("172.16.43.187",8800);
//2.得到socket读写流
OutputStream outputStream = socket.getOutputStream();
// PrintWriter printWriter = new PrintWriter(outputStream);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
//输入流
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
//3.利用流按照一定的操作,对socket进行读写操作
String info1 = "";
Scanner scan = new Scanner(System.in);
info1 = scan.nextLine();
String info2 = "";
info2 =scan.nextLine();
String s = scan.next();
// String info = new String(info1.getBytes("GBK"),"utf-8");
// printWriter.write(info);
// printWriter.flush();
outputStreamWriter.write(info1);
outputStreamWriter.flush();
outputStreamWriter.write(info2);
outputStreamWriter.flush();
outputStreamWriter.write(s);
outputStreamWriter.flush();
socket.shutdownOutput();
//接收服务器的响应
String reply = null;
reply = bufferedReader.readLine();
System.out.println("接收服务器的信息为:" + reply);
//4.关闭资源
bufferedReader.close();
inputStream.close();
outputStreamWriter.close();
//printWriter.close();
outputStream.close();
socket.close();
}
}
package com.company;
import static java.lang.Character.*;
public class hello
{
private int i=0;
private double n=0;
private int[] a = new int[2];
private String x;
public hello(String s)
{
x = s;
}
public double getrealpart()
{
String[] reply = x.split("\+|-|i");
n = Integer.parseInt(reply[0]);
return n;
}
public double getimagepart()
{
String[] reply1 = x.split("\+|-|i");
n = Integer.parseInt(reply1[1]);
return n;
}
}
package com.company;
public class Complex{
private double r;
private double i;
public Complex(double R, double I) {
r = R;
i = I;
}
public static double getRealPart(double r) {
return r;
}
public static double getImagePart(double i) {
return i;
}
public Complex ComplexAdd(Complex c) {
return new Complex(r + c.r, i + c.i);
}
public Complex ComplexSub(Complex c) {
return new Complex(r - c.r, i - c.i);
}
public Complex ComplexMulti(Complex c) {
return new Complex(r * c.r - i * c.i, r * c.i + i * c.r);
}
public Complex ComplexDiv(Complex c) {
return new Complex((r * c.i + i * c.r)/(c.i * c.i + c.r * c.r), (i * c.i + r * c.r)/(c.i * c.i + c.r * c.r));
}
public String toString() {
String s = " ";
if (i > 0)
s = r + "+" + i + "i";
if (i == 0)
s = r + "";
if (i < 0)
s = r + " " + i + "i";
return s;
}
}
运行结果如图所示:
3. 实验过程中遇到的问题和解决过程
-
问题1:出现connect reset问题。
-
问题1解决方法:一开始连接不上的时候我们查阅了百度,可是发现始终没有有效的解决方法,于是我决定把编写的类给去掉,检查连接是否有问题。
图片显示在没有类的情况下(里面的类是后面加上去的),并没有出现connect reset的问题,所以应该是类出现了问题。
那么我编写了一个主程序aswd来运行试试看类,(因时间仓促没有截图)在运行过程中可以发现类里面的问题,并及时纠正,这样有助于快速找出程序问题的原因。
(如图,我利用这个程序把自己写过的四个类都测试过,以此来找出程序中存在的问题)。
- 问题2:split的使用问题
- 问题2解决方法;split(“”)是将一个字符串一“”里的字符为标准,分成多个部分,如
String s = "jdwlkaj-dwjakjwd";
String[] a = s.split("-");
那么,a[0]="jdwlkaj",a[1]="dwjakjwd",但是当我们在“”里放入+号时,系统却显示错误?原来+ * 不是有效的模式匹配规则表达式,用"" "+"转义后即可得到正确的结果。只需要改为split(“|+”)程序就可以运行了。
其他(感悟、思考等)
- 本次实验看起来对我们难度非常的大,其实内在是很简单明了的,有理数类的加减乘除和复数类的加减乘除的代码书上其实都有,而我们只需要理解清楚javasocket编程代码上的含义还有明白如何运用类将字符串转换成数字就好了,造成这次实验完成的不好在于对于书本的不重视。
- 当程序运行出错的时候,其实可以将idea下方报错的名字信息复制粘贴到百度上来查询是为什么错了,这有利于我们自己发现问题,而不是有一点的问题都去询问助教。
参考资料
《Java程序设计与数据结构教程(第二版)》
《Java程序设计与数据结构教程(第二版)》学习指导