java socket編程客戶端_Java Socket編程 - 基于Socket實現HTTP下載客戶端

沒有借助任何第三方庫,完全基于JAVA Socket實現一個最小化的HTTP文件下載客

戶端。完整的演示如何通過Socket實現下載文件的HTTP請求(request header)發送

如何從Socket中接受HTTP響應(Response header, Response body)報文并解析與

保存文件內容。如何通過SwingWork實現UI刷新,實時顯示下載進度。

首先看一下UI部分:

0818b9ca8b590ca3270a3433284dd417.png

【添加下載】按鈕:

點擊彈出URL輸入框,用戶Copy要下載文件URL到輸入框以后,點擊[OK]按鈕即開始

下載

0818b9ca8b590ca3270a3433284dd417.png

【清除完成】按鈕:

清除所有已經下載完成的文件列表

文件下載狀態分為以下幾種:

package com.gloomyfish.socket.tutorial.http.download;

public enum DownLoadStatus {

NOT_STARTED,

IN_PROCESS,

COMPLETED,

ERROR

}

UI部分主要是利用Swing組件完成。點擊【添加下載】執行的代碼如下:

final JDialog dialog = new JDialog(this,"Add File Link",true);

dialog.getContentPane().setLayout(new BorderLayout());

// dialog.setSize(new Dimension(400,200));

final URLFilePanel panel = new URLFilePanel();

panel.setUpListener(new ActionListener(){

@Override

public void actionPerformed(ActionEvent e) {

if("OK".equals(e.getActionCommand())){

if(panel.validateInput()) {

DownloadDetailStatusInfoModel data = new DownloadDetailStatusInfoModel(panel.getValidFileURL());

tableModel.getData().add(data);

startDownlaod();

refreshUI();

}

dialog.setVisible(false);

dialog.dispose();

} else if("Cancel".equals(e.getActionCommand())) {

dialog.setVisible(false);

dialog.dispose();

}

}});

dialog.getContentPane().add(panel, BorderLayout.CENTER);

dialog.pack();

centre(dialog);

dialog.setVisible(true);

【清除完成】按鈕執行的代碼如下:

private void clearDownloaded() {

List downloadedList = new ArrayList();

for(DownloadDetailStatusInfoModel fileStatus : tableModel.getData()) {

if(fileStatus.getStatus().toString().equals(DownLoadStatus.COMPLETED.toString())) {

downloadedList.add(fileStatus);

}

}

tableModel.getData().removeAll(downloadedList);

refreshUI();

}

讓JFrame組件居中顯示的代碼如下:

public static void centre(Window w) {

Dimension us = w.getSize();

Dimension them = Toolkit.getDefaultToolkit().getScreenSize();

int newX = (them.width - us.width) / 2;

int newY = (them.height - us.height) / 2;

w.setLocation(newX, newY);

}

HTTP協議實現部分:

概述:HTTP請求頭與相應頭報文基本結構與解釋

HTTP請求:一個標準的HTTP請求報文如

0818b9ca8b590ca3270a3433284dd417.png

其中請求頭可以有多個,message-body可以沒有,不是必須的。請求行的格式如下:

Request-Line = Method SP Request-URI SPHTTP-Version CRLF 舉例說明如下:

Request-Line = GET http://www.w3.org/pub/WWW/TheProject.htmlHTTP/1.1\r\n

其中SP表示空格, CRLF表示回車換行符\r\n

當你想要上傳文件時候,使用Post方式來填寫數據到message-body中即可。發送一個

簡單的HTTP請求報文如下:

GET /pub/WWW/TheProject.html HTTP/1.1\r\n

\r\n

HTTP響應:一個標準的HTTP響應報文如下

0818b9ca8b590ca3270a3433284dd417.png

最先得到是狀態行,其格式如下:

Status-Line = HTTP-Version SP Status-CodeSP Reason-Phrase CRLF, 一個狀態行的

簡單例子如下:Status-Line = HTTP/1.1 200 OK一般大家最喜歡的就是Status-Code會

給你很多提示,最常見的就是404,500等狀態碼。狀態碼的意思可以參考RFC2616中

的解釋。下載文件最要緊是的檢查HTTP響應頭中的Content-Length與Content-Type兩

個中分別聲明了文件的長度與文件的類型。其它如Accept-Ranges表示接受多少到多少

的字節。可能在多線程下載中使用。搞清楚了HTTP請求與響應的報文格式以后,我們

就可以通過Socket按照報文格式解析內容,發送與讀取HTTP請求與響應。具體步驟

如下:

一:根據用戶輸入的文件URL建立Socket連接

URL url = new URL(fileInfo.getFileURL());

String host = url.getHost();

int port = (url.getPort() == -1) ? url.getDefaultPort():url.getPort();

System.out.println("Host Name = " + host);

System.out.println("port = " + port);

System.out.println("File URI = " + url.getFile());

// create socket and start to construct the request line

Socket socket = new Socket();

SocketAddress address = new InetSocketAddress(host, port);

socket.connect(address);

用了URL類來把用戶輸入的url string變成容易解析一點的URL。

二:構造HTTP請求

BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF8"));

String requestStr = "GET " + url.getFile() + " HTTP/1.1\r\n"; // request line

// construct the request header - 構造HTTP請求頭(request header)

String hostHeader = "Host: " + host + "\r\n";

String acceptHeader = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";

String charsetHeader = "Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3\r\n";

String languageHeader = "Accept-Language: zh-CN,zh;q=0.8\r\n";

String keepHeader = "Connection: close\r\n";

三:發送HTTP請求

// 發送HTTP請求

bufferedWriter.write(requestStr);

bufferedWriter.write(hostHeader);

bufferedWriter.write(acceptHeader);

bufferedWriter.write(charsetHeader);

bufferedWriter.write(languageHeader);

bufferedWriter.write(keepHeader);

bufferedWriter.write("\r\n"); // 請求頭信息發送結束標志

bufferedWriter.flush();

四:接受HTTP響應并解析內容,寫入創建好的文件

// 準備接受HTTP響應頭并解析

CustomDataInputStream input = new CustomDataInputStream(socket.getInputStream());

File myFile = new File(fileInfo.getStoreLocation() + File.separator + fileInfo.getFileName());

String content = null;

HttpResponseHeaderParser responseHeader = new HttpResponseHeaderParser();

BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(myFile));

boolean hasData = false;

while((content = input.readHttpResponseHeaderLine()) != null) {

System.out.println("response header contect -->> " + content);

responseHeader.addResponseHeaderLine(content);

if(content.length() == 0) {

hasData = true;

}

if(hasData) {

int totalBytes = responseHeader.getFileLength();

if(totalBytes == 0) break; // no response body and data

int offset = 0;

byte[] myData = null;

if(totalBytes >= 2048) {

myData = new byte[2048];

} else {

myData = new byte[totalBytes];

}

int numOfBytes = 0;

while((numOfBytes = input.read(myData, 0, myData.length)) > 0 && offset < totalBytes) {

offset += numOfBytes;

float p = ((float)offset) / ((float)totalBytes) * 100.0f;

if(offset > totalBytes) {

numOfBytes = numOfBytes + totalBytes - offset;

p = 100.0f;

}

output.write(myData, 0, numOfBytes);

updateStatus(p);

}

hasData = false;

break;

}

}

簡單的HTTP響應頭解析類HttpResponseHeaderParser代碼如下:

package com.gloomyfish.socket.tutorial.http.download;

import java.util.HashMap;

import java.util.Map;

/**

* it can parse entity header, response head

* and response line

* refer to RFC2616,關于HTTP響應頭,請看RFC文檔,描寫的很詳細啊!!

*

* @author fish

*

*/

public class HttpResponseHeaderParser {

public final static String CONTENT_LENGTH = "Content-Length";

public final static String CONTENT_TYPE = "Content-Type";

public final static String ACCEPT_RANGES = "Accetp-Ranges";

private Map headerMap;

public HttpResponseHeaderParser() {

headerMap = new HashMap();

}

/**

*

get the response header key value pair

* @param responseHeaderLine

*/

public void addResponseHeaderLine(String responseHeaderLine) {

if(responseHeaderLine.contains(":")) {

String[] keyValue = responseHeaderLine.split(": ");

if(keyValue[0].equalsIgnoreCase(CONTENT_LENGTH)) {

headerMap.put(CONTENT_LENGTH, keyValue[1]);

} else if(keyValue[0].equalsIgnoreCase(CONTENT_TYPE)) {

headerMap.put(CONTENT_TYPE, keyValue[1]);

} else {

headerMap.put(keyValue[0], keyValue[1]);

}

}

}

public int getFileLength() {

if(headerMap.get(CONTENT_LENGTH) == null){

return 0;

}

return Integer.parseInt(headerMap.get(CONTENT_LENGTH));

}

public String getFileType() {

return headerMap.get(CONTENT_TYPE);

}

public Map getAllHeaders() {

return headerMap;

}

}

可執行的Jar文件下載地址(這次我要點分):

http://download.csdn.net/detail/jia20003/4862076

轉載請務必注明

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/530877.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/530877.shtml
英文地址,請注明出處:http://en.pswp.cn/news/530877.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java讀c二進制文件_如何使用JAVA讀取C / Matlab創建的二進制文件

小編典典正如我所猜測的那樣&#xff0c;這是一個字節序問題&#xff0c;即您的二進制文件被寫為低字節序的整數(可能是因為您使用的是Intel或類似的CPU)。但是&#xff0c;無論Java代碼運行在哪個CPU上&#xff0c;它都在讀取大端整數。為了顯示該問題&#xff0c;以下代碼將讀…

java 通聯支付接口_allinpay 通聯支付接口實例

【實例簡介】allinpay 支付的實例代碼&#xff0c;這只是部分&#xff0c;需要其它的請聯系我。 幫忙找【實例截圖】【核心代碼】201708081652114811└── unionorder_demo├── java│ └── uniondemo│ ├── WebContent│ │ ├── META-INF│ │ │ └…

java web tcp長連接超時時間_常用java web容器http長連接超時設置

1.http長連接相關知識http長連接對我們來說并不陌生&#xff0c;但長連接并不是永遠不會關閉。對于HTTP長連接需要注意下面幾點&#xff1a;keepalive_timeout指的是web服務器發送完最后一個響應報文后&#xff0c;開始計時&#xff0c;如果在keepalive_timeout指定的時間內還沒…

java相遇問題_行程問題

行程問題 《行程問題》說課設計——現代教育信息技術與數學學科的整合福建省閩侯縣尚干中心小學 林惠貞 郵編&#xff1a;350112 郵箱:zhenzi2277163.com眾所周知,未來的教育&#xff0c;倡導開放式學習&#xff0c;把學習的地點擴展到社會、網絡&…

jbutton 數組創建 java_java-將JButton數組添加到JPanel(按鈕不可見)

我正在嘗試使用Java創建一個簡單的計算器.為此,我創建了一個JButton數組并將其添加到JPanel中.問題&#xff1a;按鈕不可見.我還添加了一個JLabel和一個JButton進行測試,它們可以正確顯示.編碼&#xff1a;package test;import java.awt.BorderLayout;import javax.swing.*;pub…

jwt java 項目實例_JWT(JsonWebToken)+SpringMVC項目demo

【實例簡介】JSON Web Token(JWT)是一個非常輕巧的規范。現在免費給大家分享一個JWT(JsonWebToken)SpringMVC項目的demo!【實例截圖】【核心代碼】jwt-demo└── jwt-demo├── pom.xml├── src│ └── main│ ├── java│ │ └── com│ │ └── hthl…

java寫一個99到0_Java中一個普通的循環為何從10開始到99連續相乘會得到0?

【套裝4本】java編程思想4第4版402.5元包郵(需用券)去購買 >這是一塊非常簡單的Java代碼片段&#xff1a;public class HelloWorld{public static void main(String []args){int product 1;for (int i 10; i < 99; i) {product * i;}System.out.println(product);}}為什…

neo4j java查找_Spring-Boot使用neo4j-java-driver-- 查找兩個節點之間關系的最短路徑

一、Cypher數據create (小北:朋友圈{姓名:"小北", 喜歡的書類:"Poetry"}),(小菲:朋友圈{姓名:"小菲", 喜歡的書類:"Science Fiction"}),(小鵬:朋友圈{姓名:"小鵬", 喜歡的書類:"Music"}),(小穎:朋友圈{姓名:"…

java 返回兩個集合_使用 java stream 獲取兩個集合之間的交集.差集

原始對象List hrProbationMainList new ArrayList();List hrProbationMains probationMainDao.findAll();獲取交集對象stream 轉換成流 (只有變成流才能操作)filter 封裝判斷條件(如果true 則收集到結果中,false則不收集)collect 收集結果返回到指定類型中//交集對象ListhrPro…

java重寫重定向_JavaWeb請求轉發與請求重定向理解

請求轉發使用方法httpServletRequest.getRequestDispatcher("login.jsp").forward(httpServletRequest, servletresponse);此請求過程 完全是一次request對象 完成的。我們可以用 filter 截取用戶的這次請求&#xff0c;然后利用這次請求 訪問其它頁面&#xff0c;當…

autojs調用java識字_autojs實現抽象類的繼承

作者: 牙叔使用情景在java中, 抽象類必須繼承才能使用, 那么在autojs中怎樣實現繼承抽象類呢?java中的實現創建一個抽象類package com.yashu;public abstract class Employee{ private String name; private String address; private int number; public abstract double com…

java wix_使用WIX升級MSI

我正在使用WIX創建一個MSI安裝程序 . 一切都很好&#xff0c;我能夠創建自己的產品并創造一切 .但是&#xff0c;當我想要創建升級時&#xff0c;它無法正常工作 .我使用了以下代碼&#xff0c;其中$(var.ProductUpgradeCode)被定義并用作product元素中的upgrade-code .Minimum…

繼承易錯總結

1.繼承會將所有的成員繼承下來&#xff0c;但是繼承方式限定的是繼承下來成員的可見類型(如果是private繼承&#xff0c;那么他不論哪里都是不可見的&#xff1b;如果是protected繼承在類中是可見的&#xff0c;在類外是不可見的&#xff1b;如果是public繼承&#xff0c;在任何…

flat在java中的含義_Java 9中Collectors.flatMapping()方法的重要性?

在Java 9中&#xff0c;向Collectors類添加了一個新方法&#xff1a;flatMapping()。它類似于Collectors.mapping()方法&#xff0c;其中flatMapping()方法允許我們處理嵌套的集合。 Collectors.flatMapping()方法需要的功能將被施加到輸入元件和集電極積累通過功能傳遞的元件。…

hhkb適合寫java嗎_起底這屆HHKB最強新品鍵盤,究竟好在哪兒?

2019年12月HHKB上市了3大品類的12款新品鍵盤&#xff0c;今天為大家分享外設天下為HHKB Professional HYBIRD Type-S 雙模靜音旗艦版靜電容鍵盤做的評測&#xff0c;起底這屆HHKB新品的最強新品。近日&#xff0c;HHKB更新了旗下的在售產品系列&#xff0c;為了滿足嚴肅、安靜辦…

java暫停5s_java如何實現繼續/暫停按鈕

匿名用戶1級2016-11-24 回答很簡單&#xff0c;我給你寫一個Demo&#xff1a;import java.awt.event.*;import javax.swing.*;public class Demo extends Thread {private JFrame frm new JFrame("Demo");private JButton btnPause new JButton("Pause")…

c mysql數據庫_C實現MySQL數據庫操作

C實現MySQL數據庫操作兩個月前&#xff0c;也就是9月份&#xff0c;心血來潮在windows下實現了用C連接數據庫&#xff0c;當時很是有把linux下一并給實現了。但是沒有想得那么簡單。這個任務一直推遲到了現在才完成&#xff0c;究其原因&#xff1a;1.我直接用eclipse開發工具&…

php生成不重復時間戳,PHP獲取時間戳和微秒數以及生成唯一ID

microtime函數描述&#xff1a;返回當前Unix時間戳和微秒數語法&#xff1a;mixed microtime( [ bool $get_as_float ] )//直接輸出echo microtime();//得到的是 如&#xff1a;0.26672100 1585622985 前面是當前微秒數&#xff0c;后面是正常時間戳&#xff0c;中間以空格分隔…

php異步處理任務工具,PHP實現異步任務分發處理利器-Gearman

通常&#xff0c;多語言多系統之間的集成是個大問題&#xff0c;一般來說&#xff0c;人們多半會采用WebService的方式來處理此類集成問題&#xff0c;但不管采用何 種風格的WebService&#xff0c;如RPC風格&#xff0c;或者REST風格&#xff0c;其本身都有一定的復雜性。相比…

java dos窗口小工具下載,maxdos 9 3-maxdos工具箱 v9.3 官方版

maxdos 9 3maxdos工具箱是一個dos工具箱&#xff0c;maxdos工具箱為安裝好的電腦系統加入dos&#xff0c;方便用戶維護與備份還原&#xff0c;是完全傻瓜化的工作模式&#xff0c;更是可以直接啟動硬盤上的光盤ISO鏡像一鍵啟動。maxdos工具箱特色說明&#xff1a;1、支持進入DO…