计算机网络


计算机网络_网络通讯协议关系图2020版

计算机网络_TCP-IP协议关系图.png

计算机网络_数据格式.png

TCP与UDP

TCP

特点:

  • 面向连接的、可靠的、基于字节流的传输层通信协议
  • 将应用层的数据流分割成报文段并发送给目标节点的TCP层
  • 数据包都有序号,对方收到则发送ACK确认,未收到则重传
  • 使用校验和来校验数据在传输过程中是否有误

无论是TCP还是UDP都是不包含IP地址的(由TCP/IP负责),包含双方的端口号(标识进程)。

计算机网络_TCP报头.jpg

  • 16位源端口号和16位目的端口号;
  • 32位序号:一次TCP通信过程中某一个传输方向上的字节流的每个字节的编号,通过这个来确认发送的数据有序,比如现在序列号为500,发送了200,下一个序列号就是700;
  • 32位确认号:用来响应TCP报文段,给收到的TCP报文段的序号加1,三握时还要携带自己的序号;
  • 4位头部长度:标识该TCP头部有多少个4字节,共表示最长15*4=60字节。同IP头部;
  • 6位保留。6位标志。URG(紧急指针是否有效)ACK(表示确认号是否有效)PSH(提示接收端应用程序应该立即从TCP接收缓冲区读走数据)RST(表示要求对方重新建立连接)SYN(同步序号,表示请求建立一个连接)FIN(表示通知对方本端要关闭连接,用于释放连接);
  • 16位窗口大小:TCP流量控制的一个手段,用来告诉对端TCP缓冲区还能容纳多少字节;
  • 16位校验和:由发送端填充,接收端对报文段执行CRC算法以检验TCP报文段在传输中是否损坏;
  • 16位紧急指针:一个正的偏移量,它和序号段的值相加表示最后一个紧急数据的下一字节的序号;
端口是否被服用

tcp链接可以被简化为对应的四元组或者五元组、七元组.完全一样的无法复用同一个端口.

客户端在分配请求的端口的时候会先看这个端口是否被占用,占用且四元组一致时,遍历下一个端口,直到找到对应的端口.不然报错.端口范围为net.ipv4.ip_local_port_range.

三次握手

三次握手连接.png

三次握手的必要性:

  • 为了初始化Seq,用于之后的传输;

SYN Flood攻击:在首次握手时SYN超时,此时,Server不断重试直到超时,Linux默认重试五次,每次时间翻倍,起始时间从1秒开始,等待1+2+4+8+16+32=63秒才断开连接。使得服务器可能遭到SYN Flood攻击。针对这种情况,SYN队列满了,通过tcp_syncookies参数回发SYN cookie。若为正常连接则Client会回发SYN cookie,直接建立连接。

在建立连接之后,Client故障,采用保活机制。此时向对方发送保活机制探测报文,如果未收到响应则继续发送,直至尝试次数达到保活探测数的上限。此时认定该Client不可达,中断连接。

计算机网络_TCP三次握手中连接队列情况.png

在内核中,使用链表管理全连接队列,使用哈希表管理半连接队列.

服务器绑定服务端口的时候申请并初始化接受队列(包含:半连接队列与全连接队列).在服务端接受到客户端的SYN包会创建socket在半连接队列.在收到客户端的ACK包后创建新的socket在全连接队列.应用程序accept之后会从全连接队列中取走socket.

四次挥手

四次回收关闭.png

TIME_WAIT状态的含义:

  • 确保有足够的时间让对方收到ACK包,避免发生重传;
  • 避免新旧连接混淆;

服务端大量出现CLOSE_WAIT状态的原因:对方关闭socket连接,我方忙于读写,没有及时关闭连接。此时,检查代码,特别是释放资源的代码。检查配置,特别是处理请求的线程配置。

Linux查看CLOSE_WAIT命令:netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

滑动窗口

RTT:发送一个数据包到收到对应的ACK所花费的时间

RTO:重传时间间隔

TCP使用滑动窗口做流量控制与乱序重排,保证TCP的可靠性与流控特性。

计算机网络_TCP发送窗口.jpg

滑动窗口的数据由两部分构成:已发送但是没有收到ACK的和允许发送还没发送的。

计算机网络_TCP接收窗口.png

接收窗口:已接受到没发ACK的

UDP

无论是TCP还是UDP都是不包含IP地址的(有TCP/IP负责),包含双方的端口号(标识进程)。

特点:

  • 面向非连接
  • 不维护连接状态,支持同时向多个客户端传输相同的信息
  • 数据包报头只有8字节,额外开销小
  • 吞吐量只受限于数据生成速率、传输速率与机器性能
  • 尽最大努力交付,不保证可靠交付,不需要维护复杂的链接状态表
  • 面向报文,不对应用程序提交的报文信息进行拆分或者合并

计算机网络_UDP报文.png

二者区别

tcp udp
连接 无连接
可靠 不可靠
有序 无序
速度慢 速度快
重量 轻量

HTTP

特点:

  • 支持客户端/服务器模式
  • 简单灵活快速
  • 灵活
  • 无连接,长连接是由下层实现的对HTTP透明
  • 无状态

HTTP1.1相较于HTTP1.0最大的区别就是引入长连接。

HTTP请求结构.png

在浏览器输入url后经历的流程:

  1. DNS解析url对应的IP地址,由近到远的DNS解析为浏览器缓存、系统缓存、路由器缓存、ISP缓存、域名服务器缓存、顶级域名服务器缓存;
  2. TCP连接
  3. 发送HTTP请求
  4. 服务器处理请求并返回HTTP报文
  5. 浏览器解析渲染页面
  6. 连接结束

状态码

五种可能的取值:

  • 1xx:只是信息——表示请求已接收,继续处理
  • 2xx:成功——表示请求已经被成功接收、理解
  • 3xx:重定向——要完成请求必须进行进一步的操作
  • 4xx:客户端错误——请求有语法错误或请求无法实现
  • 5XX:服务端错误——服务器未能实现合法的请求
常见HTTP请求头
请求头 说明
Accept-Charset 用于指定客户端接受的字符集
Accept-Encoding 用于指定可接受的内容编码,如Accept-Encoding:gzip.deflate
Accept-Language 用于指定一种自然语言,如Accept-Language:zh-cn
Host 用于指定被请求资源的Internet主机和端口号,如Host:buerlog.top
User-Agent 客户端的操作系统、浏览器、等其他属性
Connection 当前连接是否保持,如Connection:Keep-Alive
常见HTTP响应头
响应头 说明
Server 使用的服务器名称:如Server:nginx/1.10.3 (Ubuntu)
Content-Type 用来指明发送给客户端的实体正文的媒体类型,如text/html; charset=utf-8
Content-Encoding 与请求抱头Accept-Encoding对应,告诉浏览器服务端采用什么压缩格式
Content-Language 描述了资源所用的自然语言
Content-length 指明实体正文的长度,用以字节方式存储的十进制数字来表示
Keep-Alive 保持连接的时间,如Keep-Alive:timeout:5,max=120
常见状态码
状态码 英文名称 中文描述
200 OK 请求成功。一般用于GET与POST请求
302 临时跳转,跳转地址通过location指定
400 Bad Request 客户端请求的语法错误,服务器无法理解
401 Unauthorized 请求要求用户的身份认证
403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
500 Internal Server Error 服务器内部错误,无法完成请求
503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中

GET与POST区别

  • Http报文层面:GET将请求信息放在URL,有长度限制,相对不安全。POST放报文体中,无长度限制,相对安全,但是也是明文,还是要依靠HTTPS。
  • 数据库层面:GET符合幂等性和安全性,POST不符合。
  • 其他层面:GET可以被缓存、被存储,而POST不行。

Cookie与Session区别

HTTP是无状态的。

Cookie:是由服务器发送给客户端的特殊信息。以文本的形式存放在客户端,通过响应头传输。客户端再次发送的时候会将cookie回发。服务端收到后,会解析cookie生成与客户端相对应的内容。

Session:服务器端的机制,在服务器上保存信息。解析客户端请求并操作session id,按需保存状态信息。两种实现方式:使用cookie或使用url回写。

Tomcat同时使用两种实现方式,如果客户端支持使用cookie的方式,则使用cookie。否则使用url回写的方式。

HTTPS

计算机网络_HTTPS.png

SSL

Security Sockets Layer,安全套接层。为网络通信提供安全及数据完整性的一种安全协议。对操作系统对外的API,SSL3.0后更名为TLS。采用身份验证和数据加密保证网络通信的安全和数据的完整性。

加密

对称加密

加密解密使用同一个密钥。

非对称加密

加密使用的密钥和解密使用的密钥是不相同的。

哈希算法

将任意长度的信息转换为固定长度的值,算法不可逆,比如md5算法。

数字签名

证明某个信息或者文件是某人发出或认同的。

HTTPS数据传输流程

  1. 浏览器将支持的加密算法信息发送给服务器
  2. 服务器选择一套浏览器支持的加密算法,已证书的形式回发浏览器
  3. 浏览器验证证书合法性,并结合证书公钥加密信息发送给浏览器
  4. 服务器使用私钥解密信息,验证哈希,加密响应信息回发浏览器
  5. 浏览器解密响应信息,并对消息进行验证,之后进行加密交互数据

区别

  • HTTPS需要CA申请证书,HTTP不需要
  • HTTPS密文传输,HTTP明文传输
  • HTTPS默认443,HTTP默认80
  • HTTPS=HTTP+加密+认证+完整性保护,较HTTP安全

用户还有使用HTTP的,网站大多进行跳转。但在跳转过程中可能出现劫持。可以使用HSTS优化。

Socket

本地中每个进程用PID进行唯一标识。

IP地址+协议+端口号标识网络中的进程。

Socket是对TCP/IP的抽象,是操作系统对外开放的接口。

计算机网络_Socket.png

Socket通信流程

计算机网络_Socket通信.png

以下是JAVA实现分别基于TCP和UDP的Socket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//TCPClient.java
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TCPClient {
public static void main(String[] args) throws Exception {
//创建socket,并指定连接的是本机的端口号为65000的服务器socket
Socket socket = new Socket("127.0.0.1", 65000);
//获取输出流
OutputStream os = socket.getOutputStream();
//获取输入流
InputStream is = socket.getInputStream();
//将要传递给server的字符串参数转换成byte数组,并将数组写入到输出流中
os.write(new String("hello world").getBytes());
int ch = 0;
byte[] buff = new byte[1024];
//buff主要用来读取输入的内容,存成byte数组,ch主要用来获取读取数组的长度
ch = is.read(buff);
//将接收流的byte数组转换成字符串,这里是从服务端回发回来的字符串参数的长度
String content = new String(buff, 0, ch);
System.out.println(content);
//不要忘记关闭输入输出流以及socket
is.close();
os.close();
socket.close();
}
}

//TCPServer.java
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
public static void main(String[] args) throws Exception {
//创建socket,并将socket绑定到65000端口
ServerSocket ss = new ServerSocket(65000);
//死循环,使得socket一直等待并处理客户端发送过来的请求
while (true) {
//监听65000端口,直到客户端返回连接信息后才返回
Socket socket = ss.accept();
//获取客户端的请求信息后,执行相关业务逻辑
new LengthCalculator(socket).start();
}
}
}

//UDPClient.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPClient {
public static void main(String[] args) throws Exception {
// 客户端发数据报给服务端
DatagramSocket socket = new DatagramSocket();
// 要发送给服务端的数据
byte[] buf = "Hello World".getBytes();
// 将IP地址封装成InetAddress对象
InetAddress address = InetAddress.getByName("127.0.0.1");
// 将要发送给服务端的数据封装成DatagramPacket对象 需要填写上ip地址与端口号
DatagramPacket packet = new DatagramPacket(buf, buf.length, address,
65001);
// 发送数据给服务端
socket.send(packet);

// 客户端接受服务端发送过来的数据报
byte[] data = new byte[100];
// 创建DatagramPacket对象用来存储服务端发送过来的数据
DatagramPacket receivedPacket = new DatagramPacket(data, data.length);
// 将接受到的数据存储到DatagramPacket对象中
socket.receive(receivedPacket);
// 将服务器端发送过来的数据取出来并打印到控制台
String content = new String(receivedPacket.getData(), 0,
receivedPacket.getLength());
System.out.println(content);

}

}

//UDPServer.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPServer {
public static void main(String[] args) throws Exception {
// 服务端接受客户端发送的数据报
DatagramSocket socket = new DatagramSocket(65001); //监听的端口号
byte[] buff = new byte[100]; //存储从客户端接受到的内容
DatagramPacket packet = new DatagramPacket(buff, buff.length);
//接受客户端发送过来的内容,并将内容封装进DatagramPacket对象中
socket.receive(packet);

byte[] data = packet.getData(); //从DatagramPacket对象中获取到真正存储的数据
//将数据从二进制转换成字符串形式
String content = new String(data, 0, packet.getLength());
System.out.println(content);
//将要发送给客户端的数据转换成二进制
byte[] sendedContent = String.valueOf(content.length()).getBytes();
// 服务端给客户端发送数据报
//从DatagramPacket对象中获取到数据的来源地址与端口号
DatagramPacket packetToClient = new DatagramPacket(sendedContent,
sendedContent.length, packet.getAddress(), packet.getPort());
socket.send(packetToClient); //发送数据给客户端
}

}

//LengthCalculator.java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class LengthCalculator extends Thread {
//以socket为成员变量
private Socket socket;

public LengthCalculator(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
try {
//获取socket的输出流
OutputStream os = socket.getOutputStream();
//获取socket的输入流
InputStream is = socket.getInputStream();
int ch = 0;
byte[] buff = new byte[1024];
//buff主要用来读取输入的内容,存成byte数组,ch主要用来获取读取数组的长度
ch = is.read(buff);
//将接收流的byte数组转换成字符串,这里获取的内容是客户端发送过来的字符串参数
String content = new String(buff, 0, ch);
System.out.println(content);
//往输出流里写入获得的字符串的长度,回发给客户端
os.write(String.valueOf(content.length()).getBytes());
//不要忘记关闭输入输出流以及socket
is.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}


Tip

Websocket

127.0.0.1&0.0.0.0.0

众所周知,。IP地址一共分为5位。

大致分类如下:

  1. A类地址:网络号占1个字节,网络号的第一位固定为0;
  2. B类地址:网络号占2个字节,网络号的前两位固定为10;
  3. C类地址:网络号占3个字节,网络号的前三位固定位110;
  4. D类地址:前四位是1110,用于多播(multicast),即一对多通信;
  5. E类地址:前四位是1111,保留为以后使用。

计算机网络_IP地址分类.png

其中,ABC三类地址为单播地址(unicast),用于一对一通信,是最常用的。

计算机网络_IP地址划分.jpg

netstat

命令参数:

  • -a或–all 显示所有连线中的Socket。
  • -A<网络类型>或–<网络类型> 列出该网络类型连线中的相关地址。
  • -c或–continuous 持续列出网络状态。
  • -C或–cache 显示路由器配置的快取信息。
  • -e或–extend 显示网络其他相关信息。
  • -F或–fib 显示FIB。
  • -g或–groups 显示多重广播功能群组组员名单。
  • -h或–help 在线帮助。
  • -i或–interfaces 显示网络界面信息表单。
  • -l或–listening 显示监控中的服务器的Socket。
  • -M或–masquerade 显示伪装的网络连线。
  • -n或–numeric 直接使用IP地址,而不通过域名服务器。
  • -N或–netlink或–symbolic 显示网络硬件外围设备的符号连接名称。
  • -o或–timers 显示计时器。
  • -p或–programs 显示正在使用Socket的程序识别码和程序名称。
  • -r或–route 显示Routing Table。
  • -s或–statistice 显示网络工作信息统计表。
  • -t或–tcp 显示TCP传输协议的连线状况。
  • -u或–udp 显示UDP传输协议的连线状况。
  • -v或–verbose 显示指令执行过程。
  • -V或–version 显示版本信息。
  • -w或–raw 显示RAW传输协议的连线状况。
  • -x或–unix 此参数的效果和指定”-A unix”参数相同。
  • –ip或–inet 此参数的效果和指定”-A inet”参数相同。

ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

awk

行匹配语句 awk ‘’ 只能用单引号。

log.txt文本内容如下:

1
2
3
4
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 每行按空格或TAB分割,输出文本中的1、4项,每项之间用空格分开
$ awk '{print $1,$4}' log.txt
---------------------------------------------
2 a
3 like
This's
10 orange,apple,mongo
# 格式化输出
$ awk '{printf "%-8s %-10s\n",$1,$4}' log.txt
---------------------------------------------
2 a
3 like
This's
10 orange,apple,mongo

-F <可以指定标识符>


文章作者: 不二
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 不二 !
  目录