内容
1.HTML⽹⻚和Apache服务器后台程序(PHP)如何进行交互
2.OSI七层模型(了解)
3.HTTP与TCP
4.请求(Request)和响应(Response)
5.三次握手建立连接
6.IP地址与DNS
7.端口号
8.URL、URLEncoder和URLDecoder
9.URL类的简单使用
10.URL类下载图片/视频
11.多线程下载器项目(难点)
一.HTML⽹⻚和Apache服务器后台程序(PHP)如何进行交互
①我们在浏览器中输⼊⽹⻚的地址,比如127.0.0.1/login.html,进入网页
②⽹⻚中可以向服务器提交数据form表单
③服务器端使⽤对应的⽅式接收表单中的数据,比如 _GET["name"] 或_POST["name"]
④服务器使⽤echo返回给⽹⻚数据
二.OSI七层模型(了解)
1.应用层
网络服务与最终用户的一个接口。
协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP
2. 表示层
数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层)
格式有,JPEG、ASCll、EBCDIC、加密格式等
3. 会话层
建立、管理、终止会话。(在五层模型里面已经合并到了应用层)
对应主机进程,指本地主机与远程主机正在进行的会话
4. 传输层
定义传输数据的协议端口号,以及流控和差错校验。
协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层
5. 网络层
进行逻辑地址寻址,实现不同网络之间的路径选择。
协议有:ICMP IGMP IP(IPV4 IPV6)
6. 数据链路层
建立逻辑连接、进行硬件地址寻址、差错校验 等功能。(由底层网络定义协议)
将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。
7. 物理层
建立、维护、断开物理连接。(由底层网络定义协议)
三.HTTP与TCP
1.HTTP协议:
是Hyper Text Transfer Protocol,即超文本传输协议,是一个基于请求与响应模式、无状态的网络协议,是浏览器和服务器间最常用的通讯协议。
HTTP协议包括两部分,请求协议和响应协议
2.URL与URI:
URL:Uniform Resource Locator,即统一资源定位符(可以理解成一个网址就是一个URL,但是URL不一定就是网址),是互联网上标准资源的地址,可以在全球范围内唯一确定的一个资源。
URI:Uniform Resource Identifier,即统一资源标识符,用于标识一个资源的名称。通过这种名称命名的资源可以被互联网定位和访问
3.HTTP和TCP关系
①客户端若要向服务器端发出请求,必须首先在它们之间建立一个TCP(Transfer Control Protocal,即传输控制协议)连接,当客户端与服务器端的通讯结束后,TCP连接将被关闭,而这个连接就是基于HTTP协议的。
②HTTP1.0在客户端接收到服务器端发送来的响应后,TCP马上关闭,而HTTP1.1不同,客户端在发送创建TCP连接请求之前首先计算出本次连接中,浏览器所要发送的请求数量,即一次手工请求加上其所携带的所有自动请求数量,当所有浏览器所发出的请求全部发送完毕后,客户端会自动发送一个关闭TCP连接的请求,这个请求在HTTPWatch中是看不到的。为了防止服务器主动将TCP连接关闭,在每一个请求中都携带了一个参数Connection,用于告诉服务器是否关闭连接,在HTTPWatch中可以看到的这些请求中,其Connection参数值均为Keep-Alive保持连接,只有当客户端发送了关闭TCP连接请求(Connection为close)时,服务器才会将TCP连接关闭
四.请求(Request)和响应(Response)
1.注意:
请求和响应是成对出现的
2.请求(Request):(一般是浏览器发出)
(1)何时发出请求?
一般情况下,当
①客户端需要向服务器 上传数据
②客户端需要从服务器 下载数据
时,浏览器就会向服务器端发出请求,在这些请求中,有用户手动提出的请求,也有浏览器自动发出的请求
(2)常用的请求方式:GET和POST
(3)GET请求
由于GET请求会将请求所携带的参数作为URL中的一部分出现,所以请求参数会显示在地址栏,这就导致了GET提交的三点不足
①参数值只能是字符串,而不能是其他类型
②可以携带的数据量小
③数据安全性低
但GET请求有一个很重要的特征:客户端一旦接收到“服务器向GET请求发送的响应”后,浏览器会自动缓存响应,当客户再次进行相同请求的提交时,将直接读取本地浏览器缓存中的数据,而不再向服务器端真正发送数据,让用户感觉服务器端的响应很快,提升用户体验,减轻了服务器压力。
(4)POST请求
POST请求会将请求所携带的数据以请求正文的形式出现(而不是在URL中),所以与GET相比,就显示出了几点长处:
①数据类型可以是任意类型,还可以是声音、视频、图片等文件
②请求可以携带的数据量大(理论上没有上限)
③数据安全性高
但发出POST请求的客户端浏览器不会对接收到的“服务器向POST请求发送的响应”进行缓存,所以当用户再次进行相同请求时,仍是真正向服务器发送的请求,从服务器读取的数据
(5)为何GET和POST不都有缓存?
主要原因是:以不同的方式提交请求,其目的也是不同的
Ⅰ:GET(意为得到)请求的目的一般是客户端要从服务器端下载资源,发送相同的请求就代表要下载相同的资源。既然要下载相同的资源,而这些资源已经被下载到了客户端,那么就无需再下载了,所以也就无需再向服务器发送真正的下载请求了。所以就将GET提交方式设计为了“GET请求的响应结果会被浏览器缓存”。
Ⅱ:但POST(意为上传)请求的目的一般是客户端要向服务器端上传资源,对于向服务器端上传资源后的响应结果,浏览器是无需缓存的(缓存了没有意义)
(6)GET和POST如何进行选择
①当提交时所携带的数据类型不是字符串(比如文件、文本、音频、对象等等)时
②提交时所携带的数据量比较大
③提交时所携带的数据具有敏感性,安全性要求较高时
都用POST,其余情况均用GET
3.响应Response:
响应即服务器端对客户端的请求作出的回应
4.抓包工具:
如果想要查看地址栏所发出的请求详情,那么我们可以通过抓包工具来拦截HTTP请求与响应,从工具中可以看到具体的请求与响应内容,抓包工具很多,最常用的是HttpWatch
5.HEAD
HEAD:只是获取服务器端返回的响应信息 ,不会获取具体的内容
6.状态码:
①2xx:表示对请求计算与响应成功,其中常用的状态码是200
②4xx:表示请求错误。其中常见的状态码是404,表示资源找不到,一般都是请求路径书写有问题。
③5xx:表示服务器端错误,其中常见的状态码是500,表示服务器内部有错误,一般都是服务器端的Java代码发生错误
具体例子
200-206 请求成功
300-305 重定向 比如 www.baidu.com 然后跳转到 www.qq.com
400-415 客户端错误
500-505 服务器端错误
五.三次握⼿建⽴连接
HTTP封装完数据之后,需要将数据使⽤TCP协议向⽹络中的其他主机(服务器)进⾏发送
需要经过三个过程/三次握⼿
可以概括为:①先问问有没有(发送请求)(类似打电话开始播),②然后服务器告诉你有没有(进行数据校验)(类似打电话时提示 拨打的电话不存在或噔噔的声音)③最后获取数据(建立连接)(类似打电话的说话)
六.IP地址与DNS
①IP地址 就是⽤来唯⼀标识⽹络中的⼀台设备,比如127.0.0.1
②域名就类似:www.baidu.com,域名和IP地址是一一对应的关系,因为IP地址不好记,所以有了域名。
③DNS :域名解析器,输入域名之后系统如何识别?就是用DNS将域名解析成IP地址。
比如:
http://127.0.0.1/login.php?user_name=jack&user_password=123
①http: 数据传输使⽤的具体协议(除了http,还有https)
②127.0.0.1或者 www.baidu.com :访问的主机地址
③login.php: 访问主机的那个⽂件或者⽬录(也就是具体的服务)
④?:分隔符 表示:需要向服务器提交数据 。提交⽅式为:GET, 后⾯就是具体提交的数据
⑤user_name=jack就是提交的⼀个数据 。user_name是客户端(html)定义的字段 。jack是字段对应的数据
⑥使⽤&来连接多个字段
七.端⼝号
一个端⼝对应的是⼀种服务,对应一个应用程序,比如QQ。
一般来说,80端⼝对应的是⽹络服务
公认端⼝:0-1023 ,用来进行⼀些特定的服务
注册端⼝:1024-49151 自己写的应⽤程序要使⽤该范围端⼝
动态私有端⼝:49152- 65535
八.URL、URLEncoder和URLDecoder
java使⽤URL类来封装⽹络数据的地址
注意:url地址⾥⾯不能包含中⽂或者其他⼀些特殊的字符, 对于这些字符需要进⾏编码或者解码。
九.URL类的简单使用
1.常用方法介绍
①getHost():是获取主机。比如输出得到127.0.0.1
②getProtocol():是得到协议,一般是http协议
③getQuery():是获取传递的参数,比如user_name=jack&user_password=jcnsjacns这些
④getPath():是得到哪个具体的文件,打印就输出比如 test.php
⑤URLDecoder.decode()和URLEncoder.encode()分别是解码和编码
String word = URLDecoder.decode("%E7%99%BB%E5%BD%95","utf-8");//使用utf-8进行编码
String code = URLEncoder.encode("登录","utf-8");//使用utf-8进行解码
System.out.println(word);
System.out.println(code);
后面两个分别输出
登录
%E7%99%BB%E5%BD%95
使用URLConnection获取网络数据
2.使用示例(向网络中发起氢请求
import java.io.*;
import java.net.MalformedURLException;
import java.net.*;
import java.util.*;
public class 测试程序{
public static void main(String[] args) throws Exception{
//向网络中发起请求
//1.获取对应的url地址
URL url = new URL("http://127.0.0.1:8081/test.php?user_name=jack&user_password=123");//&submit=%E7%99%BB%E5%BD%95
//2.使用URLConnection(抽象类)发起链接(会做三次握手)
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//设置请求方式
conn.setRequestMethod("GET");//默认GET
conn.setDoInput(true);//设置是否打开接收的流(默认打开)
conn.setDoOutput(true);//设置是否打开输出流 POST方式必须要打开
conn.setConnectTimeout(5*1000);//请求时间,如果5s还没得到响应信息,那么链接断开
conn.setUseCaches(false);//设置是否缓存(如果缓存,则下一次打开的时候会块很多)
//3.接收数据(发送和接收都是用输入输出流)
//openStream 是从服务器端获取数据
//conn.getOutputStream() 向服务器端发出数据
InputStream is = url.openStream();
//4.处理输入流的数据 二进制(放到byte数组里面)然后转换成String
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);
}
//5.关闭流
is.close();
}
}
十.URL类下载图片/视频
注意这里的输入输出流怎么写的。
import java.io.*;
import java.net.MalformedURLException;
import java.net.*;
import java.util.*;
public class 测试程序{
public static void main(String[] args) throws Exception{
//下载图片
//1.url
URL url = new URL("http://127.0.0.1:8081/uploadtimg.jpg");
//2.连接
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3.获取输入流
InputStream inputStream = url.openStream();
BufferedInputStream bis = new BufferedInputStream(inputStream);
//4.创建输出流
FileOutputStream fos = new FileOutputStream("C:\\Users\\刘金豪\\Desktop\\test.jpg");
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);
}
//6.关闭流
bos.close();
fos.close();
bis.close();
inputStream.close();
}
}
十一.多线程下载器项目
1.总体思路
先知道文件大小,然后分配线程,在此之前要有一个文件来接收数据(使用RandomAccessFile的seek方法写入数据)
多线程下载获取文件大小
2.具体代码
Main
import java.io.*;
import java.net.MalformedURLException;
import java.net.*;
import java.util.*;
public class 测试程序{
public static void main(String[] args) throws Exception{
String url = "http://127.0.0.1:8081/uploadtimg.jpg";
String path = "C:\\Users\\刘金豪\\Desktop\\copy.jpg";
//下载一个文件
DownloadManager.getInstance().loadData(url,path);
}
}
DownloadManager
这个类用来分配任务
import java.util.Map;
public class DownloadManager{
private Map[] source;
private static DownloadManager manager;
private static Object obj = new Object();
private DownloadManager() {
}
public static DownloadManager getInstance() {
if(manager == null) {
synchronized(obj) {
if(manager == null) {
manager = new DownloadManager();
}
}
}
return manager;
}
//下载一个图片(资源比较少)
public void loadData(String urlString,String filePath) {//从哪下到哪
DownloadOperation downloader = new DownloadOperation(urlString,filePath,3);
downloader.download();
new Thread(new Runnable() {
public void run() {
while(true) {
float rate = downloader.currentRate();
if(rate < 1.000001) {
System.out.println("当前下载比例"+ downloader.currentRate());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}).start();
}
//下载多个资源
public void loadData(Map[] datas) {
}
}
DownloadOperation
这个类用来做具体的下载任务
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
public class DownloadOperation {
private URL url;
private String filePath;//文件地址
private int threadCount;//线程数量
private DownloadThread[] tasks;//线程数组
private long size;
DownloadOperation(String urlString,String filePath,int threadCount){{
try {
this.url = new URL(urlString);
} catch (Exception e) {
e.printStackTrace();
}
this.filePath = filePath;
this.threadCount = threadCount;
tasks = new DownloadThread[threadCount];
}
}
public void download() {
//1.获取文件的大小
getFileSize();
//System.out.println(size);
//创建文件,用于保存下载的数据
createFile();
//分配线程下载数据
dispatch();
}
private void dispatch() {
//计算每个线程下载的平均大小
long average = size / threadCount;
long start = 0;
long downloadSize = average;
for(int i = 0;i < threadCount;i++){
start = i * average;//默认下都是下average,但是最后一个线程可能不一样
//最后一个线程可能下载的数量要大于平均值
if(i == threadCount - 1) {
downloadSize = size - start;
}
//创建线程,执行对应的模块进行下载
DownloadThread dt = new DownloadThread(url,filePath,start,downloadSize);
//保存这个线程对象
tasks[i] = dt;
//启动下载
dt.start();
}
}
//获取当前下载比例,实现进度监听
public float currentRate() {
long len = 0;
for(DownloadThread dt:tasks) {
len += dt.downloadedLength;
}
return (float)len / size;
}
private void createFile() {
File file = new File(filePath);
try {
file.createNewFile();//此时文件是0字节
} catch (IOException e) {
e.printStackTrace();
}
//设置文件大小
RandomAccessFile rac = null;
try {
rac = new RandomAccessFile(file,"rw");
rac.setLength(size);//此时创建的文件大小就是你要下载的文件大小了
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally {
try {
rac.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void getFileSize() {
//url
//获取链接
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("HEAD");//注意这里,请求时HEAD
//获取资源大小
size = conn.getContentLengthLong();//获取大小的方法
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭链接
conn.disconnect();
}
}
}
DownloadThread
skip()里面的参数是要跳过的字节数
package 对象;
import java.io.*;
import java.net.*;
public class DownloadThread extends Thread{
private URL url;
private String filePath;
private long startPosition;
private long size;//到底要下多少
public long downloadedLength;//现在下了多少
public DownloadThread(URL url,String filePath,long startPosition,long size) {
this.url = url;
this.filePath = filePath;
this.startPosition = startPosition;
this.size = size;
}
public void run() {
//定位文件到这个线程应该写入的位置
try {
RandomAccessFile raf = new RandomAccessFile(filePath,"rw");
raf.seek(startPosition);
//开始下载
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.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();
conn.disconnect();
raf.close();
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
总结
这次学的内容是非常之多,头一次对网络有了一点比较清晰的认识,但是内容比较难消化,尤其是最后的多线程下载器项目。等空闲的时候我还要自己写一遍,总结一下问题。