实验四

学号 2019-2020-182321 《数据结构与面向对象程序设计》实验四报告

课程:《程序设计与数据结构》

班级: 1823

姓名: 杨凯涵

学号:20182321

实验教师:王志强

实验日期:2019年10月7日

必修/选修: 必修

1.实验内容

  • 实验一: Java Socket编程
    1.学习蓝墨云上教材《Java和Android编程》“第16章 输入/输出 ”和“第22章 网络”,学习JavaSocket编程
    2.结对编程。结对伙伴A编写客户端SocketClient.java,结对伙伴B编写服务器端。
    3.截图加学号水印上传蓝墨云,代码push到码云,并撰写实验报告。
  • 实验二:Java和密码学,参考http://www.cnblogs.com/rocedu/p/6683948.html,以结对的方式完成Java密码学相关内容的学习(帖子中所有代码和相关知识点需要学习)。
  • 实验三:编写有理数/复数计算器,结对编程,结对伙伴A编写有理数计算器。结对伙伴B编写复数计算器
  • 实验四:远程有理数计算器,结对编程,结对伙伴A编程实现客户端,结果伙伴B实现服务器端。
    客户端通过键盘输入一个有理数计算的公式(例如:1/4 + 1/6 = ),并把该公式以字符串的形式发送给伙伴B(服务器端),服务器端根据字符串计算出结果为5/12,并把结果返回给客户端A,A收到结果后输出结果。截图加水印上传蓝墨云,代码push码云。
  • 实验五:远程有理数复数计算器,结对编程,结对伙伴B编程实现客户端,结果伙伴A实现服务器端。
    客户端通过键盘输入一个有理数计算的公式(例如:1/4 + 1/6 = ),并把该公式以字符串的形式发送给伙伴A(服务器端),服务器端根据字符串计算出结果为5/12,并把结果返回给客户端B,B收到结果后输出结果。截图加水印上传蓝墨云,代码push码云

2实验过程及结果

实验一

学习javasocket编程,并与结对小伙伴一个实现服务端,一个实现客户端。


通过以上的学习,我们知道,socket无外乎就是一个借口,SSocket就像是发动机,提供了网络通信的能力。由此,我们利用老师给我们的代码
服务器:

package HttpSocket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by besti on 2019/9/29.
 */
public class Server1823 {
    public static void main(String[] args) throws IOException {
        //1.建立一个服务器Socket(ServerSocket)绑定指定端口
        ServerSocket serverSocket=new ServerSocket(8800);
        //2.使用accept()方法阻止等待监听,获得新连接
        Socket socket=serverSocket.accept();
        //3.获得输入流
        InputStream inputStream=socket.getInputStream();
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
        //获得输出流
        OutputStream outputStream=socket.getOutputStream();
        PrintWriter printWriter=new PrintWriter(outputStream);
        //4.读取用户输入信息
        String info=null;
        System.out.println("服务器已经建立......");
        while(!((info = bufferedReader.readLine()) ==null)){
            System.out.println("我是服务器,用户信息为:" + info);
        }
        //给客户一个响应
        String reply="welcome";
        printWriter.write(reply);
        printWriter.flush();
        //5.关闭资源
        printWriter.close();
        outputStream.close();
        bufferedReader.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

客户端:

package HttpSocket;

import com.sun.corba.se.impl.orbutil.ObjectUtility;

import java.io.*;
import java.net.Socket;

/**
 * Created by besti on 2018/6/9.
 */
public class SocketClient {
    public static void main(String[] args) throws IOException {
        //1.建立客户端Socket连接,指定服务器位置和端口
        Socket socket = new Socket("localhost",8800);
//        Socket socket = new Socket("172.16.43.187",8800);

        //2.得到socket读写流
        OutputStream outputStream = socket.getOutputStream();
 //       PrintWriter printWriter = new PrintWriter(outputStream);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        //输入流
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        //3.利用流按照一定的操作,对socket进行读写操作
        String info1 = " 用户名:Tom,密码:123456";
//        String info = new String(info1.getBytes("GBK"),"utf-8");
   //     printWriter.write(info);
   //     printWriter.flush();
        outputStreamWriter.write(info1);
        outputStreamWriter.flush();
        socket.shutdownOutput();
        //接收服务器的响应
        String reply = null;
        while (!((reply = bufferedReader.readLine()) ==null)){
            System.out.println("接收服务器的信息为:" + reply);
        }
        //4.关闭资源
        bufferedReader.close();
        inputStream.close();
        outputStreamWriter.close();
        //printWriter.close();
        outputStream.close();
        socket.close();
    }
}

在读取用户信息之前,我们是要建立一个接口,绑定一个端口,并且创建输出流和输入流的对象,这样两个程序之间的连接就算是做好了,接着作为服务器,就要从客户端出接受信息,这里用bufferedReader.readLine()来进行获取,而客户端是用outputStreamWriter.write来进行输出(这两个输入输出的对象在此之前已经建立),往下走,服务器再返回客户端一个信息(使用printWriter.write()),而客户端接受信息后就显示出来,最后再关闭我们的客户端和服务器。(运行结果中的乱码只需要把UTF-8挑为GBK就好了)。

实验二

Java和密码学:
根据楼老师的博客操作:

  1. 凯撒密码:先约定好将明文往后移动多少个字母,然后对明文进行加密。如:YKH加密后为AOJ。我们的代码如下
public static void main(String args[]) throws Exception{
String s=args[0];
        int key=Integer.parseInt(args[1]);
        String es="";
        for(int i=0;i<s.length( );i++)
{  char c=s.charAt(i);
               if(c>='a' && c<='z') // 是小写字母
                  { c+=key%26;  //移动key%26位
                    if(c<'a') c+=26;  //向左超界
                    if(c>'z') c-=26;  //向右超界
                  }
               else if(c>='A' && c<='Z') // 是大写字母
{  c+=key%26;
                    if(c<'A') c+=26;
                    if(c>'Z') c-=26;
                  }
               es+=c;
           }
       System.out.println(es);
     }

运行截图:

在这个地方输入我们要加密的内容,然后往后移动四位的意思。

  1. Java对称加密-DES算法
    运行截图:
    运行DES程序,得到密钥key1.dat

保存密钥编码格式,在程序中输入java Skey_kb,在程序的当前目录中将产生文件名为keykb1.dat的文件,运行结果如下:

处理加密结果:

最终进行解密:

  1. Java非对称加密-RSA算法:
    在凯撒密码里提到的老地方输入java Skey_RSA,运行RSA从而生成公钥和私钥。

继续输入java Skey_RSA,运行Enc RsA程序,得到如下结果:![]

这都显示了公钥中的参数以及加密的结果c。

接着运行程序输入java Dec_RSA运行DecRsa程序,得到如下结果:
其中显示了私钥中的参数以及解密的结果,其中整型的明文转换后显示出字符串“Hello World!”。

  1. 使用密钥协定创建共享密钥
    建立A、B两个文件夹,然后分别在里面输入“java Key_DH Apub.dat Apri.dat”和“java Key_DH Bpub.dat Bpri.dat”运行,运行结果如图所示:

将程序KeyAgree编译后分别拷贝在A和B两个目录,首先在A目录输入“java KeyAgree Bpub.dat Apri.dat”运行程序,它使用文件Bpub.dat中对方的公钥和文件Apri.dat中自己的私钥创建了一段共享的字节数组。

实验三

根据要求,我编写了一个计算器如图:

实验四

编写远程有理数计算器,我们编写了两个类,一个rational类,一个script类,rational用来进行有理数计算,而script用来讲客户输入的字符串转换成double输入到rational中。以下为代码

rational.java
package com.company;

public class Rational {
    private int numerator ,denominator;
    public Rational(int numer,int denom)
    {
        if(denom == 0)
            denom = 1;
        if(denom < 0)
        {
            numer = numer * -1;
            denom = denom * -1;
        }
        numerator = numer;
        denominator = denom;
        reduce();
    }


public int getNumerator()
{
    return numerator;
}
public int getDemoninator()
{
    return denominator;
}
public Rational reciprocal()
{
    return new Rational(denominator,numerator);
}
public  Rational add (Rational op2)
{
    int commonDenominator = denominator*op2.getDemoninator();
    int numerator1 = numerator*op2.getDemoninator();
    int numerator2 = op2.getNumerator()*denominator;
    int sum = numerator1 + numerator2;
    return new Rational(sum,commonDenominator);
}
public Rational subtract (Rational op2)
{
    int commonDenominator = denominator*op2.getDemoninator();
    int numerator1 = numerator*op2.getDemoninator();
    int numerator2 = op2.getNumerator()*denominator;
    int difference = numerator1 - numerator2;
    return  new Rational(difference,commonDenominator);
}
public Rational multipy(Rational op2)
{
    int numer = numerator* op2.getNumerator();
    int denom = denominator*op2.getDemoninator();
    return new Rational(numer,denom);
}
public Rational divide (Rational op2)
{
    return multipy(op2.reciprocal());
}
public String toString()
{
    String result;
    if (numerator == 0)
    {
        result = "0";
    }
    else
        if(denominator == 1)
        {
            result = numerator + "";
        }
        else
            result = numerator +"/"+denominator;
        return result;
}
private void reduce()
{
    if (numerator != 0)
    {
        int common =gcd (Math.abs(numerator),denominator);
        numerator = numerator/common;
        denominator = denominator / common;
    }
}
private int gcd (int num1, int num2)
{
    while (num1 != num2)
        if(num1>num2)
            num1 = num1 - num2;
        else
            num2= num2-num1;
        return  num1;
}
}
package com.company;
public class script
{
    private String s ;
    public script(String s)
    {
        this.s=s;
    }
public  String getresult()
{
    String[] a1 = s.split("\*|\+|-|/");

    String reply = "";
    int a, b, c, d;
    a = Integer.parseInt(a1[0]);
    b = Integer.parseInt(a1[1]);
    c = Integer.parseInt(a1[2]);
    d = Integer.parseInt(a1[3]);
    Rational rational = new Rational(a,b );
    Rational rational1 = new Rational(c,d);
    int i = 0;
    int n = 0;
    char[] e = s.toCharArray();
    for (i = 0; i < e.length; i++) {
        if (e[i] == '+') {
            reply = rational.add(rational1).toString();
        } else if (e[i] == '-') {
            reply = rational.subtract(rational1).toString();
        } else if (e[i] == '*') {
            reply = rational.multipy(rational1).toString();
        } else if (e[i]=='/')
        {
            n++;
        }
        else if (e[i]=='/'||n==3)
        {
            reply = rational.divide(rational1).toString();
        }
    }
    return  reply;
}
}
服务器
package com.company;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
 * Created by besti on 2019/9/29.
 */
public class Server1823 {
    public static void main(String[] args) throws IOException, ScriptException {
        //1.建立一个服务器Socket(ServerSocket)绑定指定端口
        ServerSocket serverSocket=new ServerSocket(8800);
        //2.使用accept()方法阻止等待监听,获得新连接
        Socket socket=serverSocket.accept();
        //3.获得输入流
        InputStream inputStream=socket.getInputStream();
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
        //获得输出流
        OutputStream outputStream=socket.getOutputStream();
        PrintWriter printWriter=new PrintWriter(outputStream);
        //4.读取用户输入信息
        String info=null;
        System.out.println("服务器已经建立......");
        info = bufferedReader.readLine();
        System.out.println("我是服务器,用户信息为:" + info);
        String reply = "";
        script s = new script(info);
        reply = s.getresult();
        printWriter.write(reply);
        printWriter.flush();

        //5.关闭资源
        printWriter.close();
        outputStream.close();
        bufferedReader.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}
客户端
package com.company;
import java.io.*;
import java.net.Socket;

/**
 * Created by besti on 2018/6/9.
 */
public class SocketClient {
    public static void main(String[] args) throws IOException {
        //1.建立客户端Socket连接,指定服务器位置和端口
        Socket socket = new Socket("192.168.43.229",8800);
//        Socket socket = new Socket("172.16.43.187",8800);

        //2.得到socket读写流
        OutputStream outputStream = socket.getOutputStream();
        //       PrintWriter printWriter = new PrintWriter(outputStream);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        //输入流
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        //3.利用流按照一定的操作,对socket进行读写操作
        String info1 = "1/4+1/5";
//        String info = new String(info1.getBytes("GBK"),"utf-8");
        //     printWriter.write(info);
        //     printWriter.flush();
        outputStreamWriter.write(info1);
        outputStreamWriter.flush();
        socket.shutdownOutput();
        //接收服务器的响应
        String reply = null;
        reply = bufferedReader.readLine();
        System.out.println("接收服务器的信息为:" + reply);

        //4.关闭资源
        bufferedReader.close();
        inputStream.close();
        outputStreamWriter.close();
        //printWriter.close();
        outputStream.close();
        socket.close();
    }
}

运行结果如图所示:

实验五

同实验四相同,我们编写了一个Complex类,一个hello类,前者用来计算复数,后者用来实现将用户的字符串转换成数字输入到complex中。两次实验我们都使用了slipt来对字符串进行分割。

复数服务器
package com.company;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
 * Created by besti on 2019/9/29.
 */
public class ServerComplex {
    public static void main(String[] args) throws IOException, ScriptException {
        //1.建立一个服务器Socket(ServerSocket)绑定指定端口
        ServerSocket serverSocket=new ServerSocket(8800);
        //2.使用accept()方法阻止等待监听,获得新连接
        Socket socket=serverSocket.accept();
        //3.获得输入流
        InputStream inputStream=socket.getInputStream();
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
        //获得输出流
        OutputStream outputStream=socket.getOutputStream();
        PrintWriter printWriter=new PrintWriter(outputStream);
        //4.读取用户输入信息
        String info1=null;
        System.out.println("服务器已经建立......");
        info1 = bufferedReader.readLine();
        System.out.println("第一个复数为" + info1);
        String[] reply = info1.split("i");
        hello hi = new hello(reply[0]);
        hello hii = new hello(reply[1]);
        double a,b,c,d;
        a = hi.getrealpart();
        b = hi.getimagepart();
        c = hii.getrealpart();
        d = hii.getimagepart();
        char g = reply[2].charAt(0);
        Complex c1 =new Complex(a,b);
        Complex c2 =new Complex(c,d);
        String e="";
        if (g=='+')
        {
            e = c1.ComplexAdd(c2).toString();
        }
        else if(g=='-')
        {
            e = c1.ComplexSub(c2).toString();
        }
        else if(g=='*')
        {
            e = c1.ComplexMulti(c2).toString();
        }
        else if(g=='/')
        {
            e = c1.ComplexDiv(c2).toString();
        }
        printWriter.write(e);
        printWriter.flush();

        //5.关闭资源
        printWriter.close();
        outputStream.close();
        bufferedReader.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}
复数客户端
package com.company;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
 * Created by besti on 2018/6/9.
 */
public class SocketClient1823 {
    public static void main(String[] args) throws IOException {
        //1.建立客户端Socket连接,指定服务器位置和端口
        Socket socket = new Socket("192.168.43.174",8800);
//        Socket socket = new Socket("172.16.43.187",8800);

        //2.得到socket读写流
        OutputStream outputStream = socket.getOutputStream();
        //       PrintWriter printWriter = new PrintWriter(outputStream);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        //输入流
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        //3.利用流按照一定的操作,对socket进行读写操作
        String info1 = "";
        Scanner scan = new Scanner(System.in);
        info1 = scan.nextLine();
        String info2 = "";
        info2 =scan.nextLine();
        String s = scan.next();
//        String info = new String(info1.getBytes("GBK"),"utf-8");
        //     printWriter.write(info);
        //     printWriter.flush();
        outputStreamWriter.write(info1);
        outputStreamWriter.flush();
        outputStreamWriter.write(info2);
        outputStreamWriter.flush();
        outputStreamWriter.write(s);
        outputStreamWriter.flush();
        socket.shutdownOutput();
        //接收服务器的响应
        String reply = null;
        reply = bufferedReader.readLine();
        System.out.println("接收服务器的信息为:" + reply);
        //4.关闭资源
        bufferedReader.close();
        inputStream.close();
        outputStreamWriter.close();
        //printWriter.close();
        outputStream.close();
        socket.close();
    }
}
package com.company;

import static java.lang.Character.*;

public class hello
{

    private int i=0;
    private double n=0;
    private int[] a = new int[2];
    private String x;
    public hello(String s)
    {
        x = s;
    }

       public double getrealpart()
       {
           String[] reply = x.split("\+|-|i");
           n = Integer.parseInt(reply[0]);
           return n;
       }
       public double getimagepart()
       {
           String[] reply1 = x.split("\+|-|i");
           n = Integer.parseInt(reply1[1]);
                   return n;
       }

}
package com.company;

public class Complex{
    private double r;
    private double i;

    public Complex(double R, double I) {
        r = R;
        i = I;
    }

    public static double getRealPart(double r) {
        return r;
    }

    public static double getImagePart(double i) {
        return i;
    }

    public Complex ComplexAdd(Complex c) {
        return new Complex(r + c.r, i + c.i);
    }
    public Complex ComplexSub(Complex c) {
        return new Complex(r - c.r, i - c.i);
    }
    public Complex ComplexMulti(Complex c) {
        return new Complex(r * c.r - i * c.i, r * c.i + i * c.r);
    }
    public Complex ComplexDiv(Complex c) {
        return new Complex((r * c.i + i * c.r)/(c.i * c.i + c.r * c.r), (i * c.i + r * c.r)/(c.i * c.i + c.r * c.r));
    }

    public String toString() {
        String s = " ";
        if (i > 0)
            s =  r + "+" + i + "i";
        if (i == 0)
            s =  r + "";
        if (i < 0)
            s = r + " " + i + "i";
        return s;
    }
}

运行结果如图所示:

3. 实验过程中遇到的问题和解决过程

  • 问题1:出现connect reset问题。

  • 问题1解决方法:一开始连接不上的时候我们查阅了百度,可是发现始终没有有效的解决方法,于是我决定把编写的类给去掉,检查连接是否有问题。

图片显示在没有类的情况下(里面的类是后面加上去的),并没有出现connect reset的问题,所以应该是类出现了问题。
那么我编写了一个主程序aswd来运行试试看类,(因时间仓促没有截图)在运行过程中可以发现类里面的问题,并及时纠正,这样有助于快速找出程序问题的原因。

(如图,我利用这个程序把自己写过的四个类都测试过,以此来找出程序中存在的问题)。

  • 问题2:split的使用问题
  • 问题2解决方法;split(“”)是将一个字符串一“”里的字符为标准,分成多个部分,如
String s = "jdwlkaj-dwjakjwd";
String[] a = s.split("-"); 

那么,a[0]="jdwlkaj",a[1]="dwjakjwd",但是当我们在“”里放入+号时,系统却显示错误?原来+ * 不是有效的模式匹配规则表达式,用"" "+"转义后即可得到正确的结果。只需要改为split(“|+”)程序就可以运行了。

其他(感悟、思考等)

  • 本次实验看起来对我们难度非常的大,其实内在是很简单明了的,有理数类的加减乘除和复数类的加减乘除的代码书上其实都有,而我们只需要理解清楚javasocket编程代码上的含义还有明白如何运用类将字符串转换成数字就好了,造成这次实验完成的不好在于对于书本的不重视。
  • 当程序运行出错的时候,其实可以将idea下方报错的名字信息复制粘贴到百度上来查询是为什么错了,这有利于我们自己发现问题,而不是有一点的问题都去询问助教。

参考资料

《Java程序设计与数据结构教程(第二版)》

《Java程序设计与数据结构教程(第二版)》学习指导

原文地址:https://www.cnblogs.com/yangkaihan/p/11631458.html