Java Socket编程 - 基于Socket实现HTTP下载客户端

简介: Java Socket编程 - 基于Socket实现HTTP下载客户端

没有借助任何第三方库,完全基于JAVA Socket实现一个最小化的HTTP文件下载客


户端。完整的演示如何通过Socket实现下载文件的HTTP请求(request header)发送


如何从Socket中接受HTTP响应(Response header, Response body)报文并解析与


保存文件内容。如何通过SwingWork实现UI刷新,实时显示下载进度。


首先看一下UI部分:


1354957740_6179.png

【添加下载】按钮:


点击弹出URL输入框,用户Copy要下载文件URL到输入框以后,点击[OK]按钮即开始


下载


1354957768_8405.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<DownloadDetailStatusInfoModel> downloadedList = new ArrayList<DownloadDetailStatusInfoModel>();
  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请求报文如

1354959957_1059.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


Host: www.w3.org\r\n

HTTP响应:一个标准的HTTP响应报文如下

1354960020_3451.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连接

1.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 <status code, CharSet, ect...>
 * 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<String, String> headerMap;
  public HttpResponseHeaderParser() {
    headerMap = new HashMap<String, String>();
  }
  /**
   * <p> get the response header key value pair </p>
   * @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<String, String> getAllHeaders() {
    return headerMap;
  }
 
}

可执行的Jar文件下载地址(这次我要点分):

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

转载请务必注明

相关文章
|
10月前
|
JSON 中间件 Go
Go 网络编程:HTTP服务与客户端开发
Go 语言的 `net/http` 包功能强大,可快速构建高并发 HTTP 服务。本文从创建简单 HTTP 服务入手,逐步讲解请求与响应对象、URL 参数处理、自定义路由、JSON 接口、静态文件服务、中间件编写及 HTTPS 配置等内容。通过示例代码展示如何使用 `http.HandleFunc`、`http.ServeMux`、`http.Client` 等工具实现常见功能,帮助开发者掌握构建高效 Web 应用的核心技能。
497 61
|
11月前
|
人工智能 Java API
MCP客户端调用看这一篇就够了(Java版)
本文详细介绍了MCP(Model Context Protocol)客户端的开发方法,包括在没有MCP时的痛点、MCP的作用以及如何通过Spring-AI框架和原生SDK调用MCP服务。文章首先分析了MCP协议的必要性,接着分别讲解了Spring-AI框架和自研SDK的使用方式,涵盖配置LLM接口、工具注入、动态封装工具等步骤,并提供了代码示例。此外,还记录了开发过程中遇到的问题及解决办法,如版本冲突、服务连接超时等。最后,文章探讨了框架与原生SDK的选择,认为框架适合快速构建应用,而原生SDK更适合平台级开发,强调了两者结合使用的价值。
13696 33
MCP客户端调用看这一篇就够了(Java版)
|
10月前
|
C# 图形学 开发者
Unity开发中使用UnityWebRequest从HTTP服务器下载资源。
总之,UnityWebRequest就是游戏开发者手中的万能钓鱼竿,既可以获取文本数据,也能钓上图片资源,甚至是那声音的涟漪。使用UnityWebRequest的时候,你需要精心准备,比如确定URL、配置请求类型和头信息;发起请求;巧妙处理钓获的数据;还需要机智面对网络波澜,处理各种可能出现的错误。按照这样的过程,数据的钓取将会是一次既轻松愉快也效率高效的编程钓鱼之旅。
571 18
|
8月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
11月前
|
存储 网络协议 Java
Java获取客户端IP问题:返回127.0.0.1
总结:要解决Java获取客户端IP返回127.0.0.1的问题,首先要找出原因,再采取合适的解决方案。请参考上述方案来改进代码,确保在各种网络环境下都能正确获取客户端IP地址。希望本文对您有所帮助。
693 25
|
10月前
|
存储 缓存 前端开发
http协议调试代理工具,Fiddler免费版下载,抓包工具使用教程
Fiddler是一款功能强大的HTTP协议调试代理工具,能记录并检查电脑与互联网间的HTTP通信,支持断点设置和数据编辑。相比其他网络调试器,Fiddler操作更简单且用户友好,支持查看Cookie、HTML、JS、CSS等文件内容。它还具备HTTPS抓包、过滤设置、统计页面总重量等功能,适用于安全测试与功能测试。通过插件扩展,用户可自定义视图或分析缓存行为。支持多种HTTP请求方法(如GET、POST等)及状态码分类(1xx-5xx),是开发者调试网络请求的得力工具。同类工具有HttpWatch、Firebug、Wireshark等。
2151 1
|
9月前
|
存储 人工智能 Java
java之通过Http下载文件
本文介绍了使用Java实现通过文件链接下载文件到本地的方法,主要涉及URL、HttpURLConnection及输入输出流的操作。
670 0
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
1266 29
|
JSON Java 数据格式
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
342 25
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
|
存储 Java API
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
1103 4