实验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-docdocsguidesecurityjceJCERefGuide.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解决方案:自己编写一个分数类

其他(感悟、思考等)

作业看似不多,但是通往成功的路上充满了坎坷。特别是报出几行错之后开始肉眼调试,我太难了。

参考资料

原文地址:https://www.cnblogs.com/leonzheng/p/11631725.html