实验4
学号 2019-2020-1 《数据结构与面向对象程序设计》实验4报告
课程:《程序设计与数据结构》
班级: 1823
姓名: 郑力元
学号:20182320
实验教师:王志强
实验日期:2019年10月4日
必修/选修: 必修
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. 实验过程及结果
实验4.1
第一步:编写简单的服务器和终端
代码老师已给出。
第二步:在伙伴的电脑上运行服务器代码,在我的电脑上运行终端代码
我将ip地址修改成伙伴电脑的之后,我的终端成功连接伙伴的服务器,并接收到反馈,如上。
第三步:在我的电脑上运行服务器代码,在伙伴电脑上运行终端代码
和第二步一样修改ip地址并实现连通后,出现账号和密码,如上。
实验4.2
要点:
- 什么是密码学?
答:主要是研究保密通信和信息保密的学科,包括信息保密传输和信息加密存储等。
- 密码学的两大分支:
密码编码学: 主要研究如何加密信息。
密码分析学: 主要研究如何破译密码。
编码学与分析学相互促进, 又相互制约。
- 根据密钥的使用方法,将密码分为两种:
对称密码(symmetric cryptography) 是指在加密和解密时使用同一密钥的方式。
公钥密码(public-key cryptography) 则是指在加密和解密时使用不同密钥的方式,公钥密码又称为非对称密码(asymmetric cryptography)。
- 密码与信息安全常识:
不要使用保密的密码算法
使用低强度的密码比不进行任何加密更危险
任何密码总有一天都会被破解
密码只是信息安全的一部分
实验4.2.1:凯撒密码的加解密
简介:
凯撒密码的加密方法是,将明文的每个英文字母都向前(-x)或向后移动x位(+x),将明文加密成密文,这个x是密钥。
第一步:编写代码(详解见下面注释)
//首先,往这个方法中输入一个参数,类型是字符串型,赋给字符串数组args[]
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算法
第一步:编写生成密钥代码(详解见下面注释)
方式1:创建密钥文件"key1.dat"并储存密钥对象
import java.io.*;
import javax.crypto.*;
public class Skey_DES{
public static void main(String args[])
throws Exception{
KeyGenerator kg=KeyGenerator.getInstance("DESede");
//这一行是创建一个密钥生成器,指定使用"DESede"算法
//与其它类的对象的创建方法不同,KeyGenerator通过它的静态方法getInstance()来创建对象
kg.init(168);
//初始化密钥生成器kg
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"文件中
}
}
方式2:创建密钥文件"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]+",");
// 打印密钥编码中的内容
}
}
}
第二步:运行程序并生成"key1.dat"密钥文件
方式1:
方式2:
第三步: 运用第一步第二步生成的密钥,编写加密器
import java.io.*;
import java.security.*;
import javax.crypto.*;
public class SEnc{
public static void main(String args[]) throws Exception{
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"
cp.init(Cipher.ENCRYPT_MODE, k);
//初始化密码器
String s="Hello World!";
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);
//将密文存入"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{
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公钥和私钥
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);
// 保存私钥
}
}
这里需要调整Program arguments参数,分别改为创建的两个文件的名字
然后分别运行,产生公钥和私钥文件
第二步:共享密钥
编写
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");
//生成共享信息
}
}
将这个代码分别复制到A和B的文件夹下,然后将Program arguments修改成“对方的公钥名+自己的私钥名”
最后再运行并观察打印结果是否相等,若相等则共享成功
实验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);
}
}
第二步:修改Program arguments为需要加密的明文
第三步:运行
实验4.3 编写有理数/复数计算器
实验4.3.1 编写有理数计算器
第一步:编写分数类
import java.util.StringTokenizer;
public class Fraction {
int fenzi,fenmu;
char ch;
public Fraction(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 Fraction 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;
}
}
Fraction result=new Fraction(fz+"/"+fm);
return result;
}
public Fraction getJia(Fraction x){
int newFenmu=fenmu*x.fenmu;
int newFenzi=fenzi*x.fenmu+x.fenzi*fenmu;
return yuefen(newFenzi,newFenmu);
}
public Fraction getJian(Fraction x){
int newFenmu=fenmu*x.fenmu;
int newFenzi=fenzi*x.fenmu-x.fenzi*fenmu;
return yuefen(newFenzi,newFenmu);
}
public Fraction getCheng(Fraction x){
int newFenmu=fenmu*x.fenmu;
int newFenzi=fenzi*x.fenzi;
return yuefen(newFenzi,newFenmu);
}
public Fraction getChu(Fraction x){
int newFenmu=fenmu*x.fenzi;
int newFenzi=fenzi*x.fenmu;
return yuefen(newFenzi,newFenmu);
}
@Override
public String toString() {
return fenzi + "/" + fenmu;
}
}
}
}
第二步:编写有理数计算器代码
public class RationalNumberCalculator {
public static void main(String[] args) {
Fraction result;
Fraction frac1,frac2;
if(args[0].contains("/")){
frac1=new Fraction(args[0]);
}
else{
frac1=new Fraction(args[0]+"/"+1);
}
if(args[2].contains("/")){
frac2=new Fraction(args[2]);
}
else{
frac2=new Fraction(args[2]+"/"+1);
}
char ch=args[1].charAt(0);
switch (ch)
{
case '+':
result=frac1.getJia(frac2);
System.out.println(frac1+String.valueOf(ch)+frac2+"="+result);
break;
case '-':
result=frac1.getJian(frac2);
System.out.println(frac1+String.valueOf(ch)+frac2+"="+result);
break;
case '*':
result=frac1.getCheng(frac2);
System.out.println(frac1+String.valueOf(ch)+frac2+"="+result);
break;
case '/':
result=frac1.getChu(frac2);
System.out.println(frac1+String.valueOf(ch)+frac2+"="+result);
break;
default:
System.out.println("Illegal input!");
break;
}
}
}
第三步:运行
4.3.2 编写复数计算器
第一步:编写复数类
import java.util.StringTokenizer;
public class Complex{
private int RealPart;
private int ImagePart;
private char ch;
//构造函数
public Complex(){}
public Complex(String str){
StringTokenizer st=new StringTokenizer(str,".",true);
this.RealPart=Integer.parseInt(st.nextToken());
this.ch=st.nextToken().charAt(0);
this.ImagePart=Integer.parseInt(st.nextToken());
}
//返回
public double getRealPart(){
return RealPart;
}
public double getImagePart(){
return ImagePart;
}
//接收
public void setRealPart(int realPart){
RealPart=realPart;
}
public void setImagePart(int imagePart){
ImagePart=imagePart;
}
//重写函数toString
public String toString(){
return RealPart + "." + ImagePart;
}
public boolean equals(Object a){
if (a==this){
return true;
}
else{
return false;
}
}
// 定义公有方法:加减乘除
public Complex ComplexAdd(Complex a){
Complex b = new Complex((this.RealPart+a.RealPart)+"."+(this.ImagePart+a.ImagePart));
return b;
}
public Complex ComplexSub(Complex a){
Complex b = new Complex((this.RealPart-a.RealPart)+"."+(this.ImagePart-a.ImagePart));
return b;
}
public Complex ComplexMulti(Complex a){
Complex b = new Complex((this.RealPart*a.RealPart-this.ImagePart*a.ImagePart)+"."+(this.ImagePart*a.RealPart+this.RealPart*a.ImagePart));
return b;
}
public Complex ComplexDiv(Complex a){
double scale = a.getRealPart()*a.getRealPart() + a.getImagePart()*a.getImagePart();
Complex b = new Complex((a.getRealPart() / scale)+"."+
(- a.getImagePart() / scale));
return b;
}
}
第二步:编写复数计算器
public class ComplexNumberCalculator{
public static void main(String[] args) {
Complex num1,num2,result;
char ch=args[1].charAt(0);
if(args[0].contains(".")){
num1=new Complex(args[0]);
}
else{
num1=new Complex(args[0]+"."+0);
}
if(args[2].contains(".")){
num2=new Complex(args[2]);
}
else{
num2=new Complex(args[2]+"."+0);
}
switch (ch)
{
case '+':
result=num1.ComplexAdd(num2);
System.out.println(num1+String.valueOf(ch)+num2+"="+result);
break;
case '-':
result=num1.ComplexSub(num2);
System.out.println(num1+String.valueOf(ch)+num2+"="+result);
break;
case '*':
result=num1.ComplexMulti(num2);
System.out.println(num1+String.valueOf(ch)+num2+"="+result);
break;
case '/':
result=num1.ComplexDiv(num2);
System.out.println(num1+String.valueOf(ch)+num2+"="+result);
break;
default:
System.out.println("Illegal input!");
break;
}
}
}
第三步:运行
注:我在这里为了输入简便,将输入的实部和虚部用"."符号分隔。
实验4.4 远程有理数计算器
第一步:基于上面编写的分数类、计算器代码和之前老师给过的服务器和终端代码进行修改,得出如下代码:
终端:
import java.io.*;
import java.net.Socket;
/**
* Created by besti on 2018/6/9.
*/
public class SocketClient2 {
public static void main(String[] args) throws IOException {
//1.建立客户端Socket连接,指定服务器位置和端口
Socket socket = new Socket("192.168.43.45",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 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();
}
}
服务器:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
/**
* Created by besti on 2019/9/29.
*/
public class Server18232 {
private static Fraction frac2;
private static Fraction frac1;
private static String a,b;
private static char ch;
private static Fraction 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);
StringTokenizer st = new StringTokenizer(info, " ", false);
a=st.nextToken();
ch=st.nextToken().charAt(0);
b=st.nextToken();
frac1=new Fraction(a);
frac2=new Fraction(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;
printWriter.write(reply);
printWriter.flush();
//5.关闭资源
printWriter.close();
outputStream.close();
bufferedReader.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
第二步:修改参数后,先运行服务器后运行终端
实验4.5 远程复数计算器
第一步:基于上面编写的复数类、计算器代码和之前老师给过的服务器和终端代码进行修改,得出如下代码:
终端:
import java.io.*;
import java.net.Socket;
/**
* Created by besti on 2018/6/9.
*/
public class SocketClient3 {
public static void main(String[] args) throws IOException {
//1.建立客户端Socket连接,指定服务器位置和端口
Socket socket = new Socket("192.168.43.45",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 = " 4.8 * 5.6";
// 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();
}
}
服务器:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
/**
* Created by besti on 2019/9/29.
*/
public class Server18233 {
private static Complex cplx1;
private static Complex cplx2;
private static String a,b;
private static char ch;
private static Complex 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);
StringTokenizer st = new StringTokenizer(info, " ", false);
a = st.nextToken();
ch = st.nextToken().charAt(0);
b = st.nextToken();
cplx1 = new Complex(a);
cplx2 = new Complex(b);
switch (ch) {
case '+':
result = cplx1.ComplexAdd(cplx2);
break;
case '-':
result = cplx1.ComplexSub(cplx2);
break;
case '*':
result = cplx1.ComplexMulti(cplx2);
break;
case '/':
result = cplx1.ComplexDiv(cplx2);
break;
default:
break;
}
}
//给客户一个响应
String reply=cplx1+String.valueOf(ch)+cplx2+"="+result;
printWriter.write(reply);
printWriter.flush();
//5.关闭资源
printWriter.close();
outputStream.close();
bufferedReader.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
第二步:修改参数后,先运行服务器再运行终端
3. 实验过程中遇到的问题和解决过程
- 问题1:什么是密钥?
- 问题1解决方案:
百度百科:
密钥是一个参数,在使用加密算法进行加密时,往里面输入的参数。
- 问题2:搞不懂凯撒密码程序的下面这行代码的目的
- 问题2解决方案:
首先,明确key是密钥。
其次,查找Integer.parseInt()方法的说明:
最后,明确在主方法中只能输入字符串类型的参数,因此需要上面这行代码将输入的密钥转化成整型变量,以用于后面的加解密计算。
- 问题3:FileInputStream和FileOutputStream的用法?
- 问题3解决方案:
CSDN:
可见分别是用来读取文件中的数据和生成文件的。
-
问题4:在System.out.println()中想要打印字符型,但是却打印出了它的ASCII码值。
-
问题4解决方案:使用String.valueOf('字符')的方法,就能将字符型以字符串形式返回。
-
问题5:计算器无法实现分数的计算
-
问题5解决方案:自己编写一个分数类
其他(感悟、思考等)
作业看似不多,但是通往成功的路上充满了坎坷。特别是报出几行错之后开始肉眼调试,我太难了。