一次http网络请求的过程 浏览器发起请求-> 解析域名得到ip进行TCP连接 ->浏览器发送HTTP请求和头信息发送->服务器对浏览器进行应答,响应头信息和浏览器所需的内容-> 关闭TCP连接或保持-> 浏览器得到数据数据进行操作。 先找到对方ip地址,然后用指定的传输协议传送到指定的端口。
HTTP分层
为什么要分层? 因为网络的不稳定性,所以要url分块传输
常见通讯规则、传输协议:TCP/UDP
什么是iP地址 Internet上的主机有两种方式表示地址: 域名:www.baidu.com, IP 地址:202.108.35.210 ,域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。
什么是端口 用于标识进程的逻辑地址,不同进程的标识,有效地址065535,其中01024系统使用或保留端口。
HTTP协议版本 HTTP/1.0
HTTP/1.1
三次握手 SYN,SYN/ACK,ACK
syn 客户端发送syn包给服务器,进入服务状态 syn-ack 服务器收到,客户端确实并发送给syn.这时进去接受状态 ack 客户端收到服务端的syn-ack,并向服务器确认,此时链接成功!
第一次本方发送请求,第二次对方确认连接,第三次本方再次发送确认信息告诉对方,这样双方就都知道了,从而才能建立连接。
三次握手的目的,两次行不行? 为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误的问题,网络不好的话不知道双方的状态。
四次挥手
fin 发送请求连接 ack 同意断开连接 fin+ack 服务端断开连接 ack 同意断开
四次关闭,我要把你忘掉 1.我不要消息了 2.我知道了 3.我没有消息给你发了 4.我知道了
为什么 TCP 连接在断开时是四次挥手而不是三次? 因为在客户端停止向服务器发送消息时,也许服务器还有消息需要向客户端发送,因此在它对客户端的「Fin」(即「我不再给你发送消息」)消息进行回应时,不需要立即附加上「我也不再向你发送消息」。在稍后服务器的消息发送完毕之后,才需要向客户端发送通知。
Keep-Alive 从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。虽然这里使用TCP连接保持了一段时间,但是这个时间是有限范围的,到了时间点依然是会关闭的,所以我们还把其看做是每次连接完成后就会关闭。
Socket Socket就是为网络服务提供的一种机制,通讯的两端都必须有Socket(套接字,就是接口的意思),网络通讯其实就是Socket间的通讯,数据在两个Socket间通过IO传输,IP 地址标识 Internet 上的计算机,端口号标识正在计算机上运行的进程(程序)。 端口号与IP地址的组合得出一个网络套接字。 创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。
Socket则是对TCP/IP协议的封装和应用 1)创建客户端对象: Socket(String host,int port),指定要接收的IP地址和端口号 2)创建服务端对象:ServerSocket(int port):指定接收的客户端的端口 3)Socket accept():侦听并接受到此套接字的连接,服务器用于接收客户端socket对象的方法 主要通过S.getOutputstream和S.getInputSteram 注:服务器没有socket流,也就没有读写操作的流,服务器是通过获取到客户端的socket流(accept)然后获取到其中的读写方法,对数据进行操作的,也正是因为这样服务器与客户端的数据操作才不会错乱
HTTP的缺点 通信使用明文,内容可能被窃听 不验证通信方身份,因此有可能遭遇伪装
Pragma和Cache-control共存时,Pragma的优先级是比Cache-Control高的。
only-if-cached表示不进行网络请求,完全只使用缓存,若缓存不命中,则返回503错误
1、在request里面去设置cacheControl()策略 2、在header里面去添加cache-control Android okhttp缓存真正正确的实现方式 有网时不缓存,没网时设置缓存,通过Cache-Control和max-age
public class HttpCacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetWorkHelper.isNetConnected(MainApplication.getContext())) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (NetWorkHelper.isNetConnected(MainApplication.getContext())) {
int maxAge = 60 * 60; // 如果想要不缓存,直接时间设置为0,但是需要保存下来吧
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else {
int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
return response;
}}
//设置缓存100M
Cache cache = new Cache(new File(MainApplication.getContext().getCacheDir(),"httpCache"),1024 * 1024 * 100);
return new OkHttpClient.Builder()
.cache(cache)
.addNetworkInterceptor(new HttpCacheInterceptor())
.build();
SSL/TLS防止的安全风险:
SSL/TLS防止的安全风险原理:
数字签名技术结合Hash算法和加密算法,来防止消息被篡改和进行身份认证。 数字签名就是:发送方使用自己的私钥对信息摘要加密产生
数字签名一般不单独使用,基本都是用在数字证书里实现 SSL 通信协议。
第三步:请求网址后返回证书的公钥和数字证书,客户端验证数字证书的有效性,是ca的,怎么验证的?数字签名,都有标准的,x509 第四步:通过后,使用公钥加密内容,非对称加密加密分别对公钥和内容信息摘要(hash,数字签名),发送 第六步:非对称加密解密对称加密的key,然后通过对称加密的key解密内容
正常情况:直接使用 okhttp中就能验证自己的签名,就是为了让己的签名通过验证。 如果是ca机构的证书,OKHTTP不需要配置直接就可以访问,如果是自定义的证书,OKHTTP就不行了,但是可以信任指定证书或者所有证书来访问。
需要自⼰写证书验证过程的场景
get和post的区别 get提交:提交的信息都显示在地址栏中,对于敏感数据不安全,传送的数据量较小,不能大于2KB,因为地址栏存储体积有限。将信息封装到了请求的请求行中。 post提交:提交的信息不显示在地址栏中,对于敏感数据安全,可以提交大体积数据。将信息封装到了请求体中
Cookie Session和Cookie是一种记录客户端状态的机制的话是由服务器发给客户端特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会在headers里带上这些特殊的信息
Cookie 的作⽤ 会话管理:登录状态、购物⻋ 个性化:⽤户偏好、主题 Tracking:分析⽤户⾏为
Session 不同于Cookie保存在客户端中,他是保存在服务器上。 session 的工作原理 1.第一步创建Session 2.在创建了Session的同时,服务器会为该Session生成唯一的Session id 3.在Session被创建之后,就可以调用Session相关的方法往Session中增加内容 4.当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session
cookie和session 的区别 1.存放位置不同 2.存取方式的不同 3.安全性(隐私策略)的不同 4.有效期上的不同 5.对服务器造成的压力不同
URI和 URL 统一资源标识符(URI)用于标识某一互联网资源,而统一资源定位符(URL)表示资源的地点(互联网上所处的位置)。所以 URL 是 URI 的子集。URI:http:,https:,ftp:,本地文件系统(file:),和Jar文件(jar:)。
如何上传 用post,通过表单,不过结构单一,比较繁琐,还可以通过将数据转换成jsonstring、文件、string(base64)来上传,如果是多个图片可以写个循环上传。在上传的时候可以在head添加content-type告诉服务器这是什么数据
多线程断点下载 单线程的话从输入流的第0个字节读取
原理:服务器CPU分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源。所以要用多线程去读取。
请求网络时首先获取资源长度设置被进度条,然后除以要开启的线程数,计算出每个线程应该下载多少字节。然后每个线程去请求网络读取数据。
//设置本次http请求所请求的数据的区间
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
//请求部分数据,相应码是206
if(conn.getResponseCode() == 206){
//流里此时只有1/3原文件的数据
InputStream is = conn.getInputStream();
int length = conn.getContentLength();
特别注意: 对于移动端来说,如果不是比较大的文件,不建议使用这种方式上传,因为断点续传是通过分片上传实现的,上传单个文件需要进行多次网络请求,效率不高。
WWW HTTP协议同时具备极强的扩展性,虽然浏览器请求的是http://www.sina.com.cn/的首页,但是新浪在HTML中可以链入其他服务器的资源,比如
,从而将请求压力分散到各个服务器上,并且,一个站点可以链接到其他站点,无数个站点互相链接起来,就形成了World Wide Web,简称WWW。
常见状态码 200 :请求成功处理,一切OK 302 :请求重定向 304 :服务器端资源没有改动,通知客户端查找本地缓存 404 :客户端访问资源不存在 500 :服务器内部出错
乱码的处理 乱码的出现是因为服务器和客户端码表不一致导致 //手动指定码表 text = new String(bos.toByteArray(), "UTF-8");
什么是内网外网 私有网段地址, 内网IP有3种:第一种10.0.0.0~10.255.255.255,第二种172.16.0.0~172.31.255.255,第三种192.168.0.0~192.168.255.255 运营商给你的100.64.* . * 也是私有地址,以前大家大家共享一个地址池,有随机的公网地址,现在公网地址更加紧张,运营商只给客户分配私网地址,然后nat后大家共享一个公网地址 172.31.40.210内网 192.168.17.207外网
创建 UDPSocket发送服务对象:DatagramSocket(),可不指定端口 创建 UDPSocket接收服务对象:DatagramSocket(int port), 发送:void send(DatagramPacket p) 接收:void receive(DatagramPacket p)
Socket(客户端)和ServiceSocket服务端() 建立客户端和服务端 建立连接后,通过socket中的IO流进行数据的传输 关闭socket 1)创建客户端对象: Socket(String host,int port),指定要接收的IP地址和端口号 2)创建服务端对象:ServerSocket(int port):指定接收的客户端的端口 3)Socket accept():侦听并接受到此套接字的连接,服务器用于接收客户端socket对象的方法 主要通过S.getOutputstream和S.getInputSteram 注:服务器没有socket流,也就没有读写操作的流,服务器是通过获取到客户端的socket流(accept)然后获取到其中的读写方法,对数据进行操作的,也正是因为这样服务器与客户端的数据操作才不会错乱
定义tcp的服务端
建立服务端的socket服务,ServerSocket,并监听一个端口
获取连接过来的客服端对象,通过ServerSokcet的 accept方法。没有连接就会等,所以这个方法阻塞式的。
客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据。
关闭服务端(可选)
public class TCPServerDemo {
public static void main(String[] args) throws IOException {
// 建立服务端的socket服务,并监听一个端口
ServerSocket ss = new ServerSocket(10003);
// 通过accept方法获取连接过来的客户端对象
Socket socket = ss.accept();
String ip = socket.getInetAddress().getHostAddress();
System.out.println(ip + "......connected");
// 获取客户端发过来的数据,那么要使用客户端对象的读取流来获取数据
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf, 0, len));
socket.close();// 关闭客户端
ss.close();// 关闭服务端,可选的操作
}
}
tcp分为客户端和服务端,客服端对象的对象是socket,服务端对应的是serversoceket
客户端,在建立的时候就可以去连接指定的主机
因为tcp是面向连接的,所以在建立socket时就需要有服务端存在
并连接成功,形成通路,才能在该通道上传输数据
public class TCPClientDemo {
public static void main(String[] args) throws Exception {
//1、创建客户端的socket服务,指定目的主机和端口
Socket socket = new Socket("192.168.229.1", 10003);
//2、获取socket的输出流,用于发送数据
OutputStream out = socket.getOutputStream();
//3、发送数据
out.write("tcp client send message come on".getBytes());
socket.close();
}
}
原生有两种,HttpURLConnection和HttpClient,两种方式都支持HTTPS协议、以流的形式进行上传和下载、配置超时时间、IPv6、以及连接池等功能。HttpURLConnection的API提供的比较简单,可以更加容易地去使用和扩展它,而且速度快、还能节省电量。
HttpURLConnection用法:
发送GET请求
URL url = new URL(path);
//获取连接对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置连接属性
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//建立连接,获取响应吗
if(conn.getResponseCode() == 200){
//获取服务器响应头中的流,流里的数据就是客户端请求的数据
InputStream is = conn.getInputStream()
}
获取服务器返回的流,从流中把html源码读取出来
流转换成string的方法第一种:
byte[] b = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while((len = is.read(b)) != -1){
//把读到的字节先写入字节数组输出流中存起来
bos.write(b, 0, len);
}
//把字节数组输出流中的内容转换成字符串
//默认使用utf-8
text = new String(bos.toByteArray());
//第二种:
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
String line;
while ((line=reader.readLine())!=null) {
builder.append(line);
}
String text=builder.toString();
//第三种
Reader reader = null;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
//图片的流
InputStream is = null;
...
Bitmap bitmap = BitmapFactory.decodeStream(is);
ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setImageBitmap(bitmap);
post
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
简单封装
public class HttpUtil {
public static void sendHttpRequest(final String address,
final HttpCallbackListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL(address);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
if (listener != null) {
// 回调onFinish()方法
listener.onFinish(response.toString());
}
} catch (Exception e) {
if (listener != null) {
// 回调onError()方法
listener.onError(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}