本节内容
1.OSI文件模型介绍
2.HTTP请求和响应报文结构
3.IP地址、DNS和端口号
4.使用URLConnection获取网络数据
5.实现下载文件
6.多线程下载获取文件
7.多线程下载结构分析
8.分配线程下载任务
9.实现进度监听
一、OSI文件模型介绍
1.网络中传递数据的过程:客户端(发起请求)——>本机(对请求的数据进行包装)——>(TCP/IP协议进行传输)网络中传输——>服务器主机(通过端口号访问具体进程)——>进程
2.OSI的七层网络数据模型
-
(1)应用层:为操作系统或网络程序提供访问网络服务器的接口。使用URLConnection这个接口或者socket发起网络申请
-
(2)表示层:提供数据格式转换服务,加密和解密,图片解码和编码,数据压缩和解压
-
(3)会话层:建立一个端到端的链接,并提供访问验证和会话管理
-
(4)传输层:提供应用进程之间的逻辑通信,建立连接,数据校验,数据包次序,如TCP,UDP,进程,端口
-
(5)网络层:为数据在节点之间的传输创建逻辑链路,并分组转发数据
-
(6)数据链路层:在通信实体间建立数据链路连接,如数据分帧,处理流控制,物理地址寻址,重发
-
(7)物理层:负责最后将信息编码成电流脉冲或者其他信号用于网上传输
3.OSI的四层网络数据模型
-
(1)应用层:HTTP,FTP,DNS,SMTP
-
(2)传输层:通过TCP,UDP协议进行传输
-
(3)网络层:数据包装,寻址路由IP、PHP、ICMP协议
-
(4)物理层
4.html与服务器建立联系的过程:html 通过操作系统提供的接口发起请求——>将请求的数据使用TCP/IP协议进行包装——>网络层进行数据分包——>链路中——>物理层——————————>网络————————>主机(物理层—>链路层—>网络层—>传输层—>应用层)
二、HTTP请求和响应报文结构
1.Http和TCP/UDP 的关系:
-
HTTP协议 :主要负责数据的具体传输,对数据进行封装
-
TCP/UDP协议 :负责将数据在网络中传输
2.请求Request和响应Response
-
请求:
-
客户端需要向服务器上传数据
-
客户端需要从服务器下载数据
-
响应:
-
服务器端对客户端的请求作出的回应
3.三次握手建立连接
-
HTTP封装完数据后,需要将数据使用TCP协议向网络中的其他主机(服务器)进行发送,需要经过三个过程/三次握手
4.Http协议报文
-
请求方式:
-
GET:请求数据(1.提交数据 2.接收返回的数据)提交的数据会在url地址中显示表示出来(大小有限制)
-
POST:请求数据(1.提交数据 2.接收返回的数据)提交的数据在请求体重 url地址不会出现(有文件就必须使用POST)
-
HEAD:只是获取服务器端返回的响应信息,不会获取具体的内容,先获取大小,再分配线程
-
状态码:
-
200-206 请求成功
-
-
400-405 客户端错误
-
500-505 服务器端错误
-
请求头:请求的方式、地址,上传的参数 。请求体:提交的是什么里面就是什么。
-
响应头:回调给我们的数据,包含一些状态码和一些资源信息。响应体:包括具体的资源。
三、IP地址、DNS和端口号
1.IP地址:就是用来唯一标识网络中的一台设备(192.10.121这种类型)
3.DNS:域名解析器。可以通过域名解析出它的IP地址
-
http:表示数据传输使用的具体协议
-
127.0.0.1:访问的主机地址
-
login.php:表示访问主机的文件或者目录
-
?(分隔符):表示需要向服务器提交数据,提交方式GET。后面就是具体提交的数据。
-
user_name=:表示提交的数据,user_name是服务器定义的字段,jack是字段对应的数据
-
使用&来链接多个字段
5.端口号
-
端口对应的是一种服务
-
80端口对应的是网络服务
-
公认端口:0-1023 一些特定的服务
-
注册端口:1024-49151 应用程序使用该范围端口
-
动态私有端口:49152-65535
四、使用URLConnection获取网络数据
1.Java使用URL来封装网络数据的地址
2.url地址里面不能包含中文或者其他一些特殊的字符,对于这些字符需要进行编码或者解码
使用方法如下
public class MyClass {
public static void main(String[] args) throws Exception {
String str ="http://127.0.0.1/login.php?user_name=jack&user_pwd=123&
submit=%E7%99%BB%E5%BD%95";
URL url =new URL(str);
String word= URLDecoder.decode("%E7%99%BB%E5%BD%95","utf-8");
String code=URLEncoder.encode("登录","utf-8");
System.out.println(code);
System.out.println(word);
}
}
3.通过以下方式获取主机地址
System.out.println(url.getHost());
4.通过以下方式获取使用的协议
System.out.println(url.getProtocol());
5.通过以下方式获取具体提交的数据
System.out.println(url.getQuery());
6.向网络中发起请求
String str ="http://127.0.0.1/login.php?user_name=jack&user_pwd=123";
URL url =new URL(str);
HttpURLConnection coon=(HttpURLConnection)url.openConnection();
coon.setRequestMethod("GET");
coon.setDoInput(true);/设置是否打开接收的流
coon.setDoOutput(true);/设置是否打开输出流,POST必须打开
coon.setConnectTimeout(5*1000);/链接请求时间
coon.setUseCaches(false);/不需要缓存
/url.openStream()从服务器端获取数据
/coon.getOutputStream();向服务器端发送数据
InputStream is= url.openStream();
byte[] buffer= new byte[1024];
int len=0;
while((len=is.read(buffer))!=-1){
String content= new String(buffer,0,len);
System.out.println(content);
}
is.close();
五、实现下载文件
1.新建一个URL对象
URL url=new URL("http://127.0.0.1/upload/vedio/test.mp4");
2.连接
HttpURLConnection coon=(HttpURLConnection)url.openConnection();
3.获取输入流
InputStream inputStream=url.openStream();
BufferedInputStream bis=new BufferedInputStream(inputStream);
4.创建输出流
String des= "C:\\Users\\86178\\Desktop\\gg.mp4";
FileOutputStream fos=new FileOutputStream(des);
BufferedOutputStream bos= new BufferedOutputStream(fos);
5.将数据写入磁盘
byte [] buffer= new byte[1024];
int len=0;
while ((len=bis.read(buffer))!=-1){
bos.write(buffer,0,len);
}
bos.close();
fos.close();
bis.close();
inputStream.close();
六、多线程下载获取文件
1.创建一个DownloadManager类,并采用单例设计模式
public class DownloadManager {
private Map<String,String>[] source;
private static DownloadManager manager;
private DownloadManager(){}
private static Object obj=new Object();
public static DownloadManager getInstance(){
if(manager==null){
synchronized (obj){
if (manager==null){
manager=new DownloadManager();
}
}
}
return manager;
}
public void loadData(String urlString,String filePath){
DownOperation downloader= new DownOperation(urlString,filePath,3);
downloader.download();
}
}
2.创建一个DownOperation类,创建它的构造方法,并提供获取文件大小以及链接数据的函数
public class DownOperation {
private URL url;
private String filePath;
private int threadCount;
private DownThread [] tasks;
private long size;
public DownOperation(String urlString,String filePath, int threadCount) {
try {
this.url=new URL(URLEncoder.encode(urlString,"utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
this.filePath = filePath;
this.threadCount = threadCount;
tasks= new DownThread[threadCount];
}
public void download(){
//获取文件的大小
getFileSize();
System.out.println(size);
}
private void getFileSize(){
//获取连接
HttpURLConnection coon=null;
try {
coon=(HttpURLConnection) url.openConnection();
coon.setRequestMethod("HEAD");
//获取资源大小
size= coon.getContentLengthLong();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭链接
coon.disconnect();
}
}
}
3.在主函数中,直接通过DownloadManager实现文件的下载
public class MyClass {
public static void main(String[] args) {
String url="http\\127.0.0.1\\upload\\video\\test.mp4";
String filePath="C:\\Users\\86178\\Desktop";
//下载一个文件
DownloadManager.getInstance().loadData(url,filePath);
}
}
七、多线程下载结构分析
1.首先需要知道文件大小->知道下载线程数量->准备文件接收数据->通过RandomAccessFile访问数据
八、分配线程下载任务
1.对前面的代码进行改写,在download函数里面添加一些内容
public void download(){
//获取文件的大小
getFileSize();
//创建文件,用于保存下载的数据
createFile();
//分配线程下载数据
dispatch();
}
2.分别实现后面两个函数,这一切都在DownOperation类里面
private void dispatch(){
//计算每个线程下载的平均值
long average=size/threadCount;
long start =0;
long donwloadsize=average;
for(int i=0;i<threadCount;i++){
start=i*average;
//最后一个线程,可能下载的数量要大于平均值
if(i==threadCount-1){
donwloadsize=size-start;
}
//创建线程,执行对应的模块进行下载
DownThread dt =new DownThread(url,filePath,start,donwloadsize);
//添加保存线程对象
tasks[i]=dt;
//启动下载
dt.start();
}
}
private void createFile(){
File file =new File(filePath);
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//设置文件大小
RandomAccessFile rac=null;
try {
rac= new RandomAccessFile(file,"rw");
rac.setLength(size);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
rac.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.创建一个DownThread类,在它的run方法里面真正实现文件的多线程下载
public class DownThread extends Thread {
private URL url;
private String filePath;
private long startPosition;
private long size;
private long downloadedLength;
public DownThread (URL url,String filePath,long startPosition,long size){
this.url=url;
this.filePath=filePath;
this.startPosition=startPosition;
this.size=size;
}
@Override
public void run() {
//定位文件到这个线程应该写入的位置
try {
RandomAccessFile raf=new RandomAccessFile(filePath,"rw");
raf.seek(startPosition);
//开始下载
HttpURLConnection coon=(HttpURLConnection) url.openConnection();
coon.setRequestMethod("GET");
coon.setDoInput(true);
coon.setConnectTimeout(5*1000);
//获取输入流
InputStream is= url.openStream();
//设置数据读取位置
is.skip(startPosition);
//开始读取数据,写入到文件
byte[] buffer=new byte[1024];
int len=0;
while ((len=is.read(buffer))!=-1){
raf.write(buffer,0,len);
//记录当前下载长度
downloadedLength+=len;
//判断当前线程下载的范围是否结束
if(downloadedLength>size){
break;
}
}
is.close();
coon.disconnect();
raf.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
其他的和第六的代码一样
九、实现进度监听
1.在DownOperation类里面添加一个currentRate方法来记录当前的下载进度
public float currentRate(){
long len=0;
for(DownThread dt:tasks){
len=dt.downloadedLength;
}
return (float)len/size;
}
2.在DownloadManager类里面的loadData函数里面,new 一个Thread对象,然后实现runnable接口
public void loadData(String urlString,String filePath){
final DownOperation downloader= new DownOperation(urlString,filePath,3);
downloader.download();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
float rate=downloader.currentRate();
if(rate<1.0000001) {
System.out.println("当前下载比例:" + downloader.currentRate());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}).start();
}
OK,以上就是今天的全部内容了,再见。
网友评论