网络编程 - 单元测试
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.可以实现自动化测试