java复习第9天---9.2---网络编程---综合案例-模拟文件上传和模拟BS服务器
目录
内容
1、模拟文件上传
1.1、实现原理
文件上传流程:从客户端硬盘—>通过本地输入流—>客户端内存---->通过网络传输—>服务端内存---->通过本地输出流—>服务器硬盘
-
步骤:
- 客户端通过本地字节输入流,把文件读入内存
- 客户端通过获取的网络输出流,把文件内容写入网络输出流
- 服务器通过获取的网络输入流,读取客户端传输的文件内容
- 服务器通过本地字节码文件输出流,把文件数据存储在本地硬盘中
- 服务器使用网络输出流,给客户端回复“上传成功"
- 客户端使用网络输入流读取服务器回显的数据
- 关闭流,释放资源
-
本质:就是从数据源–>目的地的文件复制
1.2、客户端代码
-
代码1.2-1:
package net.tcp; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class UploadClient { public static void main(String[] args) throws IOException { // 1、创建客户端Socket对象,绑定服务器IP和端口号 Socket client = new Socket("127.0.0.1", 8888); // 2、通过Socket对象的getOutputStream方法获取网络输出流 OutputStream out = (OutputStream) client.getOutputStream(); // 3、通过网络输出流向服务器发送数据 // 3.1、从本地文件读取数据 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("f:\\test\\client\\a.txt")); int len = 0; byte[] b = new byte[1024]; // 4、通过Socket对象的getInputStream获取网络输入流 InputStream is = client.getInputStream(); while((len = bis.read(b)) != -1) { out.write(b, 0, len); } // 解决代码阻塞 client.shutdownOutput(); // 5、使用网络输入流读取服务器返回的数据 len = is.read(b); // 6、处理返回的数据 System.out.println(new String(b, 0, len)); // 7、关闭Socket对象,释放资源 client.close(); bis.close(); } } 测试结果: 上传成功
1.3、服务端代码
-
代码1.3-1:
package net.tcp; import java.io.BufferedOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class UploadServer { public static void main(String[] args) throws IOException { // 1. 创建ServerSocket服务端套接字,并通过构造方法绑定到指定的端口 ServerSocket server = new ServerSocket(8888); // 2. 调用ServerSocket 的accept方法等待客户端套接字的连接并获取套接字 Socket socket = server.accept(); // 3. 调用上一步的得到的Socket对象的getInputStream获取网络输入流 InputStream is = socket.getInputStream(); // 4. 调用网络输入流的read方法获取客户端传入的数据 byte[] b = new byte[1024]; int len = 0; BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("f:\\test\\server\\b.txt")); while((len = is.read(b)) != -1) { bos.write(b, 0, len); } // 5. 处理客户的传入的数据,生成响应数据 String resp = "上传完毕"; // 6. 调用Socket对象的getOutputStream获取网络输出流 OutputStream os = socket.getOutputStream(); // 7. 调用网络输出流的write方法,把相应数据返回给客户端 os.write(resp.getBytes()); // 8. 关闭Socket和ServerSocket对象,释放资源 socket.close(); server.close(); bos.close(); } } 测试结果:自己测试
1.4、代码阻塞
-
分析:
- read方法为阻塞方法
- 客户端读取本地文件结束,但是并不会把结束标识发送给服务器,导致服务器读取部分无限死循环,导致程序阻塞
-
解决:
- Socket的shutdownOutput关闭输出流
- 客户端传输完成后,调用,给服务器传输结束标识
1.5、代码优化
当前服务器只能服务一个客户端,我们想要服务多个客户端怎么办呢,服务器使用多线程来解决。
-
方法
- 接收客户端连接放入循环体,一直接收客户端请求
- 每建立一个连接,就开启一个线程,完成文件上传
- 一直重复
-
代码1.5-1:客户端同上,下面为服务器端代码
package net.tcp; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class UploadClient { public static void main(String[] args) throws IOException { // 1、创建客户端Socket对象,绑定服务器IP和端口号 Socket client = new Socket("127.0.0.1", 8891); // 2、通过Socket对象的getOutputStream方法获取网络输出流 OutputStream out = (OutputStream) client.getOutputStream(); // 3、通过网络输出流向服务器发送数据 // 3.1、从本地文件读取数据 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("I:\\test\\client\\a.txt")); int len = 0; byte[] b = new byte[1024]; // 4、通过Socket对象的getInputStream获取网络输入流 InputStream is = client.getInputStream(); while((len = bis.read(b)) != -1) { out.write(b, 0, len); } client.shutdownOutput(); // 5、使用网络输入流读取服务器返回的数据 len = is.read(b); // 6、处理返回的数据 System.out.println(new String(b, 0, len)); // 7、关闭Socket对象,释放资源 client.close(); bis.close(); } }
-
多线程优化:多线程重复创建和销毁,占用过多的系统资源,此处用线程池优化
-
代码1.5-2:客户端代码同上,下面五服务端代码
package net.tcp; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; public class MultiUploadServer { public static void main(String[] args) throws IOException { // 1. 创建ServerSocket服务端套接字,并通过构造方法绑定到指定的端口 ServerSocket server = new ServerSocket(8891); Random r = new Random(); while(true) { // 2. 调用ServerSocket 的accept方法等待客户端套接字的连接并获取套接字 Socket socket1 = server.accept(); new Thread(() -> { try { Socket socket = socket1; // 3. 调用上一步的得到的Socket对象的getInputStream获取网络输入流 InputStream is = socket.getInputStream(); // 4. 调用网络输入流的read方法获取客户端传入的数据 byte[] b = new byte[1024]; int len = 0; BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("I:\\test\\server\\b" + System.currentTimeMillis()+ r.nextInt(99999) + ".txt")); while((len = is.read(b)) != -1) { bos.write(b, 0, len); } // 5. 处理客户的传入的数据,生成响应数据 String resp = "上传完毕"; // 6. 调用Socket对象的getOutputStream获取网络输出流 OutputStream os = socket.getOutputStream(); // 7. 调用网络输出流的write方法,把相应数据返回给客户端 os.write(resp.getBytes()); // 8. 关闭Socket和ServerSocket对象,释放资源 socket.close(); bos.close(); }catch(IOException e) { e.printStackTrace(); } }).start(); } } }
2、模拟B/S服务器案例
2.1、B/S通信遵循HTTP协议
&emps;http协议分浏览器请求和服务器响应。请求部分由浏览器完成,我们暂时不关心,我们关心服务端响应格式
-
响应格式
HTTP/1.1 200 OK // 响应行 Content-Type:text/html // 响应头 <h4>Hello</h4> // 响应体
-
简单解析:详细自行查阅,格式固定
- 响应行:协议/版本 响应码数字 响应码文字
- 响应头:键值对形式 key: value1, value2 ,
- 响应体:为返回内容,一般为html 经过浏览器解析后为常见的页面
-
代码2.1-1:
package net.tcp; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; public class BServer { public static void main(String[] args) throws IOException { // 1. 创建ServerSocket服务端套接字,并通过构造方法绑定到指定的端口 ServerSocket server = new ServerSocket(8891); Random r = new Random(); while(true) { // 2. 调用ServerSocket 的accept方法等待客户端套接字的连接并获取套接字 Socket socket1 = server.accept(); new Thread(() -> { try { Socket socket = socket1; // 3. 调用上一步的得到的Socket对象的getInputStream获取网络输入流 InputStream is = socket.getInputStream(); // 4. 调用网络输入流的read方法获取客户端传入的数据 String len = null; BufferedReader br = new BufferedReader(new InputStreamReader(is)); len = br.readLine(); System.out.println(len); // 5. 处理客户的传入的数据,生成响应数据 String resp = "HTTP/1.1 200 OK\r\n"; resp += "Content-Type:text/html\r\n\r\n"; resp += "<h4>Hello</h4>"; // 6. 调用Socket对象的getOutputStream获取网络输出流 OutputStream os = socket.getOutputStream(); // 7. 调用网络输出流的write方法,把相应数据返回给客户端 os.write(resp.getBytes()); // 8. 关闭Socket和ServerSocket对象,释放资源 socket.close(); }catch(IOException e) { e.printStackTrace(); } }).start(); } } }
-
测试结果:
- 浏览器图示2.1-1:
后记 :
本项目为参考某马视频开发,相关视频及配套资料可自行度娘或者联系本人。上面为自己编写的开发文档,持续更新。欢迎交流,本人QQ:806797785
前端项目源代码地址:https://gitee.com/gaogzhen/vue-leyou
后端JAVA源代码地址:https://gitee.com/gaogzhen/JAVA