目錄
一、簡介:HTTP程式設計
1、HTTP系統設計
2、HTTP客戶端作業程序
3、HTTP服務端作業程序
二、基于TCP Socket的HTTP網頁下載
三、基于SSL Socket的HTTPS網頁下載
四、HTTP客戶端完整代碼
五、界面完整代碼
六、最后+演示
一、簡介:HTTP程式設計
期末復習之HTTP網路編程,主要學習記錄HTTP(s)協議的網路編程,包括使用TCP Socket進行三次握手的HTTP網頁下載,和使用SSL Socket的安全傳輸的HTTPs網頁下載,通過案例實踐自行完成編程,認識http(s)的實際作業機制!
現在的HTTP客戶端比早期的復雜得多,不僅包括了網頁檔案下載和顯示,還有許多新的功能:跨平臺的顯示、引數的傳遞、動態網頁的實作和用戶互動等,
1、HTTP系統設計
- 客戶端軟體(web瀏覽器:Chrome、360瀏覽器等)
- 服務端軟體(web服務器:微軟的IIS、Apache Tomcat)
2、HTTP客戶端作業程序
- 客戶端軟體和服務器建立連接(TCP的三次握手);
- 發送HTTP頭格式協議;
- 接收網頁檔案;
- 顯示網頁,
3、HTTP服務端作業程序
- 服務器軟體開啟80埠;
- 回應客戶的要求、完成TCP連接;
- 檢查客戶端的HTTP頭格式發送客戶請求的網頁檔案(含動態網頁),

圖1 HTTP請求-回應完整程序
網頁下載技術是搜索引擎、網路爬蟲、網頁采集器或網路推送服務等相關應用領域內的基礎技術,下面會介紹日常使用到的兩種協議(http和https)的網頁訪問下載,
二、基于TCP Socket的HTTP網頁下載
對于TCP套接字的連接程序已經有很深刻的認識了,在本地測驗通信也使用過TCP的Socket建立連接,同理,與HTTP服務器建立連接,也是利用TCP進行資訊互動的,
建立連接之后,需要發送HTTP請求頭,服務器確認請求者,開啟兩端的通信,客戶端可以接收網頁檔案資訊,進而經過渲染后顯示網頁頁面,這里我們先實作接收網頁檔案資訊,在下一篇實作瀏覽器對網頁渲染之后的功能,

以www.baidu.com為例,與HTTP服務器建立連接之后,需要我們發送網頁請求,也就是HTTP請求頭,構造請求頭如下:
GET / HTTP/1.1
HOST: www.baidu.com
Accept: */*
Accept-Language: zh-cn
User-Agent: User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Connection: Keep-Alive
需要嚴格按照格式發送,并且通常用StringBuffer類的toString()方法可將完整的HTTP請求頭轉換為字串,一致發送到HTTP服務器,
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"
);
換行符使用\r\n是為了避免由于編碼問題出錯,
發送請求之后如果網頁資訊顯示區回傳的第一條資訊是“HTTP/1.1 200 OK”,則說明訪問正常,

可以看到HTTP服務器回傳許多資訊,這也是回應頭,包含了許多關鍵資訊內容,
三、基于SSL Socket的HTTPS網頁下載
以上面設計的基于TCP通信傳輸的HTTP,我們嘗試訪問www.sina.com.cn,結果發現回應頭資訊第一行是HTTP/1.1 302 Moved Temporarily(站點被移除),出于安全考慮,現在絕大部分的web站點都將放棄HTTP而啟用HTTPS,都使用了安全加密傳輸的HTTPS協議,而關閉了HTTP,只允許啟用了SSL/TLS的HTTPS安全連接,這種連接默認是使用443埠,所以TCP Socket建立連接的方式無正常訪問網頁,
那只是埠改為443能正常嗎,答案如下,

原因在前面也能看出,需要使用SSL/TLS的HTTPS安全連接,來建立與HTTPS服務器的通信,因此需要修改Socket型別,
這里使用到了Java安全套接字擴展(Java Secure Socket Extension,JSSE),基于SSL和TLS協議的Java網路應用程式提供了Java API以及參考實作,這里使用其客戶端的SSLSocket套接字,SSLSocket相對之前學習的客戶端套接字,只是創建方法不同,SSLSocket物件由SSLSocketFactory創建,
在類中宣告成員變數以及創建Socket連接:
private SSLSocket socket;
private SSLSocketFactory factory;
factory=(SSLSocketFactory)SSLSocketFactory.getDefault();
socket=(SSLSocket)factory.createSocket(ip,Integer.parseInt(port));
對SSL Socket的使用與TCP相同,只是創建方法不同,經過稍微修改之后,可以成功請求HTTPS網站的網頁資訊,

四、HTTP客戶端完整代碼
這里給出HTTP客戶端的完整代碼,HTTPS只需改改上述講到的SSL Socket,
/*
* 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();
}
}
}
五、界面完整代碼
我直接用一個圖形界面來訪問http和https,融合以上兩個圖形客戶端的功能,使得該圖形客戶端既能訪問443的https內容,也可以訪問非443埠(一般是80)的http內容,
/*
* 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);
}
}
六、最后+演示
HTTP連接www.baidu.com,成功

HTTP連接www.sina.com.cn,失敗

HTTPS連接www.sina.com.cn,成功

期末復習,順便寫博客記錄下來,這篇為上篇,介紹HTTP網頁請求下載,主要是HTTP(s)協議的網路編程,包括使用TCP Socket進行三次握手的HTTP網頁下載,和使用SSL Socket的安全傳輸的HTTPs網頁下載,通過案例實踐自行完成編程,認識http(s)的實際作業機制!
期待:Java之HTTP網路編程(下篇:網頁瀏覽器程式設計),將看到網頁的HTML源代碼,以及經過瀏覽器功能渲染之后的網頁!
如果覺得不錯歡迎“一鍵三連”哦,點贊收藏關注,有問題直接評論,交流學習!
我的CSDN博客:https://blog.csdn.net/Charzous/article/details/111470556
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/239177.html
標籤:其他
