网络编程 - 单元测试

109 阅读6分钟

网络编程 - 单元测试

1 网络通信

1.1 什么是网络通信

​ 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)

1.2 基本架构

​ /\ CS架构( Client客户端/Server服务端 )

​ /\ BS架构(Browser浏览器/Server服务端)

1.3 三要素

​ IP、端口、协议。

1.3.1 IP

​ 网络设备的唯一标识。 【电脑、手机...】,有IPv4 (4个字节表示一个地址(共32位)), IPv6(16字节表示一个地址(128位))

1.3.2 端口

​ 端口是程序的唯一标识, 用来标记正在计算机设备上运行的应用程序的

​ 不可以出现2个应用程序的端口号一样, 如果一样会出现端口冲突error。

​ 前1024个端口号默认分配为固定程序, 不能被调用

1.3.3 协议

​ 1) 定义: 计算机网络中,连接和通信数据的规则被称为网络通信协议

​ 2) 通信协议: UDP(User Datagram Protocol):用户数据报协议; TCP(Transmission Control Protocol) :传输控制协议

​ 3) UDP: 无连接、不可靠通信 通信效率高 (视频直播)

​ 4) TCP: 面向连接、可靠通信 通信效率不高 (网页, 文件下载, 支付) 三次握手建立连接, 四次握手断开连接

2 UDP通信

2.1 创建客户端、服务端对象
	创建发送端的Socket对象: public DatagramSocket()
	创建接收端的Socket对象: public DatagramSocket(int port) 
	(接收端一定要指定端口, 否则=>找不到接收对象)
2.2 数据包对象及常用方法

​ DatagramPacket

	创建发出去的数据包对象: public DatagramPacket(byte[] buf, int length, InetAddress address, int port)
	创建用来接收数据的数据包: public DatagramPacket(byte[] buf, int length)
2.3 发送、接收数据
使用DatagramSocket的如下方法:
发送数据包: public void send(DatagramPacket dp)
接收数据包:public void receive(DatagramPacket dp)  
2.4 InetAddress及常用方法

​ InetAddress代表IP地址

	- 获取本机IP,会以一个inetAddress的对象返回: public static InetAddress getLocalHost()
	- 根据ip地址或者域名,返回一个inetAdress对象: public static InetAddress getByName(String host)
	- 获取该ip地址对象对应的主机名: public String getHostName()
	- 获取该ip地址对象中的ip地址信息: public String getHostAddress()
	- 在指定毫秒内,判断主机与该ip对应的主机是否能连通: public boolean isReachable(int timeout)
2.5 代码实现

​ 发送端循环发送数据

public class Sender {
    public static void main(String[] args) throws Exception {
        // sender不一定需要端口
        DatagramSocket ds = new DatagramSocket(9133);

        // 创建一个array用于发送,并赋值hello
        byte[] bytes = "hello".getBytes();
        // 创建data包用于存放准备发送的bytes array, 设置发送的对象ip端口(快递写地址,收件人姓名)
        DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length, InetAddress.getByName("127.0.0.1"),9132);
        // 发送
        ds.send(dp);
        System.out.println("已发送");
        // 关流
        ds.close();
    }
}

​ 接收端循环接收数据

public class Receiver {
    public static void main(String[] args) throws Exception {

        while (true) {
            // 创建端口9132, 必须=>否则包裹收不到
            DatagramSocket ds = new DatagramSocket(9132);
            // 创建一个array准备接受
            byte[] bytes = new byte[1024*8];
            // 创建一个包裹准备接收
            DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length);
            // 接收
            ds.receive(dp);
            System.out.println("接受到:" + new String(bytes,0,dp.getLength()));
            ds.close();
        }
    }
}

3 TCP通信

3.1 客户端实现
3.1.1 客户端的代表类

​ /\ Socket类 ​ /\ public Socket(String host , int port)

3.1.2 构造器
根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket
public Socket(String host , int port)
3.1.3 常用方法
	获得字节输出流对象: public OutputStream getOutputStream()
	获得字节输入流对象: public InputStream getInputStream()
3.1.4 实现步骤

​ 1) 创建客户端的Socket对象,请求与服务端的连接。 ​ 2) 使用socket对象调用getOutputStream()方法得到字节输出流。 ​ 3) 使用字节输出流完成数据的发送。 ​ 4) 释放资源:关闭socket管道。

3.2 服务端实现
3.1.1 客户端的代表类

​ /\ ServerSocket类 ​ /\ 注册端口

3.1.2 构造器
为服务端程序注册端口
public ServerSocket(int port)
3.1.3 常用方法
	阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象: public Socket accept()
3.1.4 实现步骤

​ 1) 创建ServerSocket对象,注册服务端端口。 ​ 2) 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。 ​ 3) 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。 ​ 4) 释放资源:关闭socket管道

3.1.5 多发多收

​ /\ 客户端使用死循环反复地发送消息。 ​ /\ 服务端使用死循环反复地接收消息。

3.3 TCP支持与多个客户端同时通信
3.3.1 实现原理

​ /\ 主线程定义了循环负责接收客户端Socket管道连接 ​ /\ 每接收到一个Socket通信管道后分配一个独立的线程负责处理它。

3.4 代码实现 (多发多收+多线程+读写单独分配线程(即实现同时发同时收))

服务器端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class Server {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(9090);
        System.out.println("服务器已启动,等待客户端连接...");

        while (true) {
            Socket socket = serverSocket.accept();
            System.out.println("客户端已连接,启动新线程处理请求。");
            //     接收
            Thread thread = new Thread(() -> {
                try {
                    receiveMtd(socket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            //     发送
            Thread thread1 = new Thread(() -> {
                try {
                    sendMtd(socket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
          thread.start();
          thread1.start();
        }
    }
    
    public static void sendMtd(Socket socket) throws IOException {
        try (OutputStream os = socket.getOutputStream();) {
            while (true) {
                byte[] bytes1 = inputLine().getBytes();
                os.write(bytes1, 0, bytes1.length);
                System.out.println("发送完毕");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            socket.close();
        }
    }
    
    public static void receiveMtd(Socket socket) throws IOException {
        try (InputStream is = socket.getInputStream();) {
            byte[] bytes = new byte[1024 * 8];
            int bytesRead;
            while ((bytesRead = is.read(bytes)) != -1) {
                String request = new String(bytes, 0, bytesRead);
                System.out.println("来自Client: " + request);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            socket.close();
        }
    }
    
    public static String inputLine() {
        Scanner sc = new Scanner(System.in);
        return sc.nextLine();
    }
}

客户端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws Exception {
        Socket socket = null;
        OutputStream os = null;
        InputStream is = null;
        socket = new Socket(InetAddress.getByName("localhost"), 9090);

        while (true) {
            //     发送
            byte[] bytes = "你好啊, 我是cllient".getBytes();
            os = socket.getOutputStream();
            os.write(bytes, 0, bytes.length);
            System.out.println("发送完毕!");

            //     接收
            is = socket.getInputStream();
            byte[] bytes1 = new byte[1024 * 8];
            int read = is.read(bytes1);
            System.out.println("从Server接收到了: " + new String(bytes1, 0, read));
        }
    }
}

4 单元测试

​ 就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试

4.1 JUnit单元测试的实现过程

​ 1) 必须导入Junit框架的jar包。 ​ 2) 定义的测试方法必须是无参数无返回值,且公开的方法。 ​ 3) 测试方法使用@Test注解标记。

​ 4) 测试某个方法直接右键该方法启动测试。 ​ 5) 测试全部方法,可以选择类或者模块启动。 ​ 6) 红色失败,绿色通过。

4.2 常用方法
测试方法: @Test 
用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次: @BeforeEach
用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次: @AfterEach
用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次: @BeforeAll
用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次: @AfterAll
断言的类
Assertions

/\ :开始执行的方法:初始化资源。 /: 执行完之后的方法:释放资源。

4.3 优点

1.可以实现自动化测试

2.可以实现自动化测试

3.可以实现自动化测试