P43 网络编程综合案例

系统:Win10
Java:1.8.0_333
IDEA:2020.3.4

1.文件上传案例

1.1 文件上传分析图解

  1. 【客户端】输入流,从硬盘读取文件数据到程序中
  2. 【客户端】输出流,写出文件数据到服务端
  3. 【服务端】输入流,读取文件数据到服务端程序
  4. 【服务端】输出流,写出文件数据到服务器硬盘中
    在这里插入图片描述

基本实现
服务端实现

public class FileUploadServer {
    public static void main(String[] args) throws IOException {
        System.out.println("服务器启动...");
        // 1.创建服务端ServerSocket
        ServerSocket server = new ServerSocket(6666);
        // 2.建立连接
        Socket socket = server.accept();
        // 3.创建流对象
        // 3.1获取输入流,读取文件数据
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        // 3.2创建输出流,保存到本地
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\output.png"));
        // 4.读写数据
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        // 5.关闭资源
        bos.close();
        bis.close();
        server.close();
        System.out.println("文件上传成功!!!");
    }
}

客户端实现

public class FileUploadClient {
    public static void main(String[] args) throws IOException {
        // 1.创建流对象
        String filename = System.getProperty("user.dir") + File.separator + "src" + File.separator + "s01" + File.separator + "lol.png";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename));
        // 2.创建输出流,写到服务器
        Socket socket = new Socket("127.0.0.1", 6666);
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        // 3.写数据
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        System.out.println("文件发送完毕!");
        // 4.释放资源
        bos.close();
        socket.close();
        bis.close();
    }
}

1.2 文件上传优化分析

1.2.1 文件名称写死的问题

服务端,保存文件的名称如果写死,那么最终导致服务器硬盘,只会保留一个文件,建议使用系统时间优化,保证文件名称唯一,代码如下

// 3.2创建输出流,保存到本地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\" + System.currentTimeMillis() + ".png"));

1.2.2 循环接收的问题

服务端,指保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断的接收不同用户的文件,代码如下

// 每次接收新的连接,创建一个Socket
while(true){
    Socket socket = server.accept();
    ......
}

1.2.3 效率问题

服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优化,代码如下

while(true){
    Socket socket = server.accept();
    // socket 交予子线程处理
    new Thread(new Runnable() {
    @Override
    public void run() {
        ......
    }).start();
}

1.3 优化实现

public class FileUploadServer {
    public static void main(String[] args) throws IOException {
        System.out.println("服务器启动...");
        // 1.创建服务端ServerSocket
        ServerSocket server = new ServerSocket(6666);
        while (true) {
            // 2.建立连接
            Socket socket = server.accept();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 3.创建流对象
                        // 3.1获取输入流,读取文件数据
                        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
                        // 3.2创建输出流,保存到本地
                        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\" + System.currentTimeMillis() + ".png"));
                        // 4.读写数据
                        int len = 0;
                        byte[] bytes = new byte[10240];
                        while ((len = bis.read(bytes)) != -1) {
                            bos.write(bytes, 0, len);
                        }
                        // 5.关闭资源
                        bos.close();
                        bis.close();
                        socket.close();
                        System.out.println("文件上传成功!!!");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

1.4 信息回写分析图解

前四步与基本文件上传一致

  1. 【服务端】获取输出流,回写数据
  2. 【客户端】获取输入流,解析回写数据

回写实现

public class FileUploadServer {
    public static void main(String[] args) throws IOException {
        System.out.println("服务器启动...");
        // 1.创建服务端ServerSocket
        ServerSocket server = new ServerSocket(6666);
        while (true) {
            // 2.建立连接
            Socket socket = server.accept();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 3.创建流对象
                        // 3.1获取输入流,读取文件数据
                        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
                        // 3.2创建输出流,保存到本地
                        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\" + System.currentTimeMillis() + ".png"));
                        // 4.读写数据
                        int len = 0;
                        byte[] bytes = new byte[10240];
                        while ((len = bis.read(bytes)) != -1) {
                            bos.write(bytes, 0, len);
                        }
                        System.out.println("文件上传成功!!!");

                        /* 信息回写 */
                        System.out.println("信息回写...");
                        OutputStream os = socket.getOutputStream();
                        os.write("回写上传成功!".getBytes());
                        os.close();

                        // 5.关闭资源
                        bos.close();
                        bis.close();
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

客户端实现

public class FileUploadClient {
    public static void main(String[] args) throws IOException {
        // 1.创建流对象
        String filename = System.getProperty("user.dir") + File.separator + "src" + File.separator + "s01" + File.separator + "lol.png";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename));
        // 2.创建输出流,写到服务器
        Socket socket = new Socket("127.0.0.1", 6666);
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        // 3.写数据
        byte[] bytes = new byte[10240];
        int len = 0;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes,0,len);
        }
        // 4.关闭输出流,通知客户端,写数据完毕
        socket.shutdownOutput();
        System.out.println("文件发送完毕!");
        // 5.解析回写
        InputStream is = socket.getInputStream();
        byte[] back = new byte[1024];
        is.read(back);
        System.out.println(new String(back));
        is.close();
        // 6.释放资源
        socket.close();
        bis.close();
    }
}

2.模拟 BS 服务器

模拟网站服务器,使用浏览器访问自己编写的服务端程序,查看网页效果

2.1 案例分析

  1. 准备页面数据,web 文件夹。复制到我们 Module 的对应文件夹下,比如复制到 s03 文件夹中
    在这里插入图片描述

  2. 我们模拟服务器端,ServerSocket 类监听端口,使用浏览器访问

public class BSServerTest {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        Socket socket = server.accept();
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes));
        socket.close();
        server.close();
    }
}

在这里插入图片描述

  1. 服务器程序中字节输入流可以读取到浏览器发来的请求信息
    在这里插入图片描述
GET /src/s03/web/index.html HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: Idea-4a52b16f=c86c4fe0-48ef-43d2-84a6-74ec4861eba3

GET /src/s03/web/index.html HTTP/1.1 是浏览器的请求消息,/src/s03/web/index.html 为浏览器想要请求的服务器端的资源,使用字符串切割方式获取到请求的资源

// 转换流,读取浏览器请求的第一行数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
// 获取请求资源的路径
String[] arr = line.split(" ");
String path = arr[1].substring(1);
System.out.println(path);

2.2 案例实现

服务端实现

public class BSServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        Socket socket = server.accept();
        // 转换流,读取浏览器请求的第一行数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line = br.readLine();
        // 获取请求资源的路径
        String[] arr = line.split(" ");
        String path = arr[1].substring(1);

        // 读取客户端请求的资源文件
        FileInputStream fis = new FileInputStream(path);
        byte[] bytes = new byte[1024];
        int len = 0;
        // 字节输出流,将文件回写客户端
        OutputStream os = socket.getOutputStream();
        // 写入HTTP协议响应头,固定写法
        os.write("HTTP/1.1 200 OK\r\n".getBytes());
        os.write("Content-Type:text/html\r\n".getBytes());
        // 必须要写入空行,否则浏览器不会解析,解析了也会乱码
        os.write("\r\n".getBytes());
        while ((len = fis.read(bytes)) != -1) {
            os.write(bytes, 0, len);
        }
        // 关闭资源
        os.close();
        fis.close();
        br.close();
        socket.close();
        server.close();
    }
}

chrome 访问效果
在这里插入图片描述

提示:不同的浏览器,内核不一样,解析效果有可能不一样

发现浏览器中出现很多的叉子,说明浏览器没有读取到图片信息导致。 浏览器工作原理是遇到图片会开启一个线程进行单独的访问,因此在服务器端加入线程技术

public class BSServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        while(true){
            Socket socket = server.accept();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        // 转换流,读取浏览器请求的第一行数据
                        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        String line = br.readLine();
                        // 获取请求资源的路径
                        String[] arr = line.split(" ");
                        String path = arr[1].substring(1);

                        // 读取客户端请求的资源文件
                        FileInputStream fis = new FileInputStream(path);
                        byte[] bytes = new byte[1024];
                        int len = 0;
                        // 字节输出流,将文件回写客户端
                        OutputStream os = socket.getOutputStream();
                        // 写入HTTP协议响应头,固定写法
                        os.write("HTTP/1.1 200 OK\r\n".getBytes());
                        os.write("Content-Type:text/html\r\n".getBytes());
                        // 必须要写入空行,否则浏览器不会解析,解析了也会乱码
                        os.write("\r\n".getBytes());
                        while ((len = fis.read(bytes)) != -1) {
                            os.write(bytes, 0, len);
                        }
                        // 关闭资源
                        os.close();
                        fis.close();
                        br.close();
                        socket.close();
                    } catch (IOException e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

2.3 访问效果

在这里插入图片描述

2.4 BS 通信图解

在这里插入图片描述

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李晋江

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

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

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

打赏作者

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

抵扣说明:

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

余额充值