Java进阶学习12:【网络编程、TCP/IP、TCP中的IO流、文件上传、模拟WEB服务器】

  • A+
所属分类:Java Java进阶

01. 软件介绍

C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。

B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。

02. 协议的介绍

参考 Java基础学习20:【网络编程】中的图。

03. TCP的三次握手

参考 Java基础学习20:【网络编程】中的图。

04. IP的介绍

IP是在网络中对于计算机的唯一标识

05. 端口号的介绍

端口号是在计算机中,对于应用设备的唯一标识。

06. 客户端和服务器的介绍

参考 Java基础学习20:【网络编程】中的图。

07. TCP中的IO流技术

参考 Java基础学习20:【网络编程】中的图。

08. TCP的客户端的实现

/*
    TCP的客户端(TCP通信的两边分别是客户端和服务器)

    在Java中,有一个类叫做Socket,这个类就表示TCP通信的客户端。

    Socket的构造方法:
        Socket(String host, int port):参数host表示目标服务器的ip地址。 参数port表示目标服务器程序的端口号

    Socket的其他方法:
        OutputStream getOutputStream(): 用于获取输出流对象, 该输出流用于向目的地写(发送)数据。
        InputStream getInputStream(): 用于获取输入流对象, 该输入流用于读取(接收)目的地程序发送过来的数据。
        void close(): 释放资源

    TCP客户端的实现步骤:
        1. 创建Socket对象,表示客户端。
        2. 通过Socket对象调用getOutputStream获取一个输出流,用来发送数据。
        3. 通过输出流向服务器发送(写)数据
        4. 通过Socket对象调用getInputStream获取一个输入流, 用来接收数据。
        5. 通过输入流接收服务器发送过来的数据。
        6. 释放资源。
 */
public class Demo01Client {
    public static void main(String[] args) throws IOException {
        //1. 创建Socket对象,表示客户端。
        //创建Socket对象的时候,会主动连接服务器,如果无法连接,那么就会报错(三次握手就是在这完成的)
        Socket socket = new Socket("127.0.0.1", 9527);
        //2. 通过Socket对象调用getOutputStream获取一个输出流,用来发送数据。
        OutputStream out = socket.getOutputStream(); //该输出流的目的地是服务器。
        //3. 通过输出流向服务器发送(写)数据
        out.write("你好服务器, I'm coming".getBytes());
        //4. 通过Socket对象调用getInputStream获取一个输入流, 用来接收数据。
        InputStream in = socket.getInputStream(); //该输入流的数据源来自服务器,这个流用于读取服务器发过来的数据。
        //5. 通过输入流接收(读取)服务器发送过来的数据。
        byte[] bArr = new byte[1024];
        int len = in.read(bArr);
        System.out.println(new String(bArr, 0, len));//将读取到的数据转成字符串进行打印
        //6. 释放资源。
        socket.close();
    }
}

09. TCP的服务器的实现

/*
    TCP的服务器端。

    在Java中,有一个类可以表示TCP的服务器端,这个类叫做ServerSocket

    ServerSocket的构造方法:
        ServerSocket(int port): 参数要传递一个端口号。 这个端口号表示该服务器程序的端口号。

    ServerSocket的其他方法:
        Socket accept(): 监听并获取客户端Socket

    TCP服务器端的实现步骤:
        1. 创建ServerSocket表示服务器。
        2. 调用服务器ServerSocket的accept方法,监听并获取客户端请求。
        3. 通过监听获取到的客户端Socket获取输入流,用来读取。
        4. 通过得到的数据读取客户端发送过来的数据。
        5. 通过监听获取到的客户端Socket获取输出流, 用来发送数据。
        6. 通过输出流给客户端发送数据。
        7. 释放资源。
 */
public class Demo02Server {
    public static void main(String[] args) throws IOException {
        //1. 创建ServerSocket表示服务器。
        ServerSocket serverSocket = new ServerSocket(9527);
        //2. 调用服务器ServerSocket的accept方法,监听并获取客户端请求。
        Socket socket = serverSocket.accept();
        //3. 通过监听获取到的客户端Socket获取输入流,用来读取。
        InputStream in = socket.getInputStream();//得到的输入流,用来接收客户端发送过来的数据。
        //4. 通过得到的数据读取客户端发送过来的数据。
        byte[] bArr = new byte[1024];
        int len = in.read(bArr);
        System.out.println(new String(bArr, 0, len));
        //5. 通过监听获取到的客户端Socket获取输出流, 用来发送数据。
        OutputStream out = socket.getOutputStream(); //得到的输出流,用来向客户端写数据。
        //6. 通过输出流给客户端发送数据。
        out.write("收到了你的消息,谢谢".getBytes());
        //7. 释放资源。
        socket.close();
        serverSocket.close();
    }
}

10. TCP案例的流程

参考 Java基础学习20:【网络编程】中的图。

11. 文件上传的流程说明

参考 Java基础学习20:【网络编程】中的图。

12. 文件上传客户端的操作

/*
    文件上传的客户端。

    对于客户端来说,他要做事情是读取自己电脑上的文件, 将读取到的文件中的字节发送给服务器。 然后再接收服务器返回过来的数据。

    客户端的实现步骤:
        1. 创建客户端Socket对象。
        2. 创建一个FileInputStream,用于读取自己电脑上的文件
        3. 调用Socket的getOutputStream,获取一个输出流, 用来向服务器写数据。
        4. 开始读写,读取客户端自己电脑的文件, 每读取一次,就将读取到的内容发送给服务器。
        5. 释放资源。
        6. 调用Socket的getInputStream,获取一个输入流, 用来接收服务器发送过来的数据。
        7. 调用输入流的read方法,接收数据。
        8. 释放资源。
 */
public class Demo01Client {
    public static void main(String[] args) throws IOException {
        //1. 创建客户端Socket对象。
        Socket socket = new Socket("127.0.0.1", 9527);
        //2. 创建一个FileInputStream,用于读取自己电脑上的文件
        FileInputStream fis = new FileInputStream("d:\\client\\aa.jpg");
        //3. 调用Socket的getOutputStream,获取一个输出流, 用来向服务器写数据。
        OutputStream out = socket.getOutputStream();
        //4. 开始读写,读取客户端自己电脑的文件, 每读取一次,就将读取到的内容发送给服务器。
        byte[] bArr = new byte[1024];
        int len;
        while((len = fis.read(bArr)) != -1) {
            //将读取到的数据发送给服务器
            out.write(bArr, 0, len);
        }
        //通知服务器,以后再也不会给服务器写数据了.
        socket.shutdownOutput();

        //5. 释放资源。
        fis.close();
        //6. 调用Socket的getInputStream,获取一个输入流, 用来接收服务器发送过来的数据。
        InputStream in = socket.getInputStream();
        //7. 调用输入流的read方法,接收数据。
        len = in.read(bArr);
        System.out.println(new String(bArr, 0, len));
        //8. 释放资源。
        socket.close();
    }
}

13. 文件上传服务器的实现

/*
    上传案例的服务器端。

    对于服务器来说, 服务器要做的事情是接收客户端发送过来的数据, 并将这些数据写到自己的电脑。 然后再给客户端回复一个消息。

    上传案例服务器的实现:
        1. 创建ServerSocket表示服务器。
        2. 监听并获取客户端的Socket
        3. 通过Socket获取一个输入流,用来读取客户端发送过来的数据。
        4. 自己创建一个输出流, 用来向服务器自己的电脑写数据。
        5. 开始读写, 每从客户端读取一次数据,那么就将读取到的数据写到自己电脑。
        6. 释放资源。
        7. 通过Socket获取一个输出流, 用来向客户端发送数据。
        8. 通过输出流调用write方法,发送数据。
        9. 释放资源。
 */
public class Demo02Server {
    public static void main(String[] args) throws IOException {
        //1. 创建ServerSocket表示服务器。
        ServerSocket serverSocket = new ServerSocket(9527);
        //2. 监听并获取客户端的Socket
        Socket socket = serverSocket.accept();
        //3. 通过Socket获取一个输入流,用来读取客户端发送过来的数据。
        InputStream in = socket.getInputStream();
        //4. 自己创建一个输出流, 用来向服务器自己的电脑写数据。
        //FileOutputStream fos = new FileOutputStream("d:\\server\\aa.jpg");
        //FileOutputStream fos = new FileOutputStream("d:\\server\\" + System.currentTimeMillis() + ".jpg");
        //使用UUID这个工具类可以获取一个随机的字符串序列(唯一)。
        // UUID.randomUUID()该方法可以得到一个随机字符串序列,它的结果是UUID类型,再调用toString方法转成字符串
        FileOutputStream fos = new FileOutputStream("d:\\server\\" + UUID.randomUUID().toString() + ".jpg");
        //5. 开始读写, 每从客户端读取一次数据,那么就将读取到的数据写到自己电脑。
        byte[] bArr = new byte[1024];
        int len;
        while((len = in.read(bArr)) != -1) {
            //将读取到的数据写到服务器自己电脑
            fos.write(bArr, 0, len);
        }
        //6. 释放资源。
        fos.close();
        //7. 通过Socket获取一个输出流, 用来向客户端发送数据。
        OutputStream out = socket.getOutputStream();
        //8. 通过输出流调用write方法,发送数据。
        out.write("上传成功".getBytes());
        //9. 释放资源。
        socket.close();
        serverSocket.close();
    }
}

14. 文件上传案例中的问题以及解决方式

参考 Java基础学习20:【网络编程】中的图。

15. 上传案例服务器死循环的实现

/*
    之前上传服务器: 服务器接收一个客户端的上传请求后,服务器就会停止,此时该服务器只能给一个客户端执行上传任务。

    要实现的效果: 服务器可以给多个客户端执行上传操作, 每次给客户端执行上传操作后, 那么服务器也不会停止。

    实现方式: 使用死循环。 使用死循环一直让服务器监听客户端的请求, 每监听到客户端的请求,那么就执行上传任务。

 */
@SuppressWarnings("all") //抑制警告
public class Demo03DeadLoopServer {
    public static void main(String[] args) throws IOException {
        //1. 创建ServerSocket表示服务器。
        ServerSocket serverSocket = new ServerSocket(9527);
        //在死循环中一直监听客户端请求, 一直执行上传任务。
        while (true) {
            //2. 监听并获取客户端的Socket
            Socket socket = serverSocket.accept();
            //3. 通过Socket获取一个输入流,用来读取客户端发送过来的数据。
            InputStream in = socket.getInputStream();
            //4. 自己创建一个输出流, 用来向服务器自己的电脑写数据。
            FileOutputStream fos = new FileOutputStream("d:\\server\\" + UUID.randomUUID().toString() + ".jpg");
            //5. 开始读写, 每从客户端读取一次数据,那么就将读取到的数据写到自己电脑。
            byte[] bArr = new byte[1024];
            int len;
            while((len = in.read(bArr)) != -1) {
                //将读取到的数据写到服务器自己电脑
                fos.write(bArr, 0, len);
            }
            //6. 释放资源。
            fos.close();
            //7. 通过Socket获取一个输出流, 用来向客户端发送数据。
            OutputStream out = socket.getOutputStream();
            //8. 通过输出流调用write方法,发送数据。
            out.write("上传成功".getBytes());
            //9. 释放资源。
            socket.close();
        }
    }
}

16. 上传案例多线程版本的实现

/*
    死循环版本服务器存在的问题: 如果有一个客户端上传了一个非常大的文件, 那么其他过来请求的客户端只能等着。

    问题原因: 在整个代码中只有一个main线程在执行任务。 服务器会使用main线程监听客户端请求, 再使用main线程执行上传任务,
              main线程的上传任务没有执行完, 那么就无法开始下次循环,去监听下一个客户端请求。

    解决方式: 使用多线程, 让main线程监听客户端请求, 每监听到客户端请求,就表示有客户端要执行上传任务,那么我们创建新的线程,
              使用新的线程去执行上传任务, 于此同时main线程还可以往下执行,再次去监听后面的客户端请求。
 */
@SuppressWarnings("all") //抑制警告
public class Demo04ThreadServer {
    public static void main(String[] args) throws IOException {
        //1. 创建ServerSocket表示服务器。
        ServerSocket serverSocket = new ServerSocket(9527);
        //在死循环中一直监听客户端请求, 一直执行上传任务。
        while (true) {
            //2. 监听并获取客户端的Socket
            Socket socket = serverSocket.accept();

            //当监听到客户端请求后,创建一个新的线程,使用新的线程执行上传任务。
            new Thread(() -> {
                //ctrl + alt + t可以生成try...catch
                try {
                    //3. 通过Socket获取一个输入流,用来读取客户端发送过来的数据。
                    InputStream in = socket.getInputStream();
                    //4. 自己创建一个输出流, 用来向服务器自己的电脑写数据。
                    FileOutputStream fos = new FileOutputStream("d:\\server\\" + UUID.randomUUID().toString() + ".jpg");
                    //5. 开始读写, 每从客户端读取一次数据,那么就将读取到的数据写到自己电脑。
                    byte[] bArr = new byte[1024];
                    int len;
                    while((len = in.read(bArr)) != -1) {
                        //将读取到的数据写到服务器自己电脑
                        fos.write(bArr, 0, len);
                    }
                    //6. 释放资源。
                    fos.close();
                    //7. 通过Socket获取一个输出流, 用来向客户端发送数据。
                    OutputStream out = socket.getOutputStream();
                    //8. 通过输出流调用write方法,发送数据。
                    out.write("上传成功".getBytes());
                    //9. 释放资源。
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

17. 模拟web服务器

/*
    浏览器也可以当做客户端。  B/S:就是浏览器/服务器的程序。

    服务器收到客户端的请求后, 要给客户端(浏览器)回一个html文件的内容,那么浏览器才能够看到页面。
 */
public class Demo01Server {
    public static void main(String[] args) throws IOException {
        //创建一个ServerSocket表示服务器
        ServerSocket serverSocket = new ServerSocket(10086);
        while(true) {
            //使用服务器调用accept方法监听并获取客户端请求
            Socket socket = serverSocket.accept();
            //如果监听到了客户端的请求, 那么就给客户端(浏览器)回写数据
            new Thread(() -> {
                try {
                    //通过Socket获取一个输出流,用来给浏览器写数据
                    OutputStream out = socket.getOutputStream();
                    //创建输入流,用来读取自己电脑的index.html文件的内容。
                    FileInputStream fis = new FileInputStream("day12\\index.html");
                    //B/S(浏览器/服务器)现在采用的协议是http协议。
                    //根据http协议的要求, 必须先给浏览器写三个固定的内容(前三行固定)
                    out.write("HTTP/1.1 200 OK\r\n".getBytes());//第一行
                    out.write("Content-Type=html/text\r\n".getBytes()); //第二行
                    out.write("\r\n".getBytes()); //第三行. 第三行固定是一个空换行。

                    //开始读取html文件,并写给浏览器
                    byte[] bArr = new byte[1024];
                    int len;
                    while((len = fis.read(bArr)) != -1) {
                        out.write(bArr, 0, len);
                    }
                    //释放资源
                    fis.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
  • 资源分享QQ群
  • weinxin
  • 官方微信公众号
  • weinxin
沙海
美女讲师教你学C语言
C语言郝斌老师教程
Java图书管理系统
Linux服务器网站环境安装

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: