day13-网络编程
主要内容如下
- 网络编程
- TCP通信
- Junit单元测试
- 单例设计模式
- 多例设计模式
- 工厂设计模式
1 网络编程
1.1 软件架构
- C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件
- B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等
- 两种架构各有优势,但是都离不开网络的支持。网络编程 , 就是在一定的协议下,实现两台计算机的通信的程序
1.2 什么是网络编程
- 在网络通信协议下,不同计算机上运行的程序,可以进行数据传输
1.3 网络编程三要素
- IP地址 : 设备在网络中的地址,是唯一的标识。
- 端口 : 设备在网络中的地址,是唯一的标识。
- 数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。
1.4 IP地址
- IP:全称”互联网协议地址”,也称IP地址。是分配给上网设备的数字标签。常见的IP分类为:ipv4和ipv6
简单来说 : 就是设备在网络中的唯一标识 , 想要连接哪一台电脑 , 就找到此电脑在网络中的ip地址 - IP地址常见分类 : ipv4和ipv6
- 常用命令:
- ipconfig:查看本机IP地址
- IP地址:检查网络是否连通
- 特殊IP地址:
- 127.0.0.1:是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用
- 为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress 供我们使用
InetAddress:此类表示Internet协议(IP)地址
static InetAddress getByName(String host) | 在给定主机名的情况下确定主机的 IP 地址 |
String getHostName() | 获取此 IP 地址的主机名 |
String getHostAddress() | 返回 IP 地址字符串(以文本表现形式)。 |
1.5 端口
- 端口:应用程序在设备中唯一的标识。
- 端口号:应用程序的唯一标识方式 , 用两个字节表示的整数,它的取值范围是0~65535。
其中0~1023之间的端口号用于一些知名的网络服务或者应用。 - 我们自己使用1024以上的端口号就可以了。
- 注意:一个端口号只能被一个应用程序使用。
1.6 通信协议
- 协议:计算机网络中,连接和通信的规则被称为网络通信协议
- UDP协议
- 用户数据报协议(User Datagram Protocol)
- UDP是面向无连接通信协议。
- 速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据。
- TCP协议
- 传输控制协议 (Transmission Control Protocol)
- TCP协议是面向连接的通信协议。
- 速度慢,没有大小限制,数据安全
2 TCP通信
2.1 TCP发送数据
package com.itheima.tcp_demo.demo1; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; /* 客户端 : 发送数据的步骤 1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接 参数说明: host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的 port 表示服务器端的端口 2 通获Socket对象取网络中的输出流,写数据 OutputStream getOutputStream() 3 释放资源 void close() */ public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户端的Socket对象(Socket) 与指定服务端连接 Socket socket = new Socket("127.0.0.1", 10010); // 通获Socket对象取网络中的输出流,写数据 OutputStream os = socket.getOutputStream(); os.write("hello".getBytes()); // while(true){} // 释放资源 os.close(); socket.close(); } }
2.2 TCP接收数据
package com.itheima.tcp_demo.demo1; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; /* 服务端接收数据 : 1 创建服务器端的Socket对象 : ServerSocket类 ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号 2 监听客户端连接,并接受连接,返回一个Socket对象 Socket accept() : 该方法会一直阻塞直到建立连接 3 获取网络中的输入流,用来读取客户端发送过来的数据 InputStream getInputStream() 4 释放资源 : 服务端一般不会关闭 void close() */ public class ServerDemo { public static void main(String[] args) throws IOException { // 1 创建服务器端的Socket对象 : ServerSocket类 // ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号 ServerSocket serverSocket = new ServerSocket(10010); // 2 监听客户端连接,并接受连接,返回一个Socket对象 // Socket accept() : 该方法会一直阻塞直到建立连接 Socket socket = serverSocket.accept(); // // 3 获取网络中的输入流,用来读取客户端发送过来的数据 // InputStream getInputStream() InputStream is = socket.getInputStream(); int by; System.out.println("read方法执行前"); while ((by = is.read()) != -1) { System.out.print((char) by); } System.out.println("read方法执行后"); } }
2.3 TCP练习1
package com.itheima.tcp_demo.test1; import java.io.*; import java.net.Socket; /* 客户端 : 发送数据的步骤 1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接 参数说明: host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的 port 表示服务器端的端口 2 通获Socket对象取网络中的输出流,写数据 OutputStream getOutputStream() 3 释放资源 void close() */ public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户端的Socket对象(Socket) 与指定服务端连接 Socket socket = new Socket("127.0.0.1", 10010); // 通获Socket对象取网络中的输出流,写数据 OutputStream os = socket.getOutputStream(); os.write("hello".getBytes()); // 像服务端写入结束标记 socket.shutdownOutput(); BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line = br.readLine(); System.out.println(line); // 释放资源 br.close(); os.close(); socket.close(); } }
package com.itheima.tcp_demo.test1; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /* 服务端接收数据 : 1 创建服务器端的Socket对象 : ServerSocket类 ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号 2 监听客户端连接,并接受连接,返回一个Socket对象 Socket accept() : 该方法会一直阻塞直到建立连接 3 获取网络中的输入流,用来读取客户端发送过来的数据 InputStream getInputStream() 4 释放资源 : 服务端一般不会关闭 void close() */ public class ServerDemo { public static void main(String[] args) throws IOException { // 1 创建服务器端的Socket对象 : ServerSocket类 // ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号 ServerSocket serverSocket = new ServerSocket(10010); // 2 监听客户端连接,并接受连接,返回一个Socket对象 // Socket accept() : 该方法会一直阻塞直到建立连接 Socket socket = serverSocket.accept(); // // 3 获取网络中的输入流,用来读取客户端发送过来的数据 // InputStream getInputStream() InputStream is = socket.getInputStream(); int by; while ((by = is.read()) != -1) { System.out.print((char) by); } BufferedWriter bos = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bos.write("你谁啊"); bos.close(); is.close(); socket.close(); serverSocket.close(); } }
2.4 TCP练习2
package com.itheima.tcp_demo.test2; import java.io.*; import java.net.Socket; public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户端Socket对象 Socket socket = new Socket("127.0.0.1", 10086); BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg")); BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); int by; while ((by = bis.read()) != -1) {// 从本地中读一个字节 bos.write(by);// 往服务器写一个字节 bos.flush(); } // 写结束标记 socket.shutdownOutput(); // 把网络中的字节输入流 , 封装成高效的字符输入流 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // String line; // while ((line = br.readLine()) != null) { // System.out.println(line); // } String msg = br.readLine();// 读到换行才叫读到一行, 所以必须写服务器必须写newLine System.out.println(msg); // 释放资源 bis.close(); socket.close(); } }
package com.itheima.tcp_demo.test2; import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class ServerDemo { public static void main(String[] args) throws IOException { // 创建服务端的连接对象 ServerSocket serverSocket = new ServerSocket(10086); Socket socket = null; BufferedInputStream bis = null; BufferedWriter socketBw = null; while (true) { // 获取Socket对象 socket = serverSocket.accept(); // 获取网络中的字节输入流 在封装成高效的字节输入流对象 bis = new BufferedInputStream(socket.getInputStream()); // 创建本地的字节输出流 , 封装成高效的字节输出流 BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("day13_demo\\图片\\a.jpg")); int by; while ((by = bis.read()) != -1) { bw.write(by); bw.flush(); } //关闭本地流 bw.close(); // 获取网络中的字节输出流 , 封装成高效的字符输出流 socketBw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); socketBw.write("谢谢你"); socketBw.newLine();// 必须有换行 , 因为readLine读到换行结束 socketBw.flush(); } // 释放资源 // socketBw.close(); // bis.close(); // socket.close(); // serverSocket.close(); } }
2.5 TCP练习3
package com.itheima.tcp_demo.test3; import java.io.*; import java.net.Socket; public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户端Socket对象 Socket socket = new Socket("127.0.0.1", 10086); BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg")); BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); int by; while ((by = bis.read()) != -1) {// 从本地中读一个字节 bos.write(by);// 往服务器写一个字节 bos.flush(); } // 写结束标记 socket.shutdownOutput(); // 把网络中的字节输入流 , 封装成高效的字符输入流 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // String line; // while ((line = br.readLine()) != null) { // System.out.println(line); // } String msg = br.readLine();// 读到换行才叫读到一行, 所以必须写服务器必须写newLine System.out.println(msg); // 释放资源 bis.close(); socket.close(); } }
package com.itheima.tcp_demo.test3; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ServerDemo { public static void main(String[] args) throws IOException { // 创建服务端的连接对象 ServerSocket serverSocket = new ServerSocket(10086); ExecutorService executorService = Executors.newFixedThreadPool(10); while (true) { // 获取Socket对象 Socket socket = serverSocket.accept(); executorService.submit(new ServerThread(socket)); } // 释放资源 // socketBw.close(); // bis.close(); // socket.close(); // serverSocket.close(); } }
package com.itheima.tcp_demo.test3; import javax.management.relation.RoleUnresolved; import java.io.*; import java.net.Socket; import java.util.UUID; public class ServerThread implements Runnable { Socket socket = null; BufferedOutputStream bw = null; public ServerThread(Socket socket) { this.socket = socket; } @Override public void run() { try { // 获取网络中的字节输入流 在封装成高效的字节输入流对象 BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); // 创建本地的字节输出流 , 封装成高效的字节输出流 bw = new BufferedOutputStream(new FileOutputStream("day13_demo\\图片\\" + UUID.randomUUID() + ".jpg")); int by; while ((by = bis.read()) != -1) { bw.write(by); bw.flush(); } // 获取网络中的字节输出流 , 封装成高效的字符输出流 BufferedWriter socketBw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); socketBw.write("谢谢你"); socketBw.newLine();// 必须有换行 , 因为readLine读到换行结束 socketBw.flush(); } catch (IOException e) { e.printStackTrace(); } finally { //关闭本地流 try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
3 Junit单元测试
3.1 Junit4单元测试概述
- 单元测试就是编写测试代码,可以准确、快速地保证程序的正确性,Junit是Java单元测试的框架。
- JUnit4可以通过注解的方式来标记方法 , 让方法存某种意义 ,常见的注解有:
- @BeforeClass 全局只会执行一次,而且是第一个运行(标记的方法需要是一个静态无参无返回值方法)
- @Before 在测试方法运行之前运行(非静态无参无返回值方法)
- **@Test 测试方法(此方法必须是非静态无参无返回值方法), 主要用于测试的方法 **
- @After 在测试方法运行之后运行(非静态无参无返回值方法)
- @AfterClass 全局只会执行一次,而且是最后一个运行(标记的方法需要是一个静态无参无返回值方法)
- @Ignore 忽略此方法
3.2 Junit的基本使用
- 已知存在需要测试的类Calculator ,这是一个能够简单实现加减乘除、平方、开方的计算器类,然后对这些功能进行单元测试。
// 计算器类 public class Calculator { // 静态变量,用于存储运行结果 private static int result; // 20 // 加法运算 public void add(int n) { result = result + n; } // 减法运算 public void subtract(int n) { // Bug: 正确的应该是 result = result - n result = result - 1; } // 乘法运算 public void multiply(int n) { // 此方法尚未写好 } // 除法运算 public void divide(int n) { result = result / n; } // 平方运算 public void square(int n) { result = n * n; } // 平方根运算 public void squareRoot(int n) { // Bug : 死循环 for (; ;) ; } // 将结果清零 public void clear() { result = 0; } // 返回运算结果 public int getResult(){ return result; } }
引入Junit4的jar包到模块中
第一步 : 在模块中新建文件夹lib,拷贝今天资料中junit4的jar包到模块中
- 第二步 : 选中jar文件 , 右键选择 Add as Library
- 生成Junit测试框架
- 使用IDEA能够直接给需要测试的类生成测试框架,如下:
- 选中本类任何位置右键选择 Generate(Alt+Insert)/ go to 选项 , 选择Test…
- 选择需要进行测试的方法
- 在上一步OK后系统会自动生成一个新类CalculatorTest,里面包含一些空的测试用例。
限时测试
对于那些逻辑很复杂,循环嵌套比较深的程序,很有可能出现死循环,因此一定要采取一些预防措施。限时测试是一个很好的解决方案。我们给这些测试函数设定一个执行时间,超过了这个时间,他们就会被系统强行终止,并且系统还会向你汇报该函数结束的原因是因为超时,这样你就可以发现这些Bug了。要实现这一功能,只需要给@Test标注加一个参数即可,代码如下:
被测方法:
public void squareRoot(int n) { //Bug : 死循环 for (; ; ) ; }
测试方法:
@Test(timeout = 1000) // Timeout参数表明了你要设定的时间,单位为毫秒,因此1000就代表1秒。 public void squareRoot() { calculator.squareRoot(4); assertEquals(2 , calculator.getResult()); }