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.mzph.cn/news/530877.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java相遇问题_行程问题

行程问题 《行程问题》说课设计——现代教育信息技术与数学学科的整合福建省闽侯县尚干中心小学 林惠贞 邮编&#xff1a;350112 邮箱:zhenzi2277163.com众所周知,未来的教育&#xff0c;倡导开放式学习&#xff0c;把学习的地点扩展到社会、网络&…

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"}),(小颖:朋友圈{姓名:"…

继承易错总结

1.继承会将所有的成员继承下来&#xff0c;但是继承方式限定的是继承下来成员的可见类型(如果是private继承&#xff0c;那么他不论哪里都是不可见的&#xff1b;如果是protected继承在类中是可见的&#xff0c;在类外是不可见的&#xff1b;如果是public继承&#xff0c;在任何…

hhkb适合写java吗_起底这届HHKB最强新品键盘,究竟好在哪儿?

2019年12月HHKB上市了3大品类的12款新品键盘&#xff0c;今天为大家分享外设天下为HHKB Professional HYBIRD Type-S 双模静音旗舰版静电容键盘做的评测&#xff0c;起底这届HHKB新品的最强新品。近日&#xff0c;HHKB更新了旗下的在售产品系列&#xff0c;为了满足严肃、安静办…

elementui树形复选框,element-ui checkbox 组件的树形联动

前言示例版本为 Element-ui 2.13.0 Vue 2.6.11最近想弄 Element-ui checkbox 的多级联动&#xff0c;网上相关的例子大多数为二级联动&#xff0c;自己研究了一下&#xff0c;弄了一个树形菜单的多级联动&#xff0c;常用于角色管理等业务。(仅供参考&#xff0c;未考虑性能问…

java 先入先出,java_阻塞队列(FIFO先进先出)

java_阻塞队列(FIFO先进先出)ArrayBlockingQueue&#xff1a;由数组结构组成的有界阻塞队列&#xff1b;LinkedBlockingQueue&#xff1a;由链表结构组成的有界阻塞队列(但大小默认值为&#xff1a;Integer.MAX_VALUE)&#xff1b;PriorityBlockingQueue&#xff1a;支持优先级…

php 复选框全选和取消,基于JavaScript实现复选框的全选和取消全选

这篇文章主要为大家详细介绍了基于JavaScript实现复选框的全选和取消全选&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下本文实例为大家分享了js复选框的全选和取消全选的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下效果图&#xff1a;…

javascript date php date,JavaScript Date 知识浅析

Date函数new Date()Date 对象会自动把当前日期和时间保存为其初始值。date.getDate()从 Date 对象返回一个月中的某一天 (1 ~ 31)。date.getDay()从 Date 对象返回一周中的某一天 (0 ~ 6)。周日是0。date.getMonth()从 Date 对象返回月份 (0 ~ 11)。date.getFullYear()从 Date …

16字节 oracle md5,Oracle中的MD5加密

因为要用到MD5加密&#xff0c;所以在网上搜了一下相关资料&#xff0c;并进行仔细研究。其核心就是MD5编码的数据包函数&#xff1a;DBMS_OBFUSCATION_TOOLKIT.M因为要用到MD5加密&#xff0c;所以在网上搜了一下相关资料&#xff0c;并进行仔细研究。其核心就是MD5编码的数据…

link linux 跨设备,Linux中的两种link方式

Linux系统中包括两种链接方式&#xff1a;硬链接(hard link)和符号链接(symbolic link)&#xff0c;其中符合链接就是所谓的软链接(soft link)&#xff0c;那么两者之间到底有什么区别呢&#xff1f;inode在Linux系统中&#xff0c;内核为每一个新创建的文件分配一个inode&…

linux设置超链接,帮助-链接 - Linux Kernel Newbies

this page is outdated and needs to be fixed参考链接形式语法备注内部链接WikiNameCamelCase page name内部自由链接["Page"] or ["free link"]可配置函数内部子页面链接/SubPage or ["/Sub page"]相对于上一级页面外部链接http://example.net…

linux 文件系统cache,终于找到一篇详解Linux文件系统Cache的文章

级别&#xff1a; 初级2006 年 5 月 11 日文件 Cache 管理是 Linux 内核中一个很重要并且较难理解的组成部分。本文详细介绍了 Linux内核中文件 Cache 管理的各个方面&#xff0c;希望能够对开发者理解相关代码有所帮助。自从诞生以来&#xff0c;Linux 就被不断完善和普及&…

七桥问题c语言程序数据结构,数据结构与算法学习——图论

什么是图&#xff1f;在计算机程序设计中&#xff0c;图结构也是一种非常常见的数据结构但是图论其实是一个非常大的话题图结构是一种与树结构有些相似的数据结构图论是数学的一个分支&#xff0c;并且在数学概念上&#xff0c;树是图的一种它以图为研究对象&#xff0c;研究顶…

c语言式表白,c语言表白必备

c语言表白必备七夕情人节表白必备&#xff0c;多颜色心形展示看图#include#include#include#include #define r 10#define R 172int main(){int i;printf("我");fflush(stdout); //强制刷新缓存&#xff0c;输出显示Sleep(1000);printf("自");fflush(stdou…

android中gradle的作用,Gradle 之 Android 中的应用

在上一篇文章中 Gradle 之语言基础 Groovy 主要介绍了 Groovy 的基础语法(如果没有 Groovy 的基础&#xff0c;建议先看看上篇文章&#xff0c;如果可以动手敲一下里面的示例代码就更好不过了)&#xff0c;也是为本篇文章打基础的。本篇文章主要介绍 Gradle 在 Android 中的应用…

自己写的android apk反编译,获取Android自己写好了的apk以及反编译

今天&#xff0c;我们先说一下&#xff0c;获取Android自带的apk以及反编译它们来学习Android工程师是怎样写的&#xff0c;今天我们就以拿到Android自带的短信管理器的apk为例子你可能有疑问&#xff0c;为什么要那么麻烦&#xff0c;从系统来拿&#xff0c;还要反编译&#x…

一加7pro系统更新android10,一加OnePlus7T Pro官方安卓10.0稳定版出厂系统固件升级更新包...

咱们的这个一加OnePlus7T Pro手机的最新稳定版系统包也是在这里来分享一下了&#xff0c;这个稳定版本的系统包是安卓10稳定版的&#xff0c;也是第一个版本的&#xff0c;系统包大小是3.2G&#xff0c;系统方面主要是全新的UI设计&#xff0c;轻快流畅操作体验&#xff0c;更多…

5元素升级android6,升级你的app以支持高长宽比的新旗舰

为了呈现更好的视觉效果&#xff0c;许多安卓OEM厂商都开始采用超大屏幕。三星刚刚发布了自己的新旗舰Samsung Galaxy S8&#xff0c;长宽比达到18.5:9。今年早些时候的全球移动大会上LG也亮相了 LG G6&#xff0c;屏幕长宽比达到了18:9。(左) maximum aspect ratio为16:9的app…

CCS太阳光准直系统使用积分球均匀光源

CCS太阳光准直系统的应用范围广泛&#xff0c;包括太阳光辐射测量、光学遥感仪器研制与标定、均匀光源的推广使用等方面。通过使用CCS太阳光准直系统&#xff0c;可以准确地模拟太阳光&#xff0c;并对各种光学仪器进行校准和标定&#xff0c;从而提高测量精度和稳定性。 CCS太…