java核心学习(二十九) 基本网络支持
一、InetAddress
java提供了InetAddress代表IP地址,它有两个子类Inet4Address和Inet6Address,分别代表了IPV4和IPV6地址。
这个类没有构造器,而是使用两个静态工厂方法来获得InetAddress实例
getByName(String host);
getByAddress(byte[] addr);
它的四个主要实例方法
String getCanonicalHostName();取得该IP地址的全限定域名
String getHostAddress();取得IP地址
String getHostName();取得该IP地址的主机名
Boolean isReachable();判断该IP地址是否可达,可以传入一个等待时长作为参数
二、URLDecoder 和 URLEncoder
这两个类用于完成普通字符串与application/x-www-form-urlencoded MIME 字符串之间的相互转换。
什么是application/x-www-form-urlencoded MIME 字符串?
当URL地址里包含非西欧字符的字符串时,系统会将这些非西欧字符转化为特殊的字符串,所以后台接受到的URL需要经过解码转换为普通字符串,这就用到了URLDecoder;当程序发送请求时,需要使用URLEncoder将URL普通字符串编码转换为特殊字符串。
解码和编码分别使用这两个类的静态方法decode(String s , String enc) 、encode(String s ,String enc)来完成。enc为字符集。
三、URL、URLConnection和URLPermission
这三个应该属于应用层的API

下面实现一个多线程下载的工具类来试用一下这个API
参考 HTTP请求头详解
package net;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class DownUtil {
private String path; //下载资源的路径
private String targetFile; //
private int threadNum;
private DownThread[] threads;
private int filesize;
private final String infoFileSuffix = "INI.txt";
public DownUtil(String path, String targetFile, int threadNum) {
this.path = path;
this.targetFile = targetFile;
this.threadNum = threadNum;
}
public DownUtil(String path, String targetFile) {
this.path = path;
this.targetFile = targetFile;
this.threadNum = Runtime.getRuntime().availableProcessors();//默认开启当前机器CPU数量的线程
}
public void download() throws Exception{
//建立http请求取得文件大小
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept","*/*");
conn.setRequestProperty("Accept-Language","zh-CN");
conn.setRequestProperty("Charset","UTF-8");
conn.setRequestProperty("Connection","Keep-Alive");
filesize = conn.getContentLength(); //取得文件大小
conn.disconnect();
//将下载任务分配给每个线程
int currentPartSize = filesize / threadNum + 1; //每个线程负责的文件块的大小
Path path = Paths.get(targetFile+infoFileSuffix);
if(Files.exists(path)){
//读取文件内容继续下载
List<String> allLine = Files.readAllLines(path, Charset.forName("UTF-8"));
for(int i = 0 ; i < threadNum ; i++){
int startPos = i * currentPartSize;
RandomAccessFile currentPart = new RandomAccessFile(targetFile,"rw");
currentPart.seek(startPos);
threads[i] = new DownThread(startPos,currentPartSize,currentPart,Integer.parseInt(allLine.get(i).toString()));
threads[i].start();
}
}else {
RandomAccessFile file = new RandomAccessFile(targetFile,"rw");
file.setLength(filesize);
file.close();
List<String> infoStrings = new ArrayList<>();
for(int i = 0 ; i < threadNum ; i++){
infoStrings.add("0");
}
Files.write(path,infoStrings,Charset.forName("UTF-8"));
for(int i = 0 ; i < threadNum ; i++){
int startPos = i * currentPartSize;
RandomAccessFile currentPart = new RandomAccessFile(targetFile,"rw");
currentPart.seek(startPos);
threads[i] = new DownThread(startPos,currentPartSize,currentPart);
threads[i].start();
}
//重新写入文件
}
}
private class DownThread extends Thread{
private int startPos; //当前线程开始下载的位置
private int currentPartSize; //当前线程负责下载的大小
private RandomAccessFile currntPart; //当前线程需要下载的文件类
public int length; //已下载的字节数,如果要实现断点下载,可以吧这个数据存在硬盘上,即存在文件里
private final String acceptString = "*/*";
private final String charSetString = "UTF-8";
private final String acceptLanguageString = "zh-CN";
public DownThread(int startPos, int currentPartSize, RandomAccessFile currntPart,int length) {
this.startPos = startPos;
this.currentPartSize = currentPartSize;
this.currntPart = currntPart;
this.length = length;
}
public DownThread(int startPos, int currentPartSize, RandomAccessFile currrntPart){
this.startPos = startPos;
this.currentPartSize = currentPartSize;
this.currntPart = currrntPart;
this.length = 0;
}
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept",acceptString);
conn.setRequestProperty("Charset",charSetString);
conn.setRequestProperty("Accept-Language",acceptLanguageString);
conn.setRequestProperty("Range","bytes=" + (startPos + length) + "-" + (startPos + currentPartSize - 1));
InputStream inputStream = conn.getInputStream();
byte[] buffer = new byte[1024];
int hasRead = 0; //一次循环读取的字节数
while (length < currentPartSize && (hasRead = inputStream.read(buffer)) != -1){
currntPart.write(buffer,0,hasRead);
length += hasRead;
}
currntPart.close();
inputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
以上代码还可以增加断点续传下载功能,这段代码基本上展示了URL和URLconniction(抽象类)这两个类的用法
而URLPermission则是java8新增的工具类,用于管理HttpURLConniction的权限问题,如果不重复造轮子基本用不到,即是要用到也在用到的时候查看文档即可(java的类太多了)。

浙公网安备 33010602011771号