TCP与UDP¶
TCP¶
特点:
- 面向连接的、可靠的、基于字节流的传输层通信协议
- 将应用层的数据流分割成报文段并发送给目标节点的TCP层
- 数据包都有序号,对方收到则发送ACK确认,未收到则重传
- 使用校验和来校验数据在传输过程中是否有误
无论是TCP还是UDP都是不包含IP地址的(由TCP/IP负责),包含双方的端口号(标识进程)。
- 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
.
三次握手¶
三次握手的必要性:
- 为了初始化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不可达,中断连接。
在内核中,使用链表
管理全连接队列,使用哈希表
管理半连接队列.
服务器绑定服务端口的时候申请并初始化接受队列(包含:半连接队列与全连接队列).在服务端接受到客户端的SYN包会创建socket在半连接队列.在收到客户端的ACK包后创建新的socket在全连接队列.应用程序accept之后会从全连接队列中取走socket.
四次挥手¶
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的可靠性与流控特性。
滑动窗口的数据由两部分构成:已发送但是没有收到ACK的和允许发送还没发送的。
接收窗口:已接受到没发ACK的
UDP¶
无论是TCP还是UDP都是不包含IP地址的(有TCP/IP负责),包含双方的端口号(标识进程)。
特点:
- 面向非连接
- 不维护连接状态,支持同时向多个客户端传输相同的信息
- 数据包报头只有8字节,额外开销小
- 吞吐量只受限于数据生成速率、传输速率与机器性能
- 尽最大努力交付,不保证可靠交付,不需要维护复杂的链接状态表
- 面向报文,不对应用程序提交的报文信息进行拆分或者合并
二者区别¶
tcp | udp |
---|---|
连接 | 无连接 |
可靠 | 不可靠 |
有序 | 无序 |
速度慢 | 速度快 |
重量 | 轻量 |
HTTP¶
特点:
- 支持客户端/服务器模式
- 简单灵活快速
- 灵活
- 无连接,长连接是由下层实现的对HTTP透明
- 无状态
HTTP1.1相较于HTTP1.0最大的区别就是引入长连接。
在浏览器输入url后经历的流程:
- DNS解析url对应的IP地址,由近到远的DNS解析为浏览器缓存、系统缓存、路由器缓存、ISP缓存、域名服务器缓存、顶级域名服务器缓存;
- TCP连接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 连接结束
状态码¶
五种可能的取值:
- 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¶
SSL¶
Security Sockets Layer,安全套接层。为网络通信提供安全及数据完整性的一种安全协议。对操作系统对外的API,SSL3.0后更名为TLS。采用身份验证和数据加密保证网络通信的安全和数据的完整性。
加密¶
对称加密¶
加密解密使用同一个密钥。
非对称加密¶
加密使用的密钥和解密使用的密钥是不相同的。
哈希算法¶
将任意长度的信息转换为固定长度的值,算法不可逆,比如md5算法。
数字签名¶
证明某个信息或者文件是某人发出或认同的。
HTTPS数据传输流程¶
- 浏览器将支持的加密算法信息发送给服务器
- 服务器选择一套浏览器支持的加密算法,已证书的形式回发浏览器
- 浏览器验证证书合法性,并结合证书公钥加密信息发送给浏览器
- 服务器使用私钥解密信息,验证哈希,加密响应信息回发浏览器
- 浏览器解密响应信息,并对消息进行验证,之后进行加密交互数据
区别¶
- HTTPS需要CA申请证书,HTTP不需要
- HTTPS密文传输,HTTP明文传输
- HTTPS默认443,HTTP默认80
- HTTPS=HTTP+加密+认证+完整性保护,较HTTP安全
用户还有使用HTTP的,网站大多进行跳转。但在跳转过程中可能出现劫持。可以使用HSTS优化。
Socket¶
本地中每个进程用PID进行唯一标识。
IP地址+协议+端口号标识网络中的进程。
Socket是对TCP/IP的抽象,是操作系统对外开放的接口。
Socket通信流程¶
以下是JAVA实现分别基于TCP和UDP的Socket
//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位。
大致分类如下:
- A类地址:网络号占1个字节,网络号的第一位固定为0;
- B类地址:网络号占2个字节,网络号的前两位固定为10;
- C类地址:网络号占3个字节,网络号的前三位固定位110;
- D类地址:前四位是1110,用于多播(multicast),即一对多通信;
- E类地址:前四位是1111,保留为以后使用。
其中,ABC三类地址为单播地址(unicast),用于一对一通信,是最常用的。
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文本内容如下:
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
# 每行按空格或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 <可以指定标识符>