20192316 实验四 《数据结构与面向对象程序设计》实验报告
课程:《程序设计与数据结构》
班级: 1923
姓名: 贝世之
学号:20192316
实验教师:王志强
实验日期:2020年10月22日
必修/选修: 必修
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编写复数计算器。截图加水印上传蓝墨云,代码push码云。
(四)远程有理数计算器
结对编程,结对伙伴A编程实现客户端,结对伙伴B实现服务器端。
客户端通过键盘输入一个有理数计算的公式(例如:1/4 + 1/6 = ),并把该公式以字符串的形式发送给伙伴B(服务器端),服务器端根据字符串计算出结果为5/12,并把结果返回给客户端A,A收到结果后输出结果。截图加水印上传蓝墨云,代码push码云。
(五)远程复数计算器
结对编程,结对伙伴B编程实现客户端,结对伙伴A实现服务器端。
客户端通过键盘输入一个有理数计算的公式(例如:1/4 + 1/6 = ),并把该公式以字符串的形式发送给伙伴A(服务器端),服务器端根据字符串计算出结果为5/12,并把结果返回给客户端B,B收到结果后输出结果。截图加水印上传蓝墨云,代码push码云。
注意实验四(4)和实验四(5),一个人不能仅实现客户端,必须实现一个客户端和服务器,否则两个实验均不得分!!!
2. 实验过程及结果(本次实验由我(B)与20192309(A)结对完成)(加分项:在实验4、5中实现客户端加密传输,服务器解密反馈)
实验4.1 Java Socket编程
4.1.1 编写服务器
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//建立服务器绑定窗口
ServerSocket serverSocket = new ServerSocket(8800);
//accept()方法处理连接请求,防止非法监听
Socket socket = serverSocket.accept();
//输入流
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
//输出流
OutputStream outputStream = socket.getOutputStream();
PrintWriter printWriter = new PrintWriter(outputStream);
//读取用户信息
String info = null;
System.out.println("服务器正在建立...");
//反馈信息
while (!((info=bufferedReader.readLine())==null)){
System.out.println("我是服务器Bei,接受的信息为:"+info);
}
String reply = "Hello 1923!";
//传递信息
printWriter.write(reply);
printWriter.flush();
//关闭资源
inputStream.close();
outputStream.close();
bufferedReader.close();
printWriter.close();
serverSocket.close();
socket.close();
}
}
4.1.2 编写客户端
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//建立客户端,host为伙伴的IP地址
Socket socket = new Socket("192.168.43.164",8800);
//输入流
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
//输出流,获取socket读写流
OutputStream outputStream = socket.getOutputStream();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
//利用流按照一定的操作,传递信息,对socket进行读写操作
String info1 = "用户名:I am a bug 密码:1923";
outputStreamWriter.write(info1);
outputStreamWriter.flush();
socket.shutdownOutput();
//接受响应
String reply = null;
while (!((reply = bufferedReader.readLine())==null)){
System.out.println(reply);
}
//关闭资源
bufferedReader.close();
inputStream.close();
outputStreamWriter.close();
outputStream.close();
}
}
4.1.3 运行服务器及客户端
在同一热点下,伙伴A填写我的IP地址,先运行服务器再运行客户端,连接成功。如图:
实验4.2 Java和密码学
相关内容:
- 开发一个程序时,最好的方法就是将对象想象为“服务提供者”,程序通过调用若干对象提供的服务来实现预期目标。
- 密码学: 主要是研究保密通信和信息保密的学科, 包括信息保密传输和信息加密存储等。
- 密码编码学: 主要研究如何对信息进行编码, 如何实现对信息的隐蔽, 是密码学理论的基础, 也是保密系统设计的基础。
- 密码分析学: 主要研究加密消息的破译或消息的伪造, 是检验密码体制安全性最为直接的手段, 只有通过实际密码分析考验的密码体制,才是真正可用的。
- 对称密码(symmetric cryptography)是指在加密和解密时使用同一密钥的方式。
- 公钥密码(public-key cryptography)则是指在加密和解密时使用不同密钥的方式,公钥密码又称为非对称密码(asymmetric cryptography)。
4.2.1 凯撒密码
凯撒密码的加密算法极其简单。其加密过程如下:
我们做此约定:明文记为m,密文记为c,加密变换记为E(k1,m)(其中k1为密钥),解密变换记为D(k2,m)(k2为解密密钥)(在这里k1=k2,不妨记为k)。凯撒密码的加密过程可记为如下一个变换:
c≡m+k mod n (其中n为基本字符个数)
同样,解密过程可表示为:
m≡c+k mod n (其中n为基本字符个数)
代码如下:
public class Caesar { //该代码用于英文字母加密
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);
}
}
加密:
解密:
4.2.2 Java对称加密-DES算法
法一:将密钥通过对象序列化方式保存在文件中
import java.io.*;
import javax.crypto.*;
public class Skey_DES{
//在当前目录下将生成文件key1.dat,其中包含的密钥可以用于使用Triple-DES算法的加密和解密,将密钥通过对象序列化方式保存在文件中
public static void main(String args[]) throws Exception{
KeyGenerator kg=KeyGenerator.getInstance("DESede"); //获取密钥生成器,指定使用"DESede"算法
//与其它类的对象的创建方法不同,KeyGenerator通过它的静态方法getInstance()来创建对象
kg.init(168); //初始化密钥生成器kg,DESede为112或168位,其中112位有效
SecretKey k=kg.generateKey( ); //生成密钥。
//Keygenerator类中的generateKey方法可以生成密钥,类型是SecretKey,可用于后面的加密解密
//通过对象序列化方式将密钥保存在文件中
FileOutputStream f=new FileOutputStream("key1.dat");
//创建一个FileOutputStream类f,同时创建并让f指向一个新文件"key1.dat"
ObjectOutputStream b=new ObjectOutputStream(f);
//创建一个新的ObjectOutputStream类的对象
b.writeObject(k);
//将生成的密钥k序列化后保存在"key1.dat"文件中
}
}
法二:将密钥以字节保存在文件中
import java.io.*;
import java.security.*;
public class Skey_kb{
public static void main(String args[]) throws Exception{
FileInputStream f=new FileInputStream("key1.dat");
ObjectInputStream b=new ObjectInputStream(f);
Key k=(Key)b.readObject( );
//用readObject()读取密钥对象,并传入k中
//因为readObject()返回的是Object类,所以要强制转换成Key类
byte[ ] kb=k.getEncoded( );
//创建一个byte类型的数组kb[],用Key的getEncoded()方法获取密钥编码格式
FileOutputStream f2=new FileOutputStream("keykb1.dat");
f2.write(kb);
//保存密钥编码格式到"keykb1.dat"文件中
for(int i=0;i<kb.length;i++){
System.out.print(kb[i]+",");
// 打印密钥编码中的内容
}
}
}
运用生成的密钥,编写加密器,生成密文编码和密文文件
import java.io.*;
import java.security.*;
import javax.crypto.*;
public class SEnc{
public static void main(String args[]) throws Exception{
//将加密结果保存在文件Senc.dat中
FileInputStream f=new FileInputStream("key1.dat");
ObjectInputStream b=new ObjectInputStream(f);
Key k=(Key)b.readObject( );
//从文件"key1.dat"中获取密钥
Cipher cp=Cipher.getInstance("DESede");
//创建一个密码器(Cipher类)对象,并指定加密算法"DESede"。
//和KeyGenerator类一样,Cipher类是一个工厂类,不通过new方法创建对象,而是通过getInstance( )获取Cipher对象。
cp.init(Cipher.ENCRYPT_MODE, k);
//初始化密码器
String s="Hello World!";
byte ptext[]=s.getBytes("UTF8");
//获取等待加密的明文,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);
//处理加密结果,将密文存入"SEnc.dat"文件
}
}
使用密钥文件解密
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class SDec{
public static void main(String args[]) throws Exception{
FileInputStream f=new FileInputStream("SEnc.dat");
int num=f.available();
byte[ ] ctext=new byte[num];
f.read(ctext);
// 获取密文
FileInputStream f2=new FileInputStream("keykb1.dat");
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);
// 显示明文
}
}
4.2.3 Java非对称加密-RSA算法
获取公钥和私钥
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class Skey_RSA{
//当前目录下将生成两个文件:Skey_RSA_pub.dat和Skey_RSA_priv.dat,前者保存着公钥,后者保存着私钥。
public static void main(String args[]) throws Exception{
KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
//创建密钥对生成器
kpg.initialize(1024);
//初始化密钥生成器
KeyPair kp=kpg.genKeyPair();
//生成密钥对
PublicKey pbkey=kp.getPublic();
PrivateKey prkey=kp.getPrivate();
//生成公钥和私钥
FileOutputStream f1=new FileOutputStream("Skey_RSA_pub.dat");
ObjectOutputStream b1=new ObjectOutputStream(f1);
b1.writeObject(pbkey);
//保存公钥
FileOutputStream f2=new FileOutputStream("Skey_RSA_priv.dat");
ObjectOutputStream b2=new ObjectOutputStream(f2);
b2.writeObject(prkey);
//保存私钥
}
}
公钥加密
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
import java.security.interfaces.*;
import java.math.*;
import java.io.*;
public class Enc_RSA{
public static void main(String args[]) throws Exception{
String s="Hello World!";
FileInputStream f=new FileInputStream("Skey_RSA_pub.dat");
ObjectInputStream b=new ObjectInputStream(f);
RSAPublicKey pbk=(RSAPublicKey)b.readObject( );
BigInteger e=pbk.getPublicExponent();
BigInteger n=pbk.getModulus();
System.out.println("e= "+e);
System.out.println("n= "+n);
// 获取公钥及参数e,n
byte ptext[]=s.getBytes("UTF8");
BigInteger m=new BigInteger(ptext);
// 明文 m
BigInteger c=m.modPow(e,n);
System.out.println("c= "+c);
// 计算密文c,打印
String cs=c.toString( );
BufferedWriter out=
new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("Enc_RSA.dat")));
out.write(cs,0,cs.length( ));
out.close( );
// 保存密文
}
}
私钥解密
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
import java.security.interfaces.*;
import java.math.*;
import java.io.*;
public class Dec_RSA{
public static void main(String args[]) throws Exception{
BufferedReader in=
new BufferedReader(new InputStreamReader(
new FileInputStream("Enc_RSA.dat")));
String ctext=in.readLine();
BigInteger c=new BigInteger(ctext);
//读取密文
FileInputStream f=new FileInputStream("Skey_RSA_priv.dat");
ObjectInputStream b=new ObjectInputStream(f);
RSAPrivateKey prk=(RSAPrivateKey)b.readObject( );
BigInteger d=prk.getPrivateExponent();
//读取私钥
BigInteger n=prk.getModulus();
System.out.println("d= "+d);
System.out.println("n= "+n);
BigInteger m=c.modPow(d,n);
//获取私钥参数及解密
System.out.println("m= "+m);
byte[] mt=m.toByteArray();
System.out.println("PlainText is ");
for(int i=0;i<mt.length;i++){
System.out.print((char) mt[i]);
}
//显示解密结果
}
}
4.2.4 使用密钥协定创建共享密钥
创建DH公钥和私钥
建立两个目录A和B,模拟需要秘密通信的A、B双方,由于DH算法需要A和B各自生成DH公钥和私钥,因此在这两个目录下都拷贝编译后文件Key_DH。
import java.io.*;
import java.math.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
public class Key_DH{
private static final byte skip1024ModulusBytes[] = {
(byte)0xF4, (byte)0x88, (byte)0xFD, (byte)0x58,
(byte)0x4E, (byte)0x49, (byte)0xDB, (byte)0xCD,
(byte)0x20, (byte)0xB4, (byte)0x9D, (byte)0xE4,
(byte)0x91, (byte)0x07, (byte)0x36, (byte)0x6B,
(byte)0x33, (byte)0x6C, (byte)0x38, (byte)0x0D,
(byte)0x45, (byte)0x1D, (byte)0x0F, (byte)0x7C,
(byte)0x88, (byte)0xB3, (byte)0x1C, (byte)0x7C,
(byte)0x5B, (byte)0x2D, (byte)0x8E, (byte)0xF6,
(byte)0xF3, (byte)0xC9, (byte)0x23, (byte)0xC0,
(byte)0x43, (byte)0xF0, (byte)0xA5, (byte)0x5B,
(byte)0x18, (byte)0x8D, (byte)0x8E, (byte)0xBB,
(byte)0x55, (byte)0x8C, (byte)0xB8, (byte)0x5D,
(byte)0x38, (byte)0xD3, (byte)0x34, (byte)0xFD,
(byte)0x7C, (byte)0x17, (byte)0x57, (byte)0x43,
(byte)0xA3, (byte)0x1D, (byte)0x18, (byte)0x6C,
(byte)0xDE, (byte)0x33, (byte)0x21, (byte)0x2C,
(byte)0xB5, (byte)0x2A, (byte)0xFF, (byte)0x3C,
(byte)0xE1, (byte)0xB1, (byte)0x29, (byte)0x40,
(byte)0x18, (byte)0x11, (byte)0x8D, (byte)0x7C,
(byte)0x84, (byte)0xA7, (byte)0x0A, (byte)0x72,
(byte)0xD6, (byte)0x86, (byte)0xC4, (byte)0x03,
(byte)0x19, (byte)0xC8, (byte)0x07, (byte)0x29,
(byte)0x7A, (byte)0xCA, (byte)0x95, (byte)0x0C,
(byte)0xD9, (byte)0x96, (byte)0x9F, (byte)0xAB,
(byte)0xD0, (byte)0x0A, (byte)0x50, (byte)0x9B,
(byte)0x02, (byte)0x46, (byte)0xD3, (byte)0x08,
(byte)0x3D, (byte)0x66, (byte)0xA4, (byte)0x5D,
(byte)0x41, (byte)0x9F, (byte)0x9C, (byte)0x7C,
(byte)0xBD, (byte)0x89, (byte)0x4B, (byte)0x22,
(byte)0x19, (byte)0x26, (byte)0xBA, (byte)0xAB,
(byte)0xA2, (byte)0x5E, (byte)0xC3, (byte)0x55,
(byte)0xE9, (byte)0x2F, (byte)0x78, (byte)0xC7
};
//三个静态变量的定义从
// C:\j2sdk-1_4_0-doc\docs\guide\security\jce\JCERefGuide.html
// 拷贝而来
// The 1024 bit Diffie-Hellman modulus values used by SKIP
private static final BigInteger skip1024Modulus
= new BigInteger(1, skip1024ModulusBytes);
// The SKIP 1024 bit modulus
private static final BigInteger skip1024Base = BigInteger.valueOf(2);
// The base used with the SKIP 1024 bit modulus
public static void main(String args[ ]) throws Exception{
DHParameterSpec DHP=
new DHParameterSpec(skip1024Modulus,skip1024Base);
KeyPairGenerator kpg= KeyPairGenerator.getInstance("DH");
kpg.initialize(DHP);
KeyPair kp=kpg.genKeyPair();
PublicKey pbk=kp.getPublic();
PrivateKey prk=kp.getPrivate();
FileOutputStream f1=new FileOutputStream(args[0]);
ObjectOutputStream b1=new ObjectOutputStream(f1);
b1.writeObject(pbk);
// 保存公钥
FileOutputStream f2=new FileOutputStream(args[1]);
ObjectOutputStream b2=new ObjectOutputStream(f2);
b2.writeObject(prk);
// 保存私钥
}
}
调整命令行参数,分别改为创建的两个文件的名字,运行
创建共享密钥
同样需要在A、B两个目录下都拷贝编译代码
import java.io.*;
import java.math.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
public class KeyAgree{
public static void main(String args[ ]) throws Exception{
FileInputStream f1=new FileInputStream(args[0]);
ObjectInputStream b1=new ObjectInputStream(f1);
PublicKey pbk=(PublicKey)b1.readObject( );
// 读取对方的DH公钥
FileInputStream f2=new FileInputStream(args[1]);
ObjectInputStream b2=new ObjectInputStream(f2);
PrivateKey prk=(PrivateKey)b2.readObject( );
//读取自己的DH私钥
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]+",");
}
SecretKeySpec k=new SecretKeySpec(sb,"DESede");
//生成共享信息
}
}
将命令行参数修改成“对方的公钥名+自己的私钥名”
运行并观察打印结果是否相同,若相同说明则共享成功
4.2.5 Java摘要算法- MD5
import java.security.*;
public class DigestPass{
public static void main(String args[ ]) throws Exception{
String x=args[0];
MessageDigest m=MessageDigest.getInstance("MD5");
//生成对象并指定MD5算法
m.update(x.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);
}
System.out.println(result);
}
}
修改命令行参数为需要加密的明文,运行
4.3 编写有理数/复数计算器
4.1.1 伙伴A的有理数计算器
代码
public class RationalNumberCalculator {
public static void main(String[] args) {
fenshu result;
fenshu fenshu1,fenshu2;
if(args[0].contains("/")){
fenshu1=new fenshu(args[0]);
}
else{
fenshu1=new fenshu(args[0]+"/"+1);
}
if(args[2].contains("/")){
fenshu2=new fenshu(args[2]);
}
else{
fenshu2=new fenshu(args[2]+"/"+1);
}
char ch=args[1].charAt(0);
switch (ch)
{
case '+':
result=fenshu1.getJia(fenshu2);
System.out.println(fenshu1+String.valueOf(ch)+fenshu2+"="+result);
break;
case '-':
result=fenshu1.getJian(fenshu2);
System.out.println(fenshu1+String.valueOf(ch)+fenshu2+"="+result);
break;
case '*':
result=fenshu1.getCheng(fenshu2);
System.out.println(fenshu1+String.valueOf(ch)+fenshu2+"="+result);
break;
case '/':
result=fenshu1.getChu(fenshu2);
System.out.println(fenshu1+String.valueOf(ch)+fenshu2+"="+result);
break;
default:
System.out.println("Illegal input!");
break;
}
}
}
分数类
import java.util.StringTokenizer;
public class fenshu {
int fenzi,fenmu;
char ch;
public fenshu(String str) {
StringTokenizer st=new StringTokenizer(str,"/",true);
this.fenzi = Integer.parseInt(st.nextToken());
this.ch=st.nextToken().charAt(0);
this.fenmu = Integer.parseInt(st.nextToken());
}
public fenshu yuefen(int fz,int fm){
int i;
for (i=2;i<=fz&&i<=fm;i++){
if(fz%i==0&&fm%i==0){
fz=fz/i;
fm=fm/i;
}
}
fenshu result=new fenshu(fz+"/"+fm);
return result;
}
public fenshu getJia(fenshu x){
int newFenmu=this.fenmu*x.fenmu;
int newFenzi=fenzi*x.fenmu+x.fenzi*fenmu;
return yuefen(newFenzi,newFenmu);
}
public fenshu getJian(fenshu x){
int newFenmu=fenmu*x.fenmu;
int newFenzi=fenzi*x.fenmu-x.fenzi*fenmu;
return yuefen(newFenzi,newFenmu);
}
public fenshu getCheng(fenshu x){
int newFenmu=fenmu*x.fenmu;
int newFenzi=fenzi*x.fenzi;
return yuefen(newFenzi,newFenmu);
}
public fenshu getChu(fenshu x){
int newFenmu=fenmu*x.fenzi;
int newFenzi=fenzi*x.fenmu;
return yuefen(newFenzi,newFenmu);
}
@Override
public String toString() {
return fenzi + "/" + fenmu;
}
}
运行截图
4.3.2 复数计算器
父类代码:
//父类
package Lei;
import java.util.StringTokenizer;
public abstract class Calculate {
static double a,b,c,d;
double RealPart;
double ImagePart;
static String choose;
public static char ch;
public void Add() {
RealPart = a + c;
ImagePart = b + d;
}
public void Sub() {
RealPart = a - c;
ImagePart = b - d;
}
public void Mul() {
RealPart = a*c - b*d;
ImagePart = b*c + a*d;
}
public void Div() {
RealPart = (a*c + b*d)/(c*c + d*d);
ImagePart = (b*c + a*d)/(c*c + d*d);
}
public String toString(String s) {
if(ImagePart >= 0){
return s + "=" + RealPart + "+" + ImagePart + "i";
}else {
return s + "=" + RealPart +""+ ImagePart + "i";
}
}
public void Faction(String f){
char z; //取出不用于计算的符号
StringTokenizer st = new StringTokenizer(f, " ", false);
z = st.nextToken().charAt(0);
a = Double.parseDouble(st.nextToken());
z = st.nextToken().charAt(0);
b = Double.parseDouble(st.nextToken());
z = st.nextToken().charAt(0);
z = st.nextToken().charAt(0);
ch = st.nextToken().charAt(0);
z = st.nextToken().charAt(0);
c = Double.parseDouble(st.nextToken());
z = st.nextToken().charAt(0);
d = Double.parseDouble(st.nextToken());
}
}
子类代码:
//子类
package Lei;
import java.util.Scanner;
public class Plural extends Calculate{
public static void main(String[] args) {
String next = "y"; //用于后续判断是否继续计算
String s; //用于接收输入和输出
do{
Scanner scan = new Scanner(System.in);
System.out.println("请输入复数运算式,各符号数字间用空格隔开,负数除外(如:( 1 + 2 i ) + ( 2 + -3 i ) ):");
Calculate ff;
ff = new Plural();
s = scan.nextLine();
ff.Faction(s); //输入并提取出a,b,c,d进行运算
Calculate count;
count = new Plural();
switch (ch){
case '+':
{
count.Add();
break;
}
case '-':
{
count.Sub();
break;
}
case '*':
{
count.Mul();
break;
}
case '/':
{
count.Div();
break;
}
default:
System.out.println("Illegal input!");
break;
}
System.out.println(count.toString(s));
System.out.print("如果您想要继续计算,请输入y,否则输入其他: ");
choose = scan.nextLine();
}while(choose.equals(next));
}
}
运行截图
4.4 远程有理数计算器
伙伴A的客户端
import java.io.*;
import java.net.Socket;
public class client2 {
public static void main(String[] args) throws IOException {
//1.建立客户端Socket连接,指定服务器位置和端口
Socket socket = new Socket("192.168.43.9",8809);
// 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/6";
String z= info1;
String tes="";
int key = 4;
for(int j=0;j<z.length( );j++)
{ char d=z.charAt(j);
if(d>=0 && d<=127) // 是小写字母
{ d+=key%26; //移动key%26位
if(d<0) d+=127; //向左超界
if(d>127) d-=127; //向右超界
}
tes+=d;
}
// String info = new String(info1.getBytes("GBK"),"utf-8");
// printWriter.write(info);
// printWriter.flush();
outputStreamWriter.write(tes);
outputStreamWriter.flush();
socket.shutdownOutput();
//接收服务器的响应
String reply = null;
while (!((reply = bufferedReader.readLine()) ==null)){
System.out.println("接收服务器的信息密文为:" + reply);
String s= reply;
String es="";
key = -4;
for(int i=0;i<s.length( );i++)
{ char c=s.charAt(i);
if(c>=0 && c<=127) // 是小写字母
{ c+=key%26; //移动key%26位
if(c<0) c+=127; //向左超界
if(c>127) c-=127; //向右超界
}
es+=c;
}
System.out.println("接收服务器的信息明文为:" + es);
}
//4.关闭资源
bufferedReader.close();
inputStream.close();
outputStreamWriter.close();
//printWriter.close();
outputStream.close();
socket.close();
}
}
伙伴A的服务器
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
public class server2 {
private static fenshu frac2;
private static fenshu frac1;
private static String a,b;
private static char ch;
private static fenshu result = null;
public static void main(String[] args) throws IOException {
//1.建立一个服务器Socket(ServerSocket)绑定指定端口
ServerSocket serverSocket=new ServerSocket(8809);
//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 s= info;
String es="";
int key = -4;
for(int i=0;i<s.length( );i++)
{ char c=s.charAt(i);
if(c>=0 && c<=127) // 是小写字母
{ c+=key%26; //移动key%26位
if(c<0) c+=127; //向左超界
if(c>127) c-=127; //向右超界
}
es+=c;
}
System.out.println("我是服务器,用户的解密后的信息为:" + es);
StringTokenizer st = new StringTokenizer(es, " ", false);
a=st.nextToken();
ch=st.nextToken().charAt(0);
b=st.nextToken();
frac1=new fenshu(a);
frac2=new fenshu(b);
switch (ch)
{
case '+':
result=frac1.getJia(frac2);
break;
case '-':
result=frac1.getJian(frac2);
break;
case '*':
result=frac1.getCheng(frac2);
break;
case '/':
result=frac1.getChu(frac2);
break;
default:
break;
}
}
//给客户一个响应
String reply=frac1+String.valueOf(ch)+frac2+"="+result;
String z= reply;
String tes="";
int key = 4;
for(int j=0;j<z.length( );j++)
{ char d=z.charAt(j);
if(d>=0 && d<=127) // 是小写字母
{ d+=key%26; //移动key%26位
if(d<0) d+=127; //向左超界
if(d>127) d-=127; //向右超界
}
tes+=d;
}
printWriter.write(tes);
printWriter.flush();
//5.关闭资源
printWriter.close();
outputStream.close();
bufferedReader.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
运行截图
4.5 远程复数计算器
客户端:
import java.io.*;
import java.net.Socket;
public class Client1 {
public static void main(String[] args) throws IOException {
//建立客户端,host为伙伴的IP地址
Socket socket = new Socket("localhost",8800);
//输入流
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
//输出流,获取socket读写流
OutputStream outputStream = socket.getOutputStream();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
//利用流按照一定的操作,传递信息,对socket进行读写操作
//运用凯撒密码,加分!!!
String s= "( 1 + 2 i ) + ( 1 + -2 i )"; //取命令行参数的第一个字符,即需要加密的明文
int key=4; //取命令行参数的第二个字符,即密钥,并将其转为整型
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>='0' && c<='9') // 是数字
{
c+=key%10;
if(c<'0') c+=10;
if(c>'9') c-=10;
}
else if(c>='*' && c<='/')
{
c+=key%6;
if(c<'*') c+=6;
if(c>'/') c-=6;
}
es+=c;
}
System.out.println(es);
String info1 = es;
outputStreamWriter.write(info1);
outputStreamWriter.flush();
socket.shutdownOutput();
//接受响应
String reply = null;
while (!((reply = bufferedReader.readLine())==null)){
System.out.println(reply);
}
//关闭资源
bufferedReader.close();
inputStream.close();
outputStreamWriter.close();
outputStream.close();
}
}
服务器
import Lei.Calculate;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server1 extends Calculate{
public static void main(String[] args) throws IOException {
//建立服务器绑定窗口
ServerSocket serverSocket = new ServerSocket(8800);
//accept()方法处理连接请求,防止非法监听
Socket socket = serverSocket.accept();
//输入流
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
//输出流
OutputStream outputStream = socket.getOutputStream();
PrintWriter printWriter = new PrintWriter(outputStream);
//读取用户信息
String info = null;
System.out.println("服务器正在建立...");
//反馈信息
while (!((info=bufferedReader.readLine())==null)) {
System.out.println("我是服务器Bei,接受的加密信息为:" + info);
//运用凯撒密码,加分!!!
int key = -4; //取命令行参数的第二个字符,即密钥,并将其转为整型
String es = ""; //创建接收密文的字符串变量并初始化
for (int i = 0; i < info.length(); i++) { //逐一对明文字符进行加密
char c = info.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 >= '0' && c <= '9') // 是数字
{
c += key % 10;
if (c < '0') c += 10;
if (c > '9') c -= 10;
} else if (c >= '*' && c <= '/') {
c += key % 6;
if (c < '*') c += 6;
if (c > '/') c -= 6;
}
es += c;
}
System.out.println("解密后的信息为:" + es);
Calculate ff;
ff = new Server1();
ff.Faction(es);
Calculate count;
count = new Server1();
switch (ch){
case '+':
{
count.Add();
break;
}
case '-':
{
count.Sub();
break;
}
case '*':
{
count.Mul();
break;
}
case '/':
{
count.Div();
break;
}
}
String reply = count.toString(es);
//传递信息
printWriter.write(reply);
printWriter.flush();
//关闭资源
inputStream.close();
outputStream.close();
bufferedReader.close();
printWriter.close();
serverSocket.close();
socket.close();
}
}
}
运行截图
3. 实验过程中遇到的问题和解决过程
-
问题1:我的服务器无法和伙伴的客户端连接不上。
-
问题1解决方案:换个热点。
-
问题2:编写复数计算器时不知道怎么把输入的字符截取成几段。
-
问题2解决方案:学习使用StringTokenizer。
-
问题3:.length()无法正常使用,产生错误。
-
问题3解决方案:考虑空指针的情况,将凯撒密码加密解密过程囊括进while (!((info=bufferedReader.readLine())==null))之中。
其他(感悟、思考等)
这次是真的有亿点多,我并没有完全掌握本次实验内容,后续空余时间我将回顾本次代码。