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))之中。

其他(感悟、思考等)

这次是真的有亿点多,我并没有完全掌握本次实验内容,后续空余时间我将回顾本次代码。

参考资料

posted on 2020-10-28 22:23  王老师铁杆粉  阅读(286)  评论(0编辑  收藏  举报

导航