网络相关
- | - |
---|---|
应用层 | Http/Telent/Ftp/Email/DNS |
SPDY | HTTP2.0继承 |
SSL/TLS | Secure Sockets Layer |
传输层 | TCP/UDP |
网络层 | IP/ICMP/ARP |
链路层 | 设备驱动程序及接口 |
TCP
高可靠性的端到端通信
-
基于
byte
流,每一条TCP连接只能点对点 -
全双工
,可以同时进行信号的双向传输 -
分组:将应用层数据流打包成若干小数据块,并封装在IP分组中(几百 ~ 40byte)
-
Internet自身无法确保可靠的分组传输,超负荷可以随意丢弃分组,所以TCP实现了自己的确认机制来确保数据的成功传输
-
每个报文段包含一个序列号和数据完整性校验和,发送后启动一个定时器;
-
另一端对收到的数据进行确认,对失序的数据重新排列,丢弃重复数据;并向发送者会送小的确认分组,如果发送者没有在指定的窗口时间内收到确认信息,发送者就认为分组已被破坏或销毁,并重新发送数据
-
报文
三次握手
四次挥手
性能
-
Nagle算法
:将大量的小数据分组绑定在一起,发送全尺寸,提高网络效率 -
延迟确认
:由于TCP规定每次都要向发送者回送确认分组,且确认分组报文很小,所以TCP允许在发往相同方向的输出数据分组中对其进行"捎带",即将返回的确认信息与输出的数据分组结合在一起。所以确认分组会在一个特定的时间窗口(100 ~ 200 ms)内将输出确认放在缓冲区,以寻找能够捎带的输出数据分组。如果没有等到,则放在单独的分组中传输 -
TCP慢启动
:为了防止网络突然过载和拥塞,限制了TCP端口在任意时刻可以传输的分组数。每成功接收一个分组,就可以发送两个分组,等待确认后,可以发送四个分组,一次类推。表现为某个HTTP事务有大量数据要发送,是不能一次将所有分组都发送出去的;新连接的传输速度会比已经交换过一定数量的连接慢一些 -
TIME_WAIT
:当某个TCP端点关闭连接时,会在内存中维护一个小的控制块,用来记录最近关闭连接的IP地址和端口号。这类信息指挥维持一小段时间,以防止创建、关闭并重新创建两个具备相同IP、端口号的连接;但在高并发情况下可能引起端口耗尽问题,要解决这个问题可以使用负载均衡(增加机器数量)
HTTP
建立在TCP协议之上,所以瓶颈及其优化技巧都是基于TCP协议本身的特性
年份 | 版本 | 改动内容 |
---|---|---|
1991 | 0.9 | 只有GET方式 |
1996 | 1.0 | GET、POST、HEAD方式,加入Header |
*1997 | 1.1 | 多种提交方式,默认都是持续连接 |
2009 | SPDY | |
2015 | 2.0 |
Request
GET /index.html HTTP/1.1
Accept: mime type
Accept-Encoding:
If-Modified-Since: EEE, dd MMM yyyy HH:mm:ss z // 文件是否修改
Referer: http://www.it315.org/index.jsp // 说明请求来自哪里,防盗链
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) 客户端代理
Cookie // 向服务器发送Cookie
Range: byte=100-999 // 多线程多点续传
username=ccy&userpwd=118
Response
Content-Type: mime type; charset=utf-8
Content-Encoding: gzip
statusCode | reasonPhrase |
---|---|
204 | 返回内容为空httpResponse.getEntity() == null
|
302 | 重定向 |
304 | 未修改,没有消息体;header信息可能不完成,需要和缓存合并 |
404 | Not Found |
409 | 资源冲突 |
长短链接
短连接
HTTP的主要缺点是,每个TCP连接只能发送一个请求,发送完毕后就关闭;keep-alive
确保服务器将保持这个tcp连接一段时间直到会话保持的时间超过keepaliveTime
时,client和server端将主动释放tcp连接
- 降低三次握手时延
- 减少打开链接的数量
- 避免慢启动
HTTP/1.0
需要主动声明Connection: keep-alive
,HTTP1.1
默认都是- 如果服务器提供的是一个接口服务(除了动态内容,几乎没有引用任何静态内容)不建议开启;如果服务器提供的是Web站点服务(一个页面除了动态内容,还包含非常多的JS、图片、css文件等)建议开启(打开1个网页会建立10~50个TCP连接)
长连接(推送)
客户端和服务端之间始终建立着一个通信连接,在连接没有中断之前,可以随时进行通信
- 断开重连、心跳检测
- 可以通过HTTP轮询的方式实现伪长连接。(频繁握手、协议冗余)
缓存
参见 HttpHeaderParser.parseCacheHeaders
Cache.Entry | Req Header | 说明 |
---|---|---|
ttl | Cache-Control | no-cache/no-store、max-age(stale-while-revalidate/must-revalidate/proxy-revalidate) |
serverDate | Date | 服务器时间 |
ttl | Expires | 在该时间后被认为失效,可以和Date配合计算出存活时间 |
lastModified | Last-Modified | 文件最后修改时间 |
etag | ETag | 根据特殊算法计算的一串字符,通过对比字符判断是否修改过 |
-
Cache-Control
设置的内容优先级较高会覆盖其他设置
文件上传
- application/x-www-form-urlencoded 在发送前默认编码所有字符
- multipart/form-data 不对字符编码,浏览器对上传实体内容中的每个字段用分割线进行分割,两个分割线间的内容成为一个分区,每个分区包含两个部分,一部分是对表单字段元素进行描述,另外一部分是表单字段元素的主体内容
<form method="post" enctype="multipart/form-data">
<input type="file"/>
</from>
Content-Type:multipart/form-data; boundary=--------------------------7dc2af520870
--------------------------7dc2af520870
- 服务端要获得上传的文件,需要用request.getHeader("Content-Type")来取得实体内容的分界字符串,通过request.getInputStream()得到上传的整个post实体流,然后根据HTTP协议,把文件部分给筛选出来,保存在服务端磁盘中;其他表单值不能直接使用request.getParamter()获得
HTTPS
OpenSSL
是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议
客户端 | 服务端 |
---|---|
发送SSL/TLS信息、算法信息 + 随机数RNC(Random Number Client) | - |
- | 回复SSL/TLS信息、算法信息、证书 + 随机数RNS(Random Number Server) |
验证证书;产生新的随机数PMS(Pre-Master Secret),使用【三个随机数】构建主密钥MS,并用证书里的公钥向加密PMS发送给服务端 | - |
- | 通过私钥获得PMS,同样使用【三个随机数】构建主密钥MS |
至此握手阶段结束,接下来使用普通的HTTP协议,只不过使用会话密钥
加密内容
HTTP2.0 & SPDY
-
头信息压缩机制
:Header压缩后发送,同时客户端和服务端同时维护一张头信息,所有字段都生成一个索引号,以后就只发送索引号 -
多路复用/管道化链接
:针对HTTP高延迟的问题,通过多个请求stream共享一个tcp连接的方式,降低延迟同时提高带宽的利用率 -
请求优先级
:允许给每个request设置优先级,重要的请求会优先得到响应 -
服务端推送
-
基于HTTPS的加密协议传输
API
HttpUrlConnection
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(encodeURL).openConnection();
conn.setConnectTimeout(timeoutMs);
conn.setReadTimeout(timeoutMs);
conn.setUseCaches(false);
conn.setDoOutput(true); // conn.setRequestMethod("POST");
conn.setRequestProperty("If-Modified-Since", "");
int nRC = conn.getResponseCode(); // 触发getInputStream(connect()); connected = true;
if (nRC == HttpURLConnection.HTTP_OK) {
conn.getInputStream();
} else if (nRC == 304) {
}
} finally {
conn.disconnect(); // input/outputStream closed
}
AsyncHttpClient
Volley
OKHttp
OkHttpClient httpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.baidu.com/")
.build();
Call call = httpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
mTvConten.setText(request.toString());
}
@Override
public void onResponse(Response response) throws IOException {
String htmlStr = response.body().string();
mTvConten.setText(htmlStr);
}
});
Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(client) // 自定义client
.build();
// 定义连网接口
GitHubService service = retrofit.create(GitHubService.class);
// Call 模式
Call<User> repos = service.getUserByName("CatDog118");
// Rxjava 模式
Observable<User> getUserByName(@Path("user") String user);
// execute同步请求; enqueue异步请求
repos.enqueue(new Callback<User>() {
public void onResponse(Call<User> call, Response<User> response) {}
public void onFailure(Call<User> call, Throwable t) {}
});
// 取消请求
call.cancel();