java复习第9天---9.3---网络编程---综合案例-模拟文件上传和模拟BS服务器

java复习第9天---9.2---网络编程---综合案例-模拟文件上传和模拟BS服务器

目录




内容

1、模拟文件上传

1.1、实现原理

  文件上传流程:从客户端硬盘—>通过本地输入流—>客户端内存---->通过网络传输—>服务端内存---->通过本地输出流—>服务器硬盘

  • 步骤:

    1. 客户端通过本地字节输入流,把文件读入内存
    2. 客户端通过获取的网络输出流,把文件内容写入网络输出流
    3. 服务器通过获取的网络输入流,读取客户端传输的文件内容
    4. 服务器通过本地字节码文件输出流,把文件数据存储在本地硬盘中
    5. 服务器使用网络输出流,给客户端回复“上传成功"
    6. 客户端使用网络输入流读取服务器回显的数据
    7. 关闭流,释放资源
  • 本质:就是从数据源–>目的地的文件复制

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、代码阻塞

  • 分析:

    1. read方法为阻塞方法
    2. 客户端读取本地文件结束,但是并不会把结束标识发送给服务器,导致服务器读取部分无限死循环,导致程序阻塞
  • 解决:

    • 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:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ET1BmAOI-1590848623118)(./images/java复习第9天---9.3---网络编程---综合案例-B.png)]

后记

本项目为参考某马视频开发,相关视频及配套资料可自行度娘或者联系本人。上面为自己编写的开发文档,持续更新。欢迎交流,本人QQ:806797785

前端项目源代码地址:https://gitee.com/gaogzhen/vue-leyou
后端JAVA源代码地址:https://gitee.com/gaogzhen/JAVA
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值