Java网络 Day03 2020-04-27

内容

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:

URLUniform Resource Locator,即统一资源定位符(可以理解成一个网址就是一个URL,但是URL不一定就是网址),是互联网上标准资源的地址,可以在全球范围内唯一确定的一个资源。
URIUniform 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();
    }
   }
   
   
}

总结

这次学的内容是非常之多,头一次对网络有了一点比较清晰的认识,但是内容比较难消化,尤其是最后的多线程下载器项目。等空闲的时候我还要自己写一遍,总结一下问题。

你可能感兴趣的:(Java网络 Day03 2020-04-27)