2022-08-03
#virtualbox后台启动
C:\programs\VirtualBox\VBoxManage.exe startvm "centos7" --type headless
#vmware后台启动
C:\programs\vmware-workstation\vmrun.exe -T ws start "D:\programs-data\vmware-vm\centos7\centos7.vmx" nogui
2022-06-07
//httpClient建议复用
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(path);
List<NameValuePair> nameValuePairs = new ArrayList<>();
nameValuePairs.add(new BasicNameValuePair(USERNAME, admin.getUsername());
nameValuePairs.add(new BasicNameValuePair(PASSWORD, admin.getPassword());
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
try(CloseableHttpResponse response = httpClient.execute(httpPost)) {
Header[] cookies = response.getHeaders(COOKIE_HTTP_HEADER);
if (cookies.length > 0) {
return cookies[0].getValue();
}
}
2022-06-05
-
传输层为运行在不同的主机上的进程提供了一种逻辑通信机制。
-
发送方将应用递交的消息分成一个或多个segment,并向下传给网络层;接收方将接收到的segment组成消息,向上交给应用层。
-
传输层协议分类:
- 可靠、按序的交付服务(TCP),提供拥塞控制、流量控制和连接建立。
- 不可靠的交付服务(UDP),基于尽力而为的网络层没有做可靠性方面的扩展。
-
接收端进行多路复用,传输层依据头部信息将接收到的segment交给正确的socket;发送端进行多路复用,多个socket为每个数据块封装头部信息,生成segment,交给网络层。
-
每个数据报携带源IP地址、目的IP地址,每个数据报携带一个传输层的端(Segment),每个段携带源端口和目的端口。主机收到segment后,传输层协议提取IP地址和端口信息,将segemnt导向相应的socket。
-
UDP的SOCKET用二元组标识(目的IP地址,目的端口号),来自不同源IP地址和端口号的IP数据报被导向同一个Socket,TCP的SOCKET用四元组标识(源地址,源端口,目的地址,目的端口)。
UDP
#
-

- UDP(USER DATAGRAM PROTOCOL)基于IP协议,简单的包装了下IP协议,提供了传输层的复用、分用,同时提供了简单的错误校验。
- UDP段可能会丢失、错序。常用于流媒体应用。
- UDP校验和提供了差错校验功能。
可靠数据传输
#
- 可靠指的是不错、不丢失、不乱序。
- 通过使用校验和来检查位错误来保证不错,同时使用ACK(acknowledgement)来显式的告诉发送方已经正确的接收分组。
- 重传可用保证不丢失,对于未收到ACK的分组,进行重传,重传需要使用定时器,当超过一定时间未收到ACK则重传。
- 简单的重传可能会导致重复分组或乱序,需要使用序列号,发送方给每个分组增加序列号,接收方通过序列号来确定分组顺序。
流水线机制
#
- 简单的停-等协议效率很低,需要使用流水线机制来提高资源的利用率。
- 例:1Gbps(R)的链路,15ms(RTT)的端到端的传播延迟,8Kb(L)分组。
-

- 使用流水线机制
-

滑动窗口协议
#
- 包括GBN(go back N)和SR(selective repeat)。
GBN
#
- 分组的头部中包含K-bit的序列号。窗口尺寸为N,最多允许N个分组未确认。ACK(n)表示序列号n(包括n)之前的分组均已被正确接收。
- 只为序列号最小的那个未收到ACK的分组设置计时器。
- 当发生了超时事件,即序列号n的分组未收到对应的ack时,会重传序列号大于等于n的所有分组。
- GBN发送方的FSM。base表示当前滑动窗口的起始位置,nextseqnum表示发送的分组的位置,下面的介绍是并行进行的。
- 当序列号小于滑动窗口的右边界时,会持续发送分组,直到达到滑动窗口的右边界。此时会启动定时器并拒绝上层协议发来的数据。
- 当定时器超时时,会重新发送滑动窗口左边界开始的窗口中的所有数据。
- 当正确接收到ack时,滑动窗口会向右移动,同时重新启动定时器;当数据已经发完了时,会关闭定时器。
-

- GBN接收方的FSM
- GBN只发送拥有最高序列号的、已被正确接收的分组的ACK,这就意味着序号为n的分组被正确接收时,序号小于n的所有分组都被成功接收。
- 对于乱序到达的分组,接收方会直接丢弃,并且为最近按序接收的、序列号最大的分组重新发送一个ACK。
-

- 案例
- 分组0和分组1均被正确接收,分组2由于丢失了。发送方发送过去的分组3、分组4、分组5,接收方均重发ACK(1),当一段事件后发送方一直未收到ACK(2),所以会重新发送分组2、分组3、分组4、分组5。
-

SR
#
-
GBN单个分组的差错就能引起大量分组的重传。选择重传能仅让发送方重传个别它怀疑在接收方出错的分组,避免了不必要的重传。
-
SR接收方确认一个正确接收的分组而不管其是否按序,失序的分组将被缓存直到所有的丢失分组(序号更小的分组)都被收到为止。
-

-
序列号的空间大小必须大于等于窗口长度的两倍。
-
例:当窗口大小为4,序号大小也为4,发送方连续发送4个分组,接收方收到4个分组后,返回4个ACK,窗口已经向右滑动了4个。此时假如4个ACK都丢了,发送方超时后重新发送4个分组,此时接收方把分组0、1、2、3当作新的分组,就会导致错误。
-

-
例:当窗口大小为4,序号大小为5,发送方连续发送4个分组,接收方收到4个分组后,返回4个ACK,窗口已经向右滑动了4个。此时假如4个ACK都丢了,发送方超时后重新发送4个分组,此时接收方把分组0、1、2当作新的分组,就会导致错误。
-

-
例:当窗口大小为4,序号大小为7,发送方连续发送4个分组,接收方收到4个分组后,返回4个ACK,窗口已经向右滑动了4个。此时假如4个ACK都丢了,发送方超时后重新发送4个分组,此时接收方把分组0当作新的分组,就会导致错误。
-

TCP
#
- 一个应用进程向另一个应用进程发送数据之前,会现在两个进程之间建立一条逻辑链路。当一端通过套接字传递数据,TCP将这些数据引导到该连接的发送缓存中,发送缓存时三次握手期间设置的缓存之一。接下来TCP会不时从发送缓存中取出一块数据,并将数据传送到网络层。
- TCP从缓存中取出并放入段中的数据大小受限于MSS(maximum segment size,即最大报文段长度)。MSS通常根据MTU(maximum Transmission Unit,最大传输单元)来设置。通常MTU为1500字节,TCP首部一般20字节,IP首部一般20字节,所以MSS是一般情况为1440字节。
TCP报文段结构
#
- 32位的序号和确认号用来实现可靠数据传输。
- 16位的接受窗口字段用来进行流量控制。
- 4位的首部长度用来指示以32bit的字为单位的TCP首部的长度。
- 可选和变长的选项字段,在协商MSS或调节窗口因子时使用。
- 6位的标志字段。ACK用于指示确认号的值是有效的,即该报文段包含一个对已被接收报文段的确认。RST、SYN、FIN用于连接的建立和拆除。
序号和确认号
#
- 一个报文段的序号是报文段首字节的编号,TCP连接双方都会随机地选择初始序号。例:假如MSS为1000,一个数据流的大小为5000字节,初始序号为234。则第一个报文段的序号为234,第二个报文段的序号为1234,第三个报文段的序号为2234。
- 确认号是接收方期望从发送方接收到的下一个字节的序号。TCP是累计确认的,如果收到了确认号为2234的报文段,则说明2234(不包含2234)之前的报文段都被成功接收。
往返时间的估计和超时
#
- TCP采用超时重传机制来处理报文段的丢失问题。超时时间间隔必须大于连接的往返时间(RTT),否则会造成丢包。
- SampleRTT是某报文段从发出到对该报文段的确认被收到之间的时间量。大多数TCP的实现是在某个时刻为一个已发送但未被确认的报文段估计SampleRTT,TCP不会对已被重传的报文段计算sampleRTT。
- 由于sampleRTT可能会有大的波动,TCP会维持一个EstimatedRTT,一旦获取一个新的sampleRTT,TCP会按照如下公式更新EstimatedRTT。α的推荐值为0.125。
EstimatedRTT = ( 1 - α ) * EstimateRTT + α * SampleRTT
- DevRTT用来计算sampleRTT偏离EstimatedRTT的程度, β的推荐值为0.25。
DevRTT = ( 1 - β ) * DevRTT + β * | SampleRTT - EstimatedRTT |
- 超时时间TimeoutInterval的计算如下,推荐初始的TimeoutInterval为1秒。当收到报文段并更新EstimatedRTT时,就会更新TimeoutInterval。
TimeoutInterval = EstimatedRTT + 4 * DevRTT
- 当出现超时后,会直接把TimeoutInterval加倍。例:假设当前的过期时间为0.75,当定时器超时后,TCP会重传报文段,并把过期时间设置为1.5s,当再次过期时,TCP会重传报文段,并将过期时间设置为3s。
可靠数据传输
#
- 快速重传是指如果TCP发送方接收到对相同数据的3个冗余ACK,即收到了一个正常的ACK(n)和三个冗余的ACK(n)时,TCP会重传序号为n的段,即使当前未超时。
-
- TCP采用累积确认,当受到ACK(n)则说明n之前的字节(不包括n)都已成功收到,存在如下的三种情况。
-
-
流量控制
#
-
一台TCP连接的每一侧主机都为该连接设置了接受缓存,为了防止缓存溢出,TCP提供了流量控制服务(flow-control service)。
-
TCP的发送方也可能因为IP网络的拥塞而遏制,这种控制方式为拥塞控制(congestion service)。
-
TCP通过让发送方维护一个叫接收窗口(Receive Window)的变量来提供流量控制,该字段是接收方的剩余缓存空间大小。因为TCP是全双工的,连接两端都会维护接收窗口。
-
假设主机A向主机B发送一个文件,主机B的接收缓存大小为RcvBuffer,LastByteRead用来表示主机B的应用程序从缓存中读到的最后一个字节编号,LastByteRcvd用来表示主机A发送到主机B且放入到接收缓存的最后一个字节的编号。
-
接收窗口rwnd的大小的计算如下,该字段是动态变化的,最开始rwnd等于RcvBuffer。
#接收窗口大小 = 接收缓存总大小 - 已接收但是未读的数据
# 1. 如果发的数据快,但是读数据慢,接收方来不及处理时,导致接收窗口变小
rwnd = RcvBuffer - ( LastByteRecvd - LastByteRead )
- 主机B通过把rwnd放到它发送给主机A报文段的接收窗口字段中,通知A该连接中还存在多少的可用缓存。主机A会跟踪两个变量,LastByteSent用来表示最后发送的一个字节编号,LastByteAcked用来表示最后一个确认的字节编号,通过如下关系来保证发送的数据不会造成缓存溢出。
#接收窗口大小 >= 未确认的数据
# 1. 当接收窗口变小后,发送方已发送但未确认的这部分数据减少
# 2. 当接收窗口大小为0时,发送方停止向接收方发送数据,但是还是会发送特殊的段,以便获取rwnd。
LastByteSent - LastByteAcked ≤ rwnd
- 为了避免主机B的接收缓存已满,然后发送给主机A rwnd=0,此时主机A不会在给主机B发送数据的情况,TCP要求当主机B的缓存已满时,主机A继续发送只有一个字节数据的报文段。这些报文段会被接收方确认,等主机B的缓存空间不为0时,会发送给主机A非0的rwnd值。
TCP连接管理
#
-
三次握手
-
- 客户端会像服务端发送一个特殊的报文段,该报文段不携带任何数据,SYN标志位被置为1,并且会随机选择一个序号client_isn,发送给服务器。该报文段被称为SYN报文段。
- 当该服务端收到SYN报文段后,为TCP连接分配缓存和变量,并向客户端发送报文段,该报文段SYN标志位置为1,并随即生成一个序号server_isn,同时ACK标志位置为1,确认号填client_isn+1,改报文段不包含任何数据。该报文段被称为SYNACK报文段。
- 客户端收到SYNACK后,为TCP连接分配缓存和变量,向服务端发送报文段,该报文段可以携带数据,SYN标志位被置为0(因为连接已建立)。同时ACK标志位置为1,确认号填server_isn+1。
-
TCP连接关闭
-
- 当客户端发起关闭连接时(服务端也可以发起),会向服务端发送一个特殊报文段,该报文段的FIN报文段被置为1,同时生成一个seq序号y,发给服务端。其实此时ACK标志位也会被置为1,用来确认上一次发来的数据。
- 服务端收到后,会将ACK标志位置为1,ack填y+1。
- 服务端会重复1~2步。
- 一次HTTP请求的连接过程,第三次握手时就已经开始带上数据了。
-
- 洪范攻击(SYN flood attack)指攻击者发送大量的TCP SYN段,却不完成第三次握手,由于服务端在第二次握手就开始分配资源,导致服务端资源被消耗殆尽。一种有效的防御机制SYN cookies能解决这个问题,】工作方式如下:
- 当服务端收到一个SYN段时,它不会开启一个半开的连接。它会根据源和目标的IP和端口以及秘密数通过散列函数来生成一个初始的TCP序号,这个序号被称为"cookie",服务端将这个特殊的SYNACK段发给客户端。
- 当服务端收到一个ACK时,通过散列函数来计算一个值,如果该值加1等于ACK,则说明改ACK是对之前的某个SYNACK的确认,服务端会生成一个具有套接字的全开链接。
- 如果客户端没有返回一个ACK,对服务器也没有影响。
- 当一台服务器接收到了一个TCP报文段,该报文段的目的端口或源IP与服务器上的套接字都不匹配时,服务器会向源发送一个特殊的重置段,该段的RST标志位被置为1。当一台主机接收到了一个UDP分组,它的目的端口与主机上的套接字都不符合,主机会发送一个ICMP数据报。
TCP拥塞控制
#
- 运行在发送方的TCP拥塞控制机制跟踪一个额外的变量拥塞窗口cwnd(congestion window),它对TCP发送方能向网络中发送的流量进行了限制,即:
LastByteSent - LastByteAcked ≤ min { rwnd, cwnd }
- TCP拥塞控制算法:1. 慢启动;2. 拥塞避免;3. 快速恢复。
慢启动
#
- 当TCP连接开始时,cwnd通常设置为MSS(存疑),这使得初始的发送速率约为MSS/RTT;
- TCP向网络中发送第一个报文段并等待一个确认,当确认到达时,将cwnd增加一个MSS;并发送出两个段,当着两个段被确认时,每个确认都将使cwnd增加一个MSS,这使得现在cwnd变成了4个MSS。总结:每经过一个RTT,
cwnd = cwnd * 2
。
-
-
拥塞窗口不会无限增大:
- 如果发生了丢包事件,TCP发送方将cwnd设置为1并重新开始慢启动过程,并设置ssthresh(慢启动阈值)的值为cwnd/2,当达到ssthresh后,cwnd线性增长;
- 如果检测到3个冗余ACK(连续收到4个相同的ACK,重复的ACK表示网络有能力来传输段)时,cwnd减半,并开始线性增长。(TCP RENO)
- 如果检测到3个冗余ACK,将cwnd设置为1(TCP Tahoe)
-
2022-05-25
wireshark
#
eq ==
ne !=
gt >
lt <
ge >=
le <=
# 指定来源ip、目的端口和协议
ip.src eq 10.2.4.50 and http and tcp.dstport eq 40010
tcp.port in {80, 443, 8080}
http.request.method in {"HEAD", "GET"}
#支持使用range
ip.addr in {10.0.0.5 .. 10.0.0.9, 192.168.1.1 .. 192.168.1.9}
#支持子网的形式
ip.addr == 129.111.0.0/16
2022-05-19
server {
listen 20000;
location / {
root /opt/share;
#开启自动映射目录
autoindex on;
charset utf-8;
#时间设置为本地时间
autoindex_localtime on;
#通过字节精确表示大小
autoindex_exact_size on;
}
}
2022-05-05
HTTP
#
- 网页包含多个对象,如HTML文件、JPEG图片、视频文件、动态脚本等,基本HTML文件包含对其他对象引用的链接。通过URL可以进行对象的寻址。
- HTTP协议流程:
- 服务器在80端口等待客户的请求
- 浏览器发起到服务器的TCP连接(创建套接字Socket)
- 浏览器与Web服务器交换HTTP消息
- 关闭TCP连接
- HTTP是无状态的协议,即服务器不维护任何有关客户端过去所发请求的信息。
HTTP连接
#
- HTTP包含非持久性连接和持久性连接
- 非持久性连接指的是每个TCP连接最多允许传输一个对象,HTTP1.0使用非持久性连接。
- 持久性连接指每个TCP连接可以传输多个对象,HTTP1.1默认使用持久性连接。
HTTP消息格式
#
- HTTP有两类消息,请求消息(request)和响应消息(response)。
- HTTP请求消息的格式如下:
-

- HTTP响应消息的格式如下:
-

Web缓存
#
- 如果浏览器缓存过数据,当下次发送相同请求时,浏览器向服务器发送HTTP请求,并带上
If-modified-since: <date>
。
- 如果对象未改变,则返回
304 Not Modified
,不反会对象,表示对象未被修改。
- 如果对象发生改变,服务器会返回对象。
DNS
#
- DNS的是分布式数据库。提供了域名向IP地址的翻译、主机别名等功能。
- 一般来说是本地域名解析服务器代替进行域名解析的,当主机进行DNS查询时,查询会被发送到本地域名服务器,当本地域名服务器无法解析域名时,就会访问根域名服务器。全球共13个根域名服务器。
- 顶级域名服务器(TLD,top-level domain),负责com、org、net等顶级域名和国家顶级域名,如cn、uk等。
- 权威域名服务器是组织的域名解析服务器,提供组织内部服务器的解析服务。
- 当
cis.poly.edu
想获取gaia.cs.umass.edu
的IP地址时,迭代查询流程如下
-

- 递归查询的流程如下(将域名解析的任务交给所联系的服务器)
-

- 只要域名解析服务器获得域名时,就会缓存这一映射,一段时间后缓存条目才会失效。
DNS记录
#
- 资源记录格式为(name, value, type, ttl),类型如下
- type=A,Name为主机域名,Value为IP地址
- type=NS,Name为域(如edu.cn),value为该域权威域名解析服务器的主机域名。
- type=CNAME,name为某一真实域名的别名,value为真实域名
- type=MX,value是与name对应的邮件服务器。
DNS协议消息格式
#
- DNS查询(query)和回复(reply消息)的格式相同。
- 消息头部
- Identification:16为查询编号,回复使用相同的编号。
- flags表示查询或回复、期望递归、递归可用、权威回答。
-

socket
#
- 对外通过IP地址+端口号表示通信端点。
- 操作系统通过套接字描述符(socket descriptor)来管理套接字。
- socket类似于文件,当应用进程创建套接字时,操作系统分配一个数据结构存储该套接字相关信息。
-

地址结构
#
- 使用TCP/IP协议簇的网络应用程序声明端点地址变量时,使用结构socketaddr_in
struct sockaddr_in
{
u_char sin_len; /*地址长度 */
u_char sin_family; /*地址族(TCP/IP:AF_INET) */
u_short sin_port; /*端口号 */
struct in_addr sin_addr; /*IP地址 */
char sin_zero[8]; /*未用(置0) */
}
socket api函数
#
socket函数
#
sd = socket(protofamily,type,proto);
创建套接字并返回套接字描述符。
- 第一个参数指定协议族:protofamily=PF_INET(TCP/IP)
- 第二个参数指定套接字类型:SOCK_STREAM(TCP), SOCK_DGRAM(UDP), SOCK_RAW(面向网络层)
- 第三个参数指定协议号:0为默认
- 例
struct protoent *p;
p=getprotobyname("tcp");
SOCKET sd=socket(PF_INET,SOCKET_STREAM,p->p_proto);
close
#
int close(SOCKET sd)
关闭一个描述符为sd的套接字
- 如果多个进程共享一个套接字,调用close将套接字引用计数减1,减至0才关闭。
- 一个进程中的多线程对一个套接字的使用无计数。
- 返回值 0:成功,SOCKET_ERROR:失败。
bind
#
int bind(sd,localaddr,addrlen);
绑定套接字的本地端点地址
- 客户端一般不必调用bind函数,一般由服务端调用。
- 一台机器可能由多个网卡,可用使用地址通配符INADDR_ANY来绑定。
listen
#
int listen(sd,queuesize);
置服务器端的流套接字处理处于监听状态。
- 仅服务端调用,仅用于面向连接的流套接字。
- queuesize表示连接请求的队列大小。
- 返回值 0:成功,SOCKET_ERROR:失败。
connect
#
connect(sd,saddr,saddrlen)
客户端调用connect函数来使客户端套接字(sd)与特定计算机的特定端口(saddr)的套接字服务进行连接。
- 仅用于客户端,可用于TCP客户端也可以用于UDP客户端。
accept
#
newsock = accept(sd,caddr,caddrlen);
服务程序调用accept函数从处于监听状态的流套接字sd的客户端连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道。
- 仅用于TCP套接字,仅用于服务器。
- 服务器会利用新创建的套接字(newsock)与客户端通信。
send
#
send(sd,*buf,len,flags);
sendto(sd,*buf,len,flags,destaddr,addrlen);
- send:发送数据(用于TCP套接字或连接模式(调用了connect函数)的客户端UDP套接字)
- sendto函数用于UDP服务器端套接字与未调用connect函数的UDP客户端套接字发送数据
recv
#
recv(sd,buffer,len,flags);
recvfrom(sd,buf,len,flags,senderaddr,saddrlen);
- recv函数从TCP连接的另一端接收数据,或者从调用了connect函数的UDP客户端套接接收服务器发来的数据
- recvfrom函数用于从UDP服务器端套接字与未调用connect函数的UDP客户端套接字接收对端数据
setsockopt,getsockopt
#
int setsockopt(int sd, int level, int optname, *optval, int optlen);
int setsockopt(int sd, int level, int optname, *optval, int optlen);
- setsockopt()函数用来设置套接字sd的选项参数
- getsockopt()函数用于获取任意类型、任意状态套接口的选项当前值,并把结果存入optval
网络字节序
#
- 网络字节序采用大端排序方式(低位低地址,高位高地址)
- 某些Socket API函数的参数需要存储为网络字节顺序(如IP地址、端口号等)
- 转换函数
- htons: 本地字节顺序→网络字节顺序(16bits)
- ntohs: 网络字节顺序→本地字节顺序(16bits)
- htonl: 本地字节顺序→网络字节顺序(32bits)
- ntohl: 网络字节顺序→本地字节顺序(32bits)
解析服务器IP地址
#
- 客户端可能使用域名或IP地址标识服务器,IP协议需要使用32为二进制IP地址,需要将函数名或IP地址转换为32为IP地址。
- inet_addr可用实现点分十进制IP地址到32位IP地址转换。
- gethostbyname实现域名到32位IP地址转换。会返回一个指向结构hostent的指针。
struct hostent {
char FAR* h_name; /*official host name */
char FAR* FAR* h_aliases; /*other aliases */
short h_addrtype; /*address type */
short h_lengty; /*address length */
char FAR* FAR* h_addr_list; /*list of address */
};
#define h_addr h_addr_list[0]
解析服务器端口号
#
- 客户端可能使用服务名(如HTTP)标识服务器端口,需要将服务名转换为熟知端口号
- getservbyname会返回一个指向结构servent的指针。
struct servent {
char FAR* s_name; /*official service name */
char FAR* FAR* s_aliases; /*other aliases */
short s_port; /*port for this service */
char FAR* s_proto; /*protocol to use */
};
解析协议号
#
- 客户端可能使用协议名来指定协议,需要将协议名转换为协议号
- 函数getprotobyname实现协议名到协议号的转换。会返回一个protoent的指针。
struct protoent {
char FAR* p_name; /*official protocol name */
char FAR* FAR* p_aliases; /*list of aliases allowed */
short p_proto; /*official protocol number*/
};
TCP客户端软件流程
#
- 确定服务器IP地址和端口号
- 创建套接字
- 分配本地端点地址(可选)
- 连接服务器(套接字)
- 遵循应用层协议进行通信
- 关闭/释放连接
UDP客户端软件流程
#
- 确定服务器IP地址与端口号
- 创建套接字
- 分配本地端点地址(IP地址+端口号)
- 指定服务器端点地址,构造UDP数据报
- 遵循应用层协议进行通信
- 关闭/释放套接字