Programación de redes HTTP en Java (Parte I: descarga de la página web TCP / SSL)

Tabla de contenido

1. Introducción: programación HTTP

1. Diseño del sistema HTTP

2. Proceso de trabajo del cliente HTTP

3. El proceso de trabajo del servidor HTTP

2. Descarga de página web HTTP basada en TCP Socket

3. Descarga de la página web HTTPS basada en SSL Socket

Cuarto, el código completo del cliente HTTP.

Cinco, el código completo de la interfaz.

Seis, final + presentación


1. Introducción: programación HTTP

Programación de red HTTP para la revisión al final del trimestre, principalmente aprendiendo a registrar la programación de red del protocolo HTTP (s), incluida la descarga de la página web HTTP usando TCP Socket para el protocolo de enlace de tres vías, y la descarga de la página web HTTPs usando Transmisión segura de SSL Socket, y completa la programación a través de la práctica del caso. ¡Comprende el mecanismo de trabajo real de http (s)!

El cliente HTTP actual es mucho más complicado que los anteriores, no solo incluye la descarga y visualización de archivos de páginas web, sino también muchas funciones nuevas: visualización multiplataforma, transferencia de parámetros, realización de páginas web dinámicas e interacción con el usuario.

1. Diseño del sistema HTTP

  • Software de cliente (navegador web: Chrome, navegador 360, etc.)
  • Software de servidor (servidor web: Microsoft IIS, Apache Tomcat)

2. Proceso de trabajo del cliente HTTP

  • El software del cliente y el servidor establecen una conexión ( protocolo de enlace de tres vías TCP );
  • Enviar protocolo de formato de encabezado HTTP ;
  • Reciba archivos web;
  • Muestra la página web.

3. El proceso de trabajo del servidor HTTP

  • El software del servidor abre el puerto 80 ;
  • Responder a los requisitos del cliente y completar la conexión TCP ;
  • Verifique el formato de encabezado HTTP del cliente para enviar el archivo de la página web (incluida la página web dinámica) solicitado por el cliente. 

Figura 1 Proceso completo de solicitud-respuesta HTTP

La tecnología de descarga de páginas web es una tecnología básica en campos de aplicaciones relacionados, como motores de búsqueda, rastreadores web, recopiladores de páginas web o servicios web push. A continuación, se presentarán dos protocolos (http y https) que se utilizan a diario para acceder y descargar páginas web. .

2. Descarga de página web HTTP basada en TCP Socket

Tengo un profundo conocimiento del proceso de conexión de los sockets TCP. También he usado TCP Socket para establecer una conexión en la comunicación de prueba local. De manera similar, establecer una conexión con un servidor HTTP también usa TCP para el intercambio de información.

Una vez establecida la conexión, se debe enviar el encabezado de la solicitud HTTP, el servidor confirma al solicitante y se abre la comunicación entre los dos extremos. El cliente puede recibir la información del archivo de la página web y luego mostrar la página web después de la renderización. Aquí primero implementamos la recepción de información del archivo de la página web e implementamos la función del navegador después de renderizar la página web en el siguiente artículo.

Tomando www.baidu.com como ejemplo, después de establecer una conexión con el servidor HTTP, necesitamos enviar una solicitud de página web, que es el encabezado de la solicitud HTTP. Construya el encabezado de la solicitud de la siguiente manera:

GET /  HTTP / 1.1  

ANFITRIÓN:  www.baidu.com

Aceptar: * / *

Aceptar-Idioma: zh-cn

Usuario-Agente:  Usuario-Agente: Mozilla / 5.0 (Windows NT 10.0; Win64; x64)

Conexión: Keep-Alive

 Debe enviarse estrictamente de acuerdo con el formato, y el método toString () de la clase StringBuffer se usa generalmente para convertir el encabezado de solicitud HTTP completo en una cadena y enviarlo al servidor HTTP de manera consistente.

StringBuffer msg = new StringBuffer();
msg.append("GET / HTTP/1.1\r\n"+
            "HOST: "+domainName+"\r\n"+
            "Accept: */*\r\n"+
            "Accept-Language: zh-CN\r\n"+
            "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n"+
            "Connection: Keep-Alive\r\n"
);

 El uso de \ r \ n para los saltos de línea es para evitar errores debido a problemas de codificación.

Después de enviar la solicitud, si el primer mensaje devuelto en el área de visualización de información de la página web es "HTTP / 1.1 200 OK" , significa que el acceso es normal.

Puede ver que el servidor HTTP devuelve mucha información, que también es el encabezado de respuesta, que contiene mucha información clave.

3. Descarga de la página web HTTPS basada en SSL Socket

Basado en el HTTP diseñado por encima de la basada en la comunicación TCP, tratamos de visitar www.sina.com.cn, y encontramos que la primera línea de la información de cabecera de respuesta fue HTTP / 1.1 302 trasladó temporalmente (el sitio se eliminó). Para razones de seguridad, ahora La gran mayoría de los sitios web abandonarán HTTP y habilitarán HTTPS, utilizando el protocolo HTTPS para una transmisión cifrada segura y desactivando HTTP, solo permitirán conexiones seguras HTTPS con SSL / TLS habilitado. Esta conexión usa el puerto 443 de forma predeterminada. Por lo tanto, la forma en que TCP Socket establece una conexión no accede a la página web normalmente.

¿Es normal cambiar el puerto a 443? La respuesta es la siguiente.

El motivo también se puede ver en lo anterior, la necesidad de utilizar una conexión segura SSL / TLS HTTPS para establecer comunicación con el servidor HTTPS, por lo que es necesario modificar el tipo de Socket.

Aquí se utiliza Java Secure Socket Extension (JSSE). Las aplicaciones web Java basadas en los protocolos SSL y TLS proporcionan implementaciones de referencia y API de Java. Aquí se utiliza el socket SSLSocket del cliente. En comparación con los sockets de cliente aprendidos anteriormente, SSLSocket solo se crea de una manera diferente. El objeto SSLSocket es creado por SSLSocketFactory.

Declare las variables miembro en la clase y cree una conexión Socket:

private SSLSocket socket;
private SSLSocketFactory factory;


factory=(SSLSocketFactory)SSLSocketFactory.getDefault();
socket=(SSLSocket)factory.createSocket(ip,Integer.parseInt(port));

 El uso de SSL Socket es el mismo que el de TCP, pero el método de creación es diferente. Después de una pequeña modificación, puede solicitar con éxito la información de la página web del sitio web HTTPS.

Cuarto, el código completo del cliente HTTP.

Aquí está el código completo del cliente HTTP, HTTPS solo necesita cambiar el Socket SSL mencionado anteriormente.

/*
 * HTTPClient.java
 * Copyright (c) 2020-12-21
 * author : Charzous
 * All right reserved.
 */

package chapter08;

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

public class HTTPClient {
    private Socket socket;

    private PrintWriter pw;
    private BufferedReader br;
    /**
     * @param ip
     * @param port
     * @return 
     * @author Charzous
     * @date 2020/12/21 14:52
     *
     */
    public HTTPClient(String ip, String port) throws IOException{
        //主动向服务器发起连接,实现TCP三次握手
        //不成功则抛出错误,由调用者处理错误
        socket =new Socket(ip,Integer.parseInt(port));

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

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

    }

    public void send(String msg) throws InterruptedException {
        //输出字符流,由socket调用系统底层函数,经网卡发送字节流
        try {
            Thread.sleep(500);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        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.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

}

Cinco, el código completo de la interfaz.

Utilizo directamente una interfaz gráfica para acceder a http y https, e integro las funciones de los dos clientes gráficos anteriores, de modo que el cliente gráfico pueda acceder tanto al contenido https del 443 como al contenido http del puerto no 443 (generalmente 80) .

/*
 * HTTPAllClientFX.java
 * Copyright (c) 2020-12-21
 * author : Charzous
 * All right reserved.
 */

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;


public class HTTPAllClientFX extends Application {

    private Button btnExit=new Button("退出");
    private Button btnSend = new Button("网页请求");

//    private TextField tfSend=new TextField();//输入信息区域

    private TextArea taDisplay=new TextArea();//显示区域
    private TextField ipAddress=new TextField();//填写ip地址
    private TextField tfport=new TextField();//填写端口
    private Button btConn=new Button("连接");
    private HTTPSClient httpsClient;
    private HTTPClient httpClient;
    private Thread readThread;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        BorderPane mainPane=new BorderPane();

        //连接服务器区域
        HBox hBox1=new HBox();
        hBox1.setSpacing(10);
        hBox1.setPadding(new Insets(10,20,10,20));
        hBox1.setAlignment(Pos.CENTER);
        hBox1.getChildren().addAll(new Label("网页地址:"),ipAddress,new Label("端口:"),tfport,btConn);
        mainPane.setTop(hBox1);

        VBox vBox=new VBox();
        vBox.setSpacing(10);

        vBox.setPadding(new Insets(10,20,10,20));
        vBox.getChildren().addAll(new Label("网页信息显示区"),taDisplay);

        VBox.setVgrow(taDisplay, Priority.ALWAYS);
        mainPane.setCenter(vBox);


        HBox hBox=new HBox();
        hBox.setSpacing(10);
        hBox.setPadding(new Insets(10,20,10,20));
        hBox.setAlignment(Pos.CENTER_RIGHT);
        hBox.getChildren().addAll(btnSend,btnExit);
        mainPane.setBottom(hBox);

        Scene scene =new Scene(mainPane,700,500);
        primaryStage.setScene(scene);
        primaryStage.show();



        //连接按钮
        btConn.setOnAction(event -> {
            String ip=ipAddress.getText().trim();
            String port=tfport.getText().trim();
            taDisplay.clear();

            try {
                if (port.equals("443")){
                    httpsClient = new HTTPSClient(ip, port);
                    //成功连接服务器,接受服务器发来的第一条欢迎信息
                    taDisplay.appendText("服务器连接成功。\n");

                    readThread = new Thread(()->{
                        String receiveMsg=null;//从服务器接收一串字符
                        if (port.equals("443")){
                            while ((receiveMsg=httpsClient.receive())!=null){
                                //lambda表达式不能直接访问外部非final类型局部变量,需要定义一个临时变量
                                //若将receiveMsg定义为类成员变量,则无需临时变量
                                String msgTemp = receiveMsg;
                                Platform.runLater(()->{
                                    taDisplay.appendText(msgTemp+"\n");
                                });
                            }
                        }
                    });
                    readThread.start();
                }

                else if (port.equals("80")){
                    httpClient = new HTTPClient(ip, port);
                    taDisplay.appendText("服务器连接成功。\n");
                    readThread = new Thread(()-> {
                        String receiveMsg = null;
                        while ((receiveMsg = httpClient.receive()) != null) {
                            String msgTemp = receiveMsg;
                            Platform.runLater(() -> {
                                taDisplay.appendText(msgTemp + "\n");
                            });
                        }
                    });
                    readThread.start();
                }


            }catch (Exception e){
                taDisplay.appendText("服务器连接失败!"+e.getMessage()+"\n");
            }
        });

        //网页请求按钮事件
        btnSend.setOnAction(event -> {
            String ip=ipAddress.getText().trim();
            String port=tfport.getText().trim();
            String domainName=ipAddress.getText().trim();
            try {
                StringBuffer msg = new StringBuffer();
                msg.append("GET / HTTP/1.1\r\n"+
                                "HOST: "+domainName+"\r\n"+
                                "Accept: */*\r\n"+
                                "Accept-Language: zh-CN\r\n"+
                                "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n"+
                                "Connection: Keep-Alive\r\n"
                        );
                if (port.equals("443"))
                    httpsClient.send(msg.toString());
                else if (port.equals("80"))
                    httpClient.send(msg.toString());

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


        });


        btnExit.setOnAction(event -> {
            try {
                exit();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //窗体关闭响应的事件,点击右上角的×关闭,客户端也关闭
        primaryStage.setOnCloseRequest(event -> {
            try {
                exit();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    private void exit() throws InterruptedException {
        if (httpsClient!=null||httpClient!=null){
            readThread.sleep(1000);//多线程等待,关闭窗口时还有线程等待IO,设置1s间隔保证所有线程已关闭
            httpsClient.close();
            httpClient.close();
        }
        System.exit(0);
    }

}

Seis, final + presentación

Conexión HTTP a www.baidu.com, exitosa

Error de conexión HTTP a www.sina.com.cn

Conexión HTTPS a www.sina.com.cn, exitosa

Por cierto, revisa al final del trimestre y escribe un blog. Este es el primero. Presenta la descarga de solicitudes de página web HTTP, principalmente la programación de red del protocolo HTTP (s), incluida la descarga de la página web HTTP mediante TCP Socket para el protocolo de enlace de tres vías y la seguridad de usar SSL Socket. Descargue la página web HTTP transmitida, complete la programación a través de la práctica del caso y comprenda el mecanismo de funcionamiento real de http (s).

Esperando: Programación de red Java HTTP (Parte II: Programación del navegador web), verá el código fuente HTML de la página web y la página web representada por la función del navegador.

Si cree que es bueno, bienvenido a "un clic, tres vínculos", haga clic en Me gusta, marque como favorito, siga, comente directamente si tiene alguna pregunta, e intercambie y aprenda.


Mi blog de CSDN: https://blog.csdn.net/Charzous/article/details/111470556

Supongo que te gusta

Origin blog.csdn.net/Charzous/article/details/111470556
Recomendado
Clasificación