HTTP程序设计(互联网程序设计课程 第8讲)


教学与实践目的:学会WEB浏览器基本的程序设计技术。

一、概述

HTTP系统包括客户端软件(浏览器)和服务器软件(HTTP服务器)。早期的客户端软件,其主要工作可理解为文件下载和文件显示。

实际上现代的HTTP客户端比文件下载要复杂得多,它包括网页文件的下载、跨平台的本地显示,参数的传递,动态网页的实现,以及交互等功能。

HTTP系统程序设计包括:

(1)客户端软件(web浏览器软件如Edge浏览器、360浏览器);
(2)服务器软件(web服务器软件如IIS、Nginx、Tomcat等)。

HTTP系统客户端的工作过程是:

(1)客户端软件和服务器建立连接(TCP的三次握手);
(2)发送HTTP头格式协议;
(3)接收网页文件;
(4)显示网页。

HTTP系统服务端的工作过程:

(1)服务器软件开启80端口;
(2)响应客户的要求、完成TCP连接;
(3)检查客户端的HTTP头格式发送客户请求的网页文件(含动态网页)。

一个完整的HTTP请求-响应如图8.1所示:
在这里插入图片描述
本讲主要学习网页下载程序设计技术。网页下载技术是搜索引擎、网络爬虫、网页采集器或网络推送服务等相关应用领域内的基础技术。

二、程序设计第一步:基于TCP套接字的网页下载程序设计

利用TCP客户套接字Socket和HTTP服务器进行信息交互,将网页的原始内容下载显示在图形界面中,具体工作如下:
(1)新建一个包chapter08,将第3讲的TCPClient.java复制到此包下,重命名为HTTPClient.java;
(2)创建HTTPClientFX.java程序,界面如图8.2所示,网页地址输入www.baidu.com进行测试,端口为80,在“连接”按钮类似以往的编码方式,放置连接服务器的代码和启动输入流“读线程”;在“网页请求”按钮中发送HTTP请求头标准格式(关于HTTP请求头的更多信息可查阅互联网);在“退出”按钮中,相对之前章节的代码,不是发送“bye”表示结束,而是send(“Connection:close” +“\r\n”) 表示结束连接。

在这里插入图片描述
具体来说,“网页请求”按钮中,我们的程序可以按顺序发送以下HTTP请求头:

GET / HTTP/1.1 //访问默认网页,注意GET后的 / 前后要留有空格
HOST: address //address指服务器的IP或域名
Accept: /
Accept-Language: zh-cn
User-Agent: User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Connection: Keep-Alive

将上述的HTTP头格式构成一整个字符串,一致发送到HTTP服务器。(注意:Windows系统下各个请求头字符串用\r\n分隔区分,不同操作系统的User-Agent也要相应修改;通常用StringBuffer类的append方法来拼接这些字符串,其toString()方法可将内容转换为字符串)。

如果网页信息显示区返回的第一条信息是“HTTP/1.1 200 OK”,则说明访问正常。

现在将网页地址改为www.gdufs.edu.cn,其它不变,再次连接并点击网页请求,结果如图8.3所示:
在这里插入图片描述

这种情况HTTP服务器确实有内容返回,却不是我们预期的内容,第一行内容为“HTTP/1.1 302 Found”,这种情况一般是站点关闭了HTTP,只允许启用了SSL/TLS的HTTPS安全连接,这种连接默认是使用443端口。事实上,出于安全考虑,现在绝大部分的web站点都将放弃HTTP而启用HTTPS。

三、程序设计第二步:基于SSL Socket的网页下载程序设计

要访问HTTPS站点,就不能用普通的客户端套接字,而是使用SSL套接字。Java安全套接字扩展(Java Secure Socket Extension,JSSE)为基于SSL和TLS协议的Java网络应用程序提供了Java API以及参考实现,相关的类都定义在javax.net.ssl包下面,我们这里只使用其客户端的SSLSocket套接字。SSLSocket相对之前学习的客户端套接字,只是创建方法不同,SSLSocket对象由SSLSocketFactory创建,创建之后用法几乎一致。

具体工作如下:
(1)根据HTTPClient.java的内容,稍作修改,新创建HTTPSClient.java程序。

(2)根据HTTPClientFX.java程序的内容,稍做修改,新创建一个HTTPSClientFX.java程序(实质上只需要将原来的使用HTTPClient及其对象实例的的地方修改为使用HTTPSClient及其对象实例即可)。
(3)运行该程序,界面如图8.4所示,可以看到已经能够正常获取到广外官网首页的内容。
在这里插入图片描述
(4)完全没有必要使用两个不同的图形界面来分别访问http和https,创建一个HTTPAllClientFX.java,融合以上两个图形客户端的功能,使得该图形客户端既可访问443https内容,也可以访问非443端口(一般是80)的http内容。

四、程序设计第三步:基于URL类的网页下载程序设计

前面直接发送http请求的程序,对于复杂的地址,例如指向具体页面的地址,无法完成网页下载任务,我们可以使用基于URL类的解决方案。

URL(Uniform Resource Locator)中文名为统一资源定位符,用于表示资源地址,资源如网页或者FTP地址等。

URL的格式为protocol://资源地址,protocol可以是HTTP、HTTPS、FTP 和 File,资源地址中可以带有端口号及查询参数,具体可以自行搜索URL的知识。

在java.net包中定义了URL类,该类用来处理有关URL的内容。并且其封装有一个InputStream返回类型的openStream()方法,我们的程序就可以读取这个字节输入流来获得对应内容。

(1)创建程序URLClientFX.java,界面布局可参见如图8.5所示:
在这里插入图片描述
注意URL方式和直接发送HTTP请求报头的方式,返回结果有何不同。
(2)完善程序,使得用户输入不符合URL语法的网址,你的程序不是抛出异常报错,而是在显示区提醒用户输入地址不合规,如图8.6所示。
在这里插入图片描述

五、扩展:web浏览器

上述程序设计仅仅实现了网页文件的下载,若想要一定的网页显示功能,可以使用JavaFX的WebEngine和WebView组件。

(1)WebEngine类

WebEngine类提供了基本的web页面功能。尽管它并不与用户直接交互,但它也支持用户交互,如导航链接和提交HTML表单。WebEngine类一次处理一个web页面。它支持加载HTML内容和访问DOM对象等基本功能,也支持执行JavaScript指令。

有两个构造方法能够创建WebEngine对象:空构造和带一个URL参数的构造。如果你使用空构造方法来实例化它,那么URL可以通过WebEngine对象的load()方法来传入。WebEngine对象实例的getLocation()方法会返回当前加载页面的URL地址。

(2)WebView类

WebView类是Node类的一个扩展。它封装了WebEngine对象,将HTML内容加入程序的scene中,并且提供各种属性和方法来应用特效和变换。WebView对象的getEngine()方法返回一个与之关联的web engine,例如可以这样使用:

WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
webEngine.load("https://www.gdufs.edu.cn");

广外首页的内容就会加载显示在webView组件中,webEngine 如果再次 load另外一个URL,则webView就自动加载另外一个页面的内容。

(3)WebHistory类
WebHistory类可获取已访问的页面列表,它表示与WebEngine对象关联的一个会话历史记录。使用WebEngine.getHistory()方法来获取某特定webEngine对象的WebHistory实例, 即:WebHistory history = webEngine.getHistory();

该历史记录基本上是一种特殊类型的列表。类似普通List,该列表也是下标从0开始,每一个条目表示一个已访问过的页面并且提供对该页面相关信息的访问。该列表可通过getEntries()方法获得。该对象的getCurrentIndex()方法返回值表示当前访问页面在列表中的索引位置。该对象的go(int index)方法表示页面的跳转,例如history.go(-1)就是返回上一个访问的页面,history.go(1)表示前进到下一个访问过的页面,这两个方法使用要注意边界条件判断,否则会抛出异常,例如已经没有可回退的页面记录,再执行回退方法就抛出异常。

(4)创建一个javaFX程序,命名为WebBrowserFX.java,参考界面如图8.8所示,完成界面所示的功能。
(5)实现附加功能:

  • 要求“前进”和“后退”按钮能够根据实际情况自动禁用和启用;
  • 在地址框中输入URL地址,回车后载入页面,但如果点击链接后,页面跳转,地址框的地址默认是不会跟随变化的。实现该功能:能自动更新为当前页面的URL地址。

提示:使用webEngine的状态监听器,监听页面的载入状态,只要页面有载入动作,就会触发其中的代码。在这个监听器中,我们就可以将访问的URL地址及时更新到地址栏,并设置前进后退按钮的可用性:

webEngine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
//……… 省略,自行实现
});

六、完整参考代码

chapter08/HTTPAllClientFX.java

package chapter08;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;

/**
 * @projectName: NetworkApp
 * @package: chapter08
 * @className: HTTPAllClientFX
 * @author: GCT
 * @description: TODO
 * @date: 2022/10/25 19:11
 * @version: 1.0
 */
public class HTTPAllClientFX extends Application {
    private Button btnConnect = new Button("连接");
    private Button btnClose = new Button("退出");
    private Button btnClear = new Button("清空");
    private Button btnHttp = new Button("网页请求");
    private TextField tfUrl = new TextField("www.gdufs.edu.cn");
    private TextField tfPort = new TextField("443");
    private TextArea taDisplay = new TextArea();
    private HTTPClient httpClient;
    private HTTPSClient httpsClient;
    Thread readThread;
    String url;
    String port;
    @Override
    public void start(Stage primaryStage){
        BorderPane mainpane = new BorderPane();
        HBox tophbox = new HBox();
        tophbox.setSpacing(10);
        tophbox.setPadding(new Insets(10,20,10,20));
        tophbox.setAlignment(Pos.CENTER);
        tophbox.getChildren().addAll(new Label("网页地址"),tfUrl,new Label("端口"),tfPort,btnConnect);

        VBox vBox = new VBox();
        vBox.setPadding(new Insets(10,20,10,20));
        vBox.setSpacing(10);
        vBox.getChildren().addAll(new Label("信息显示区"),taDisplay);
        HBox buttomHbox = new HBox();
        buttomHbox.setPadding(new Insets(10,20,10,20));
        buttomHbox.setSpacing(10);
        buttomHbox.setAlignment(Pos.CENTER_RIGHT);
        buttomHbox.getChildren().addAll(btnHttp,btnClear,btnClose);
        mainpane.setTop(tophbox);
        mainpane.setCenter(vBox);
        mainpane.setBottom(buttomHbox);
        Scene scene = new Scene(mainpane,800,400);
        primaryStage.setScene(scene);
        primaryStage.show();

        btnConnect.setOnAction(event -> {
            url = tfUrl.getText().trim();
            port = tfPort.getText().trim();
            if(port.equals("443")) {
                try {
                    httpsClient = new HTTPSClient(url, port);
                    taDisplay.appendText("服务器启动");
                    // 多线程不需要这一条了
//                String firstMsg = tcpClient.receive();
//                taDisplay.appendText(firstMsg+"\n");
//                btnConnect.setDisable(true);
                    //多线程方法

                    readThread = new Thread(() -> {
                        String msg = null;
                        while ((msg = httpsClient.receive()) != null) {
                            String msgTemp = msg;
                            Platform.runLater(() -> {
                                taDisplay.appendText(msgTemp + "\n");
                            });
                        }
                        Platform.runLater(() -> {
                            taDisplay.appendText("对话已关闭!\n");
                        });
                    });
                    readThread.start();
                } catch (IOException e) {
                    taDisplay.appendText("服务器连接失败" + e.getMessage() + "\n");
                }
            }

            else {
                try {
                    httpClient = new HTTPClient(url, port);
                    taDisplay.appendText("服务器启动");
                    readThread = new Thread(() -> {
                        String msg = null;
                        while ((msg = httpClient.receive()) != null) {
                            String msgTemp = msg;
                            Platform.runLater(() -> {
                                taDisplay.appendText(msgTemp + "\n");
                            });
                        }
                        Platform.runLater(() -> {
                            taDisplay.appendText("对话已关闭!\n");
                        });
                    });
                    readThread.start();
                } catch (IOException e) {
                    taDisplay.appendText("服务器连接失败" + e.getMessage() + "\n");
                }
            }
        });

        btnHttp.setOnAction(event -> {
            StringBuffer msg = new StringBuffer("GET / HTTP/1.1" + "\r\n"); //
            //  StringBuffer msg = new StringBuffer("GET / connecttest.txt HTTP/1.1 " + "\r\n"); //注意/后面需要有空格
            msg.append("HOST:" + url + "\r\n");
            msg.append("Accept: */*" + "\r\n");
            msg.append("Accept-Language: zh-cn"  + "\r\n");
            msg.append("Accept-Encoding: deflate" + "\r\n");
            msg.append("User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows XP)" + "\r\n");
            msg.append("Connection:Keep-Alive" + "\r\n");
            //msg.append
            if(port.equals("443")){
                httpsClient.send(msg.toString());
            }else{
                httpClient.send(msg.toString());
            }
        });

        btnClear.setOnAction(event -> {
            taDisplay.clear();
        });
        btnClose.setOnAction(event -> {
            if(httpClient != null){
                //向服务器发送关闭连接的约定信息
                httpClient.send("bye");
                httpClient.close();
            }else if(httpsClient != null){
                //向服务器发送关闭连接的约定信息
                httpsClient.send("bye");
                httpsClient.close();
            }
            System.exit(0);
        });
    }
}

chapter08/HTTPClient.java

package chapter08;

import java.io.*;
import java.net.Socket;

/**
 * @projectName: NetworkApp
 * @package: chapter02
 * @className: TCPCilent
 * @author: GCT
 * @description: TODO
 * @date: 2022/9/4 18:06
 * @version: 1.0
 */
public class HTTPClient {
    private Socket socket; //定义套接字
    //定义字符输入流和输出流
    private PrintWriter pw;
    private BufferedReader br;

    public HTTPClient(String ip, String port) throws IOException {
        //主动向服务器发起连接,实现TCP的三次握手过程
        //如果不成功,则抛出错误信息,其错误信息交由调用者处理
        socket = new Socket(ip, Integer.parseInt(port));

        //得到网络输出字节流地址,并封装成网络输出字符流
        OutputStream socketOut = socket.getOutputStream();
        pw = new PrintWriter( // 设置最后一个参数为true,表示自动flush数据
                new OutputStreamWriter(//设置utf-8编码
                        socketOut, "utf-8"), true);

        //得到网络输入字节流地址,并封装成网络输入字符流
        InputStream socketIn = socket.getInputStream();
        br = new BufferedReader(
                new InputStreamReader(socketIn, "utf-8"));
    }

    public void send(String msg) {
        //输出字符流,由Socket调用系统底层函数,经网卡发送字节流
        pw.println(msg);
    }

    public String receive() {
        String msg = null;
        try {
            //从网络输入字符流中读信息,每次只能接受一行信息
            //如果不够一行(无行结束符),则该语句阻塞等待,
            // 直到条件满足,程序才往下运行
            msg = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return msg;
    }
    public void close() {
        try {
            if (socket != null) {
                //关闭socket连接及相关的输入输出流,实现四次握手断开
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException{
        HTTPClient tcpClient = new HTTPClient("127.0.0.1", "8008");
        tcpClient.send("hello");
        System.out.println(tcpClient.receive());

    }
}

chapter08/HTTPClientFX.java

package chapter08;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;


/**
 * @projectName: NetworkApp
 * @package: chapter08
 * @className: HTTPClientFX
 * @author: GCT
 * @description: TODO
 * @date: 2022/10/25 19:04
 * @version: 1.0
 */
public class HTTPClientFX extends Application{
    private Button btnConnect = new Button("连接");
    private Button btnClose = new Button("退出");
    private Button btnClear = new Button("清空");
    private Button btnHttp = new Button("网页请求");
    private TextField tfUrl = new TextField();
    private TextField tfPort = new TextField();
    private TextArea taDisplay = new TextArea();
    private HTTPClient httpClient;
    Thread readThread;

    @Override
    public void start(Stage primaryStage){
        BorderPane mainpane = new BorderPane();
        HBox tophbox = new HBox();
        tophbox.setSpacing(10);
        tophbox.setPadding(new Insets(10,20,10,20));
        tophbox.setAlignment(Pos.CENTER);
        tophbox.getChildren().addAll(new Label("网页地址"),tfUrl,new Label("端口"),tfPort,btnConnect);

        VBox vBox = new VBox();
        vBox.setPadding(new Insets(10,20,10,20));
        vBox.setSpacing(10);
        vBox.getChildren().addAll(new Label("信息显示区"),taDisplay);
        HBox buttomHbox = new HBox();
        buttomHbox.setPadding(new Insets(10,20,10,20));
        buttomHbox.setSpacing(10);
        buttomHbox.setAlignment(Pos.CENTER_RIGHT);
        buttomHbox.getChildren().addAll(btnHttp,btnClear,btnClose);
        mainpane.setTop(tophbox);
        mainpane.setCenter(vBox);
        mainpane.setBottom(buttomHbox);
        Scene scene = new Scene(mainpane,800,400);
        primaryStage.setScene(scene);
        primaryStage.show();

        btnConnect.setOnAction(event -> {
            String url = tfUrl.getText().trim();
            String port = tfPort.getText().trim();
            try {
                httpClient = new HTTPClient(url,port);
                // 多线程不需要这一条了
//                String firstMsg = tcpClient.receive();
//                taDisplay.appendText(firstMsg+"\n");
//                btnConnect.setDisable(true);
                //多线程方法

                readThread = new Thread(()->{
                    String msg = null;
                    while((msg = httpClient.receive())!=null){
                        String msgTemp = msg;
                        Platform.runLater(()->{
                            taDisplay.appendText(msgTemp+"\n");
                        });
                    }
                    Platform.runLater(()->{
                        taDisplay.appendText("对话已关闭!\n");
                    });
                });
                readThread.start();
            } catch (IOException e) {
                taDisplay.appendText("服务器连接失败"+e.getMessage()+"\n");
            }
        });

        btnHttp.setOnAction(event -> {
            String ip = tfUrl.getText();
            StringBuffer msg = new StringBuffer("GET / HTTP/1.1" + "\r\n"); //
            //  StringBuffer msg = new StringBuffer("GET / connecttest.txt HTTP/1.1 " + "\r\n"); //注意/后面需要有空格
            msg.append("HOST:" + ip + "\r\n");
            msg.append("Accept: */*" + "\r\n");
            msg.append("Accept-Language: zh-cn"  + "\r\n");
            msg.append("Accept-Encoding: deflate" + "\r\n");
            msg.append("User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows XP)" + "\r\n");
            msg.append("Connection:Keep-Alive" + "\r\n");
            //msg.append
            httpClient.send(msg.toString());
        });

    }

}

chapter08/HTTPSClient.java

package chapter08;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.Socket;

/**
 * @projectName: NetworkApp
 * @package: chapter08
 * @className: HTTPSClient
 * @author: GCT
 * @description: TODO
 * @date: 2022/10/25 19:07
 * @version: 1.0
 */
public class HTTPSClient {
    private SSLSocket socket; //定义套接字
    private SSLSocketFactory factory;
    //定义字符输入流和输出流
    private PrintWriter pw;
    private BufferedReader br;

    public HTTPSClient(String ip, String port) throws IOException {
        //主动向服务器发起连接,实现TCP的三次握手过程
        //如果不成功,则抛出错误信息,其错误信息交由调用者处理
        factory=(SSLSocketFactory)SSLSocketFactory.getDefault();
        socket=(SSLSocket)factory.createSocket(ip,Integer.parseInt(port));

        //得到网络输出字节流地址,并封装成网络输出字符流
        OutputStream socketOut = socket.getOutputStream();
        pw = new PrintWriter( // 设置最后一个参数为true,表示自动flush数据
                new OutputStreamWriter(//设置utf-8编码
                        socketOut, "utf-8"), true);

        //得到网络输入字节流地址,并封装成网络输入字符流
        InputStream socketIn = socket.getInputStream();
        br = new BufferedReader(
                new InputStreamReader(socketIn, "utf-8"));
    }

    public void send(String msg) {
        //输出字符流,由Socket调用系统底层函数,经网卡发送字节流
        pw.println(msg);
    }

    public String receive() {
        String msg = null;
        try {
            //从网络输入字符流中读信息,每次只能接受一行信息
            //如果不够一行(无行结束符),则该语句阻塞,
            // 直到条件满足,程序才往下运行
            msg = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return msg;
    }
    public void close() {
        try {
            if (socket != null) {
                //关闭socket连接及相关的输入输出流,实现四次握手断开
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //本机模块内测试与运行,需先运行TCPServer
    public static void main(String[] args) throws IOException {
        HTTPSClient tcpClient = new HTTPSClient("127.0.0.1" ,"8008");
        tcpClient.send("hello");//发送一串字符
        //接收服务器返回的字符串并显示
        System.out.println(tcpClient.receive());
    }

}

chapter08/HTTPSClientFX.java

package chapter08;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;


/**
 * @projectName: NetworkApp
 * @package: chapter08
 * @className: HTTPSClientFX
 * @author: GCT
 * @description: TODO
 * @date: 2022/10/25 19:08
 * @version: 1.0
 */
public class HTTPSClientFX extends Application{
    private Button btnConnect = new Button("连接");
    private Button btnClose = new Button("退出");
    private Button btnClear = new Button("清空");
    private Button btnHttp = new Button("网页请求");
    private TextField tfUrl = new TextField();
    private TextField tfPort = new TextField();
    private TextArea taDisplay = new TextArea();
    private HTTPSClient httpsClient;
    Thread readThread;

    @Override
    public void start(Stage primaryStage){
        BorderPane mainpane = new BorderPane();
        HBox tophbox = new HBox();
        tophbox.setSpacing(10);
        tophbox.setPadding(new Insets(10,20,10,20));
        tophbox.setAlignment(Pos.CENTER);
        tophbox.getChildren().addAll(new Label("网页地址"),tfUrl,new Label("端口"),tfPort,btnConnect);

        VBox vBox = new VBox();
        vBox.setPadding(new Insets(10,20,10,20));
        vBox.setSpacing(10);
        vBox.getChildren().addAll(new Label("信息显示区"),taDisplay);
        HBox buttomHbox = new HBox();
        buttomHbox.setPadding(new Insets(10,20,10,20));
        buttomHbox.setSpacing(10);
        buttomHbox.setAlignment(Pos.CENTER_RIGHT);
        buttomHbox.getChildren().addAll(btnHttp,btnClear,btnClose);
        mainpane.setTop(tophbox);
        mainpane.setCenter(vBox);
        mainpane.setBottom(buttomHbox);
        Scene scene = new Scene(mainpane,800,400);
        primaryStage.setScene(scene);
        primaryStage.show();

        btnConnect.setOnAction(event -> {
            String url = tfUrl.getText().trim();
            String port = tfPort.getText().trim();
            try {
                httpsClient = new HTTPSClient(url,port);
                // 多线程不需要这一条了
//                String firstMsg = tcpClient.receive();
//                taDisplay.appendText(firstMsg+"\n");
//                btnConnect.setDisable(true);
                //多线程方法

                readThread = new Thread(()->{
                    String msg = null;
                    while((msg = httpsClient.receive())!=null){
                        String msgTemp = msg;
                        Platform.runLater(()->{
                            taDisplay.appendText(msgTemp+"\n");
                        });
                    }
                    Platform.runLater(()->{
                        taDisplay.appendText("对话已关闭!\n");
                    });
                });
                readThread.start();
            } catch (IOException e) {
                taDisplay.appendText("服务器连接失败"+e.getMessage()+"\n");
            }
        });

        btnHttp.setOnAction(event -> {
            String ip = tfUrl.getText();
            StringBuffer msg = new StringBuffer("GET / HTTP/1.1" + "\r\n"); //
            //  StringBuffer msg = new StringBuffer("GET / connecttest.txt HTTP/1.1 " + "\r\n"); //注意/后面需要有空格
            msg.append("HOST:" + ip + "\r\n");
            msg.append("Accept: */*" + "\r\n");
            msg.append("Accept-Language: zh-cn"  + "\r\n");
            msg.append("Accept-Encoding: deflate" + "\r\n");
            msg.append("User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows XP)" + "\r\n");
            msg.append("Connection:Keep-Alive" + "\r\n");
            //msg.append
            httpsClient.send(msg.toString());
        });

    }
}

chapter08/URLClientFX.java

package chapter08;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

/**
 * @projectName: NetworkApp
 * @package: chapter08
 * @className: URLClientFX
 * @author: GCT
 * @description: TODO
 * @date: 2022/10/25 19:10
 * @version: 1.0
 */
public class URLClientFX extends Application{
    private Button btnClose = new Button("退出");
    private Button btnClear = new Button("清空");
    private Button btnHttp = new Button("发送");
    private TextField tfUrl = new TextField();
    private TextArea taDisplay = new TextArea();
    private HTTPClient httpClient;
    Thread readThread;
    BufferedReader br;
    @Override
    public void start(Stage primaryStage){
        BorderPane mainpane = new BorderPane();
        taDisplay.setWrapText(true);
        VBox vBox = new VBox();
        vBox.setPadding(new Insets(10,20,10,20));
        vBox.setSpacing(10);
        vBox.getChildren().addAll(new Label("网页信息显示区"),taDisplay,new Label("输入URL地址"),tfUrl);
        VBox.setVgrow(taDisplay, Priority.ALWAYS);
        HBox buttomHbox = new HBox();
        buttomHbox.setPadding(new Insets(10,20,10,20));
        buttomHbox.setSpacing(10);
        buttomHbox.setAlignment(Pos.CENTER_RIGHT);
        buttomHbox.getChildren().addAll(btnHttp,btnClear,btnClose);
        mainpane.setCenter(vBox);
        mainpane.setBottom(buttomHbox);
        Scene scene = new Scene(mainpane,800,400);
        primaryStage.setScene(scene);
        primaryStage.show();


        btnClear.setOnAction(event -> {
            taDisplay.clear();
        });
        btnHttp.setOnAction(event -> {

            taDisplay.clear();
            try {
                String ip = tfUrl.getText();
                if (!isURL(ip)) {
                    taDisplay.appendText("当前url不合法");
                } else {
                    URL url = new URL(ip);
                    System.out.println("连接成功!");
                    InputStream in = url.openStream();
                    br = new BufferedReader(new InputStreamReader(in, "utf-8"));
//                readThread=new Thread(){
//                    String msg =null;
//                    while((msg=br.readLine())!=null){
//                        taDisplay.appendText(msg.toString());
//                    }
//                };
                    readThread = new Thread(() -> {
                        String msg;
                        //不知道服务器有多少回传信息,就持续不断接收
                        //由于在另外一个线程,不会阻塞主线程的正常运行
                        try {
                            while ((msg = br.readLine()) != null) {
                                //lambda表达式不能直接访问外部非final类型局部变量
                                //所以这里使用了一个临时变量
                                String msgTemp = msg;
                                Platform.runLater(() -> {
                                    taDisplay.appendText(msgTemp + "\r\n");
                                });
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        //跳出了循环,说明服务器已关闭,读取为null,提示对话关闭
                        Platform.runLater(() -> {
                            taDisplay.appendText("对话已关闭!\n");
                        });
                    });
                    readThread.start();

                }
            }catch(IOException e) {
                e.printStackTrace();
            }
        });


    }
    public static boolean isURL(String str){
        //转换为小写
        str = str.toLowerCase();
        String regex = "^((https|http|ftp|rtsp|mms)?://)"  //https、http、ftp、rtsp、mms
                + "(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" //ftp的user@
                + "(([0-9]{1,3}\\.){3}[0-9]{1,3}" // IP形式的URL- 例如:199.194.52.184
                + "|" // 允许IP和DOMAIN(域名)
                + "([0-9a-z_!~*'()-]+\\.)*" // 域名- www.
                + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\." // 二级域名
                + "[a-z]{2,6})" // first level domain- .com or .museum
                + "(:[0-9]{1,5})?" // 端口号最大为65535,5位数
                + "((/?)|" // a slash isn't required if there is no file name
                + "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
        return  str.matches(regex);
    }
}

chapter08/WebBrowserFX.java

package chapter08;

import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebHistory;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.concurrent.Worker.State;

/**
 * @projectName: NetworkApp
 * @package: chapter08
 * @className: WebBrowserFX
 * @author: GCT
 * @description: TODO
 * @date: 2022/10/25 19:11
 * @version: 1.0
 */
public class WebBrowserFX extends Application{
    private Button btnConnect = new Button("跳转");
    private Button btnFlash= new Button("刷新");
    private Button btnBefore= new Button("前进");
    private Button btnAfter = new Button("后退");
    private Button btnHome = new Button("首页");
    private TextField textField = new TextField();
    private WebView webView=new WebView();
    private WebEngine webEngine;
    private WebHistory webHistory;


    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane mainpane = new BorderPane();
        textField.setPrefWidth(400);
        HBox hBox = new HBox();
        hBox.setAlignment(Pos.CENTER);
        hBox.setPadding(new Insets(10,10,10,10));
        hBox.setSpacing(10);
        hBox.getChildren().addAll(btnFlash,btnBefore,btnAfter,btnHome,textField,btnConnect);
        ScrollPane scrollPane = new ScrollPane();
        webEngine = webView.getEngine();
        webHistory=webEngine.getHistory();
        webEngine.load("https://www.gdufs.edu.cn");
        scrollPane.setContent(webView);
        scrollPane.setFitToWidth(true);
        scrollPane.setFitToHeight(true);
        mainpane.setCenter(scrollPane);
        mainpane.setTop(hBox);

        Scene scene = new Scene(mainpane,1000,800);
        primaryStage.setScene(scene);
        primaryStage.show();



        btnConnect.setOnAction(event -> {
            String url = "https://"+textField.getText();
            webEngine=webView.getEngine();
            webEngine.load(url);
            System.out.println("连接成功");
        });

        btnHome.setOnAction(event -> {
            webEngine.load("https://www.gdufs.edu.cn");
        });

        btnBefore.setOnAction(event -> {
            try {
                webHistory.go(-1);
            } catch (IndexOutOfBoundsException e) {
                e.printStackTrace();
            }
        });
        btnAfter.setOnAction(event -> {
            try {
                webHistory.go(1);
            } catch (IndexOutOfBoundsException e) {
                e.printStackTrace();
            }
        });

        webEngine.getLoadWorker().stateProperty().addListener(
                (observable,oldState,newState)->{
                    if(newState == State.SUCCEEDED){
                        primaryStage.setTitle(webEngine.getTitle());
                        textField.setText(webEngine.getLocation());
                    }
                });

    }
}

七、项目结构

在这里插入图片描述

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GCTTTTTT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值