1.网络基础知识
- 通信协议的三大组成部分:
- 语义部分:决定双方对话的类型;
- 语法部分:决定双方对话的格式;
- 变换规则:决定通信双方的应答关系;
- 国际标准化组织ISO在1978年提出开放互联参考模型OSI;
- TCP/IP模型替代了OSI模型,TCP/IP模型包括:应用层、传输层、网络层、物理+数据链路层;
2.IP地址和端口号
- IP地址用于唯一标识网络中的一个通信实体(主机、路由器端口);
- IP地址是32位数字,为了方便记忆,将其用二进制分为4个部分,再转换为十进制:192.168.1.1;
- 全球IP地址由NIC负责统一规划,APNIC负责亚太地区的IP管理;
- 一个通信实体可能有多个通信程序进行,为了区分通信程序,设置了端口,每一个端口执行不同的通信程序;
- 公认端口:绑定特定服务,0-1023;
- 注册端口:应用程序的普遍选择,1024-49151;
- 动态或私有端口:应用程序使用的动态端口,一般不会主动使用这些端口,49152-65535;
3.Java网络基本支持
1.使用InetAddress类
- InetAddress类可以代表IP地址,Inet4Address和Inet6Address分别代表IPV4和IPV6;
- InetAddress的基本用法(Page783)
- 程序演示:
import java.net.*;
public class InetAddressTest
{
public static void main(String[] args)
throws Exception
{
//根据域名获得网站IP地址;
InetAddress ip = InetAddress.getByName("www.crazyit.org");
//判断网站能否访问
System.out.println("网站是否可以访问?" + ip.isReachable(2000));
//输出IP字符串
System.out.println(ip.getHostAddress());
//根据原始的IP地址获取对应的InetAddress实例
InetAddress local = InetAddress.getByAddress(
new byte[] {127, 0, 0, 1});
System.out.println("判断本机是否可达" + local.isReachable(5000));
//获取此IP地址对应的域名;
System.out.println(local.getCanonicalHostName());
}
}
2.使用URLDecoder和URLEncoder类
- URLDecoder负责普通字符串的转换工作;
- URLEncoder负责application/x-www-form-urlencoded MIME字符串(非西欧字符)的转换工作;
- URLDecoder的decode方法将application/x-www-form-urlencoded MIME字符串转换成普通字符串;
- URLEncoder的encode方法将普通字符串转换为application/x-www-form-urlencoded MIME字符串;
- 演示方法:
import java.net.*;
public class URLDecoderTest
{
public static void main(String[] args)
throws Exception
{
// 将application/x-www-form-urlencoded字符串转换成普通字符串
String keyWord = URLDecoder.decode(
"%E7%96%AF%E7%8B%82java", "utf-8");
System.out.println(keyWord);
//将普通字符串转换为application/x-www-form-urlencoded字符串
String urlStr = URLEncoder.encode(
"疯狂讲义", "GBK");
System.out.println(urlStr);
}
}
3.URL、URLConnection和URLPermission
- URL类
- URL类用于创建URL对象,URL对象代表了一种统一资源定位器,它是指向互联网资源的“指针”;URL由协议名、主机、端口号和资源组成;
- URL类的方法(page785)
- 程序演示:
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.*;
public class DownUtil
{
//定义下载资源的路径;
private String path;
//定义下载文件的保存路径
private String targetFile;
//定义需要的线程数量
private int threadNum;
//定义下载的线程对象;
private DownThread[] threads;
//定义下载的文件总大小;
private int fileSize;
//初始化DownUtil类
public DownUtil(String path, String targetFile, int threadNum)
{
this.path = path;
this.threadNum = threadNum;
//初始化threads数组
threads = new DownThread[threadNum];
this.targetFile = targetFile;
}
public void download() throws Exception
{
var url = new URL(path); //获得URL对象
var conn = (HttpURLConnection) url.openConnection(); //返回一个与URL所引用的远程对象的连接;
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
conn.setRequestProperty(
"Accept",
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
+ "application/x-shockwave-flash, application/xaml+xml, "
+ "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
+ "application/x-ms-application, application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, application/msword, */*");
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;
var file = new RandomAccessFile(targetFile, "rw");
//设置本地文件大小
file.setLength(fileSize);
file.close();
for (var i = 0; i < threadNum; i++)
{
//计算每个线程开始下载的位置
var startPos = i * currentPartSize;
//每个线程使用一个RAF进行下载
var currentPart = new RandomAccessFile(targetFile, "rw");
//定义该线程的下载位置
currentPart.seek(startPos);
//创建下载线程
threads[i] = new DownThread(startPos, currentPartSize, currentPart);
// 启动下载线程
threads[i].start();
}
}
// 获取下载的完成百分比
public double getCompleteRate()
{
// 统计多个线程已经下载的总大小
var sumSize = 0;
for (var i = 0; i < threadNum; i++)
{
sumSize += threads[i].length;
}
//返回完成百分比
return sumSize * 1.0 / fileSize;
}
private class DownThread extends Thread
{
// 当前线程的下载位置
private int startPos;
// 定义当前线程负责下载的文件大小
private int currentPartSize;
// 当前线程需要下载的文件块
private RandomAccessFile currentPart;
// 定义当前线程已下载的字节数
public int length;
public DownThread(int startPos, int currentPartSize,
RandomAccessFile currentPart)
{
this.startPos = startPos;
this.currentPartSize = currentPartSize;
this.currentPart = currentPart;
}
@Override
public void run()
{
try
{
var url = new URL(path);
var conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
conn.setRequestProperty(
"Accept",
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
+ "application/x-shockwave-flash, application/xaml+xml, "
+ "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
+ "application/x-ms-application, application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, application/msword, */*");
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Charset", "UTF-8");
InputStream inStream = conn.getInputStream();
inStream.skip(this.startPos);
var buffer = new byte[1024];
var hasRead = 0;
while (length < currentPartSize
&& (hasRead = inStream.read(buffer)) != -1)
{
currentPart.write(buffer, 0, hasRead);
length += hasRead;
}
currentPart.close();
inStream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
2.URLConnection对象表示应用程序和URL之间的通信连接,程序可以通过URLConnection实现向该URL发送请求、读取URL引用的资源;HTTPURLConnection对象表示应用程序与URL的HTTP连接;
3.URLPermission对象负责管理URLConnection对象的权限问题;
- 创建一个和URL的连接,并发送请求,读取URL资源需要如下几个步骤:
- 通过调用URL对象的openConnection()方法来创建URLConnection对象;
- 设置URLConnection的参数和普通请求属性;
- 如果只是发送GET方式请求,则使用connect()方法建立和远程资源之间的实际连接;如果需要发送POST方式的请求,则需要获取URLConnection示例对应的输出流来发送请求参数
- 远程资源变为可用,程序可以访问远程资源的头字段或通过输入流读取远程资源的数据;
- 程序演示:
import java.io.*;
import java.net.*;
import java.util.*;
public class GetTest
{
public static String sendGet(String url, String param)
{
String result = "";
String urlName = url + "?" + param;
try
{
var realUrl = new URL(urlName);
//创建和URL的连接对象
URLConnection conn = realUrl.openConnection();
//设置连接参数
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
//启动连接
conn.connect();
// 获取所有响应的头字段
Map<String, List<String>> map = conn.getHeaderFields();
// 遍历响应头字段
for (var key : map.keySet())
{
System.out.println(key + "--->" + map.get(key));
}
try (
//定义BufferedReader来读取URL响应
var in = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "utf-8")))
{
String line;
while ((line = in.readLine())!= null)
{
result += "\n" + line;
}
}
}
catch (Exception e)
{
System.out.println("发送GET请求出现异常" + e);
e.printStackTrace();
}
return result;
}