计算机网络总结

目录

一.Socket编程

二.网络基础理论:

1.网络协议的初识:

(1)协议:

(2)网络完成的事情是:

(3)网络协议:

(4)结论:

2.网络协议的分层:

a.OSI分层模型---网络理论模型

b.TCP/IP分层模型-----工业中采用的网络模型

c.TCP/IP 四层模型

3.为什么要有网络分层?

(1)从软件的层面来考虑

(2)从实现层面讲

4.网络数据的封装与复用:

(1)封装:

(2)复用:

(3)结论:

5、IP地址和MAC地址:

(1)IP地址:

(2)MAC地址:

三.TCP与UDP区别:

四.TCP粘包问题:

1.具体内容:

2.产生原因:

(1)发送方原因:

(2)接收方原因:

3.处理时机:

4.解决方法:

(1)发送方:

(2)接收方:

(3)应用层:

具体可行的解决方案如下:

5.UDP会不会产生粘包问题:

四.TCP三次握手,四次挥手:

五.套接字编程:

1.认识端口—port:

(1)本质:

(2)作用:

(3)注意事项:

(4)一些知名端口:

2.网络数据的五元组信息

(1)源IP:

(2)目的IP:

(3)源端口:

(4)目的端口:

(5)协议:

3.网络字节序:

(1)定义:

(2)分类:

(3)如何判断自己的机器遵守的是哪一种存储规则:

(4)主机字节序:

4.主机字节序与网络字节序的相互转换:

(1)原因:

(2)具体过程:

5.UDP_socket编程:

(1)编程流程:

(2)编程接口:

3.TCP_socket编程

(1)编程流程

(2)编程接口(较UDP编程新增的接口):

(3)如何同时让多个客户端都享受服务:

(4)TCP+多进程:

五.五种IO模型以及select编程:

1.五种IO模型

1.1阻塞IO

1.2 非阻塞IO

1.3 信号驱动I0

1.4 异步IO

1.5 IO多路转接

2.多路转接IO(select)

2.1 接口

2.2 select使用事件集合方式

3.多路转接模型-poll:

1. 历史地位:

2. 作用:

3. 接口:

4.poll的优缺点

4.多路转接模型epoll:

1. 历史定位

2. 接口

3.epoll监控的内部工作原理:

4. epoll的工作模式:

六.面试题:

1.TCP拥塞控制机制?

(1)慢启动:

(2)拥塞避免:

(3)快重传

(4)快恢复

2.TCP三次握手异常情况:

(1)client第一个syn包丢失,没收到server的ack:

(2)server收到了client的syn,并发出了syn+ack包,syn+ack包丢失:

(3)当Client端收到Server的SYN+ACK应答后,其状态变为ESTABLISHED,并发送ACK包给Server;如果此时ACK在网络中丢失:

3.四次挥手异常情况:

(1)client发的FIN包丢了

(2)server回client的ACK包丢了

(3)如果client收到ACK后,server直接跑路。

(4)server发的FIN包丢了

(5)client回复ACK后

(6)为什么要这有TIME_WAIT?为什么不直接给转成CLOSED状态呢?

4.为什么要三次握手,而不是二次握手:

5.三次握手流程:

6.Server端易受到SYN攻击?

7.预防SYN攻击:

8.四次挥手流程:

9.挥手为什么需要四次?

10.2MSL等待状态

11.四次挥手释放连接时,等待2MSL的意义

12.为什么TIME_WAIT状态需要经过2MSL才能返回到CLOSE状态

13.三次握手过程中可以携带数据吗

14.什么是半连接队列

15.初始化序列(ISN)是固定的吗

16.SYN攻击是什么

17.HTTP与HTTPS

1.HTTP:

2.HTTP协议连接方式:

3.HTTPS:

4.HTTP与HTTPS的区别:

5.一段HTTP报文包含三部分:

18.如何使用UDP实现TCP:

1.确认应答机制

2.超时重传机制

3.连接管理机制

4.流量控制机制


一.Socket编程

Socket是连接运行在网络上的两个程序间的双向通讯的端点。
解释:
1.网络上的两个程序(进程)若想通信,
则要通过一个双向的通信连接实现数据的交换,
这个连接的一端称为一个socket。     
2.Socket用于描述IP地址和端口,
可以标识一个端点,实现端点之间的通信。
过程详解:
1.服务器程序将一个套接字绑定到一个特定的端口(一个应用),
并通过此套接字等待和监听客户的连接请求。
2.客户程序根据服务器程序所在的主机名和端口号发出连接请求。

二.网络基础理论:

存在网络的原因:
本质是计算机数量由少变多,
计算机由单台完成某个功能编程多台协作完成某个功能的情况。
进而演变出来的数据传输的解决方案。
分类:
局域网(LAN):距离一般不超过10km
城域网(MAN):几千米到数百千米
广域网(WAN):数百千米到数千千米甚至上万千米

1.网络协议的初识:

(1)协议:

约定沟通双方传递信息的格式

(2)网络完成的事情是:

将数据从A主机的a进程传递到B主机的b进程

(3)网络协议:

约定网络主机在传输数据时候的格式

(4)结论:

a.网络数据 = 协议部分 + 应用层数据
在网络中的数据不仅仅只有数据本身,还需要有协议的内容,协议的内容帮助要传输的数据正确的在网络当中传输到对方的主机当中。
b.基于操作系统和库函数写出来的程序,都是应用层程序,产生的数据,称之为应用层数据
c.协议部分,在Linux操作系统中也是采用描述的手法,描述本质上就是一个结构体。换句话说,协议部分的内容就是就结构体数据。

2.网络协议的分层:

a.OSI分层模型---网络理论模型

从底层到顶层依次是:
物理层:以”0“,”1“代表电压的高低电平,灯光亮暗,界定连接器和网线规格。
数据链路层:互联设备之间传送和识别数据帧。
网络层:地址管理和路由选择。
传输层:管理两个节点之间的数据传输,负责可靠传输,保证数据被可靠传输到目的地址。
会话层:通信管理。负责建立和断开通信连接,管理传输层以下的分层。
表示层:设备固有数据格式和网络标准数据格式的转换。
应用层:针对特定应用的协议。

b.TCP/IP分层模型-----工业中采用的网络模型

从底层到顶层分别是:
物理层:负责将内存二进制数据转为光信号。以太网协议,典型设备:集线器
数据链路层:负责相邻数据间的转发。以太网协议,典型设备:交换机
网络层:路由选择,如何进行路由传输:源ip/目的ip。IP协议,典型设备:路由器
传输层:端与端之间传输。UDP协议、TCP协议:源端口/目的端口
应用层:产生应用层数据(QQ、微信等)。HTTP/HTTPS/SSH/FTP/SMTP

c.TCP/IP 四层模型

TCP/IP协议体系结构四层分别是:

1、数据链路层;实现网卡接口的网络驱动程序,以处理数据在物理媒介上的传输。

2、网络层;实现数据包的选路和转发。

3、传输层;为两台主机上的应用程序提供端到端的通信。

4、应用层;负责处理应用程序的逻辑。

3.为什么要有网络分层?

对于这个问题,可以从两个方面来说:

(1)从软件的层面来考虑

将网络的功能解耦开来,有负责应用层数据、有负责端与端之间的传输,有负责路由等等!

(2)从实现层面讲

分层当中的各个协议完成各自的协议功能即可,只需要将不同层之间相互通信的接口设计匹配就行。至于每一层的内部实现什么功能,如何实现,这些都可以单独考虑,也就便于实现。

4.网络数据的封装与复用:

(1)封装:

发送方数据封装的过程:

应用层将应用层数据传给传输层,传输层增加传输层协议的报头包括源端口/目的端口,之后传给网络层,网络层增加网络层协议的报头包括源IP/目的IP,之后传给数据链路层,数据链路层增加数据链路层协议的报头和CRC校验帧尾,之后传给物理层,物理层不对数据包做处理,直接发送给路由器经过若干次转发达到目的主机。

(2)复用:

接收方对网络数据进行层层分用:

物理层将广电信号转换为二进制后交给数据链路层,数据链路层去掉数据链路层的报头以及CRC校验帧尾后交给网络层,网络层去掉网络层的协议报头后交给传输层,传输层去点传输层的协议报头后交给应用层,应用层得到发送方的应用层数据。

(3)结论:

a.应用层数据经过网络传输的时候,需要经过网络协议栈的封装,到达对端之后,需要经过网络协议栈的分用。
b.网络协议栈封装的时候,是增加了协议的内容。目的是为了在网络当中能够正确的传输

5、IP地址和MAC地址:

(1)IP地址:

a.分类:目前有两个版本的IP地址,分别是IPv4和IPv6版本的IP地址
b.本质:IPv4版本的IP地址,本质是一个无符号的32位整数,范围是[0,232-1]
c.作用:在网络中标识一台主机
d.表示形式:一般使用点分十进制的方式表示IPv4版本的IP地址,将4字节按照1字节使用“.”的方式进行分割。因此点分十进制的方式,每一个数字的范围是[0.28-1]也即[0,255]。
f.两点注意:
①一个IP地址只能被一台机器占用
②一台机器可以占有多个IP地址

(2)MAC地址:

a.本质:长度为48比特位即6字节,一般使用16进制数据加上冒号的方式来表示,例如(08:89:24:fc:19:02)
b.作用:
①标识具体的某一块物理网卡设备,网卡设备在出厂时,都会打上全球独一无二的MAC地址
②MAC地址用来识别数据链路层中相连的节点
c.使用ifconfig命令查看自己的Linux机器的网卡信息,win机器使用ipconfig/all

三.TCP与UDP区别:

1.UDP无连接:UDP双方在发送数据之前不需要沟通,只需对方IP和端口便可发送
TCP面向连接:TCP在发送数据前建立连接
(1)确保对方能正常通讯
(2)沟通双方发送后续数据的细节(如序号)
2.UDP不可靠:不保证数据是可靠、有序到达对方
(1)可靠:传输数据一定可以到达目的主机
(2)有序:如发送方分别发送ABCD四个数据包,接收方应按ABCD这个顺序接收到
TCP可靠传输:保证传输的数据是可靠、有序到达对端

3.UDP面向数据报:UDP在和应用层或网络层递交数据是以整条数据交付,UDP不缓存应用层数据,而是直接加上UDP包头交给网络层
TCP面向字节流:
(1)对于传输的数据没有明显边界
(2)对接收方可以按任意字节接收(提高传输效率)
PS:存在TCP粘包问题

四.TCP粘包问题:

1.具体内容:

指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方。

2.产生原因:

(1)发送方原因:

TCP默认使用Nagle算法(主要作用:减少网络中报文段的数量),而Nagle算法主要做两件事:
a.只有上一个分组得到确认,才会发送下一个分组
b.收集多个小分组,在一个确认到来时一起发送
Nagle算法造成了发送方可能会出现粘包问题

(2)接收方原因:

TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上,TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包会被缓存,应用程序有可能读取到多个首尾相接粘到一起的包。

3.处理时机:

1.如果发送方发送的多组数据本来就是同一块数据的不同部分,比如说一个文件被分成多个部分发送,这时当然不需要处理粘包现象

2.如果多个分组毫不相干,甚至是并列关系,那么这个时候就一定要处理粘包现象了

4.解决方法:

(1)发送方:

对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法。

(2)接收方:

接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。

(3)应用层:

应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。

具体可行的解决方案如下:

循环处理,应用程序从接收缓存中读取分组时,读完一条数据,就应该循环读取下一条数据,直到所有数据都被处理完成。
如何判断每条数据的长度:
(1)格式化数据:每条数据有固定的格式(开始符,结束符),这种方法简单易行,但是选择开始符和结束符时,一定要确保每条数据的内部不包含开始符和结束符。
(2)发送长度:发送每条数据时,将数据的长度一并发送,例如规定数据的前4位是数据的长度,应用层在处理时,可以根据长度来判断每个分组的开始和结束位置。

5.UDP会不会产生粘包问题:

TCP为了保证可靠传输并减少额外的开销(每次发包都要验证),采用了基于流的传输,基于流的传输不认为消息是一条一条的,是无保护消息边界的。

保护消息边界:传输协议把数据当做一条独立消息在网上传输,接收端一次只能接受一条独立的消息。UDP则是面向消息传输的,是有保护消息边界的,接收方一次只接受一条独立的信息,所以不存在粘包问题。

举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,必须进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。

四.TCP三次握手,四次挥手:

五.套接字编程:

1.认识端口—port:

(1)本质:

端口号是一个2字节16位的无符号整数,范围是[0,65535]

(2)作用:

端口号是用来标识一个进程,告诉操作系统,当前的数据要交给那一个进程来处理

(3)注意事项:

a.一个端口只能被一个进程占用
b.一个进程可以占用多个端口

(4)一些知名端口:

[0,1023]范围内的端口已经被一些知名的协议所使用,在编写代码时不要使用该范围内的数据作为端口号。MySQL----3306端口,Oracle----1521端口

2.网络数据的五元组信息

{源IP、 目的IP 、源端口、目的端口、 协议}

(1)源IP:

标识网络数据是从哪台主机发出的

(2)目的IP:

标识数据要去往哪一台主机

(3)源端口:

标识网络数据是从“源IP”对应的这台主机的哪个进程产生的

(4)目的端口:

通过目的IP找到目的主机后,需利用目的端口找到对应进程

(5)协议:

标定双方传输数据时使用的协议,一般是UDP/TCP

3.网络字节序:

(1)定义:

字节序又称为端序或者尾序,指多字节数据在内存中存放的顺序。

(2)分类:

小端字节序和大端字节序,是两种数据在内存中存放顺序的不同规则
a.小端字节序:数据的低权值位对应空间的低地址
b.大端字节序:数据的低权值位对应空间的高地址

(3)如何判断自己的机器遵守的是哪一种存储规则:

方式一:指针+变量+强制类型转换验证:
int fun()
{
int num=1;
//对整形数据num,按照char即一个字节的步长进行访问
如果返回结果是1,说明数据的低权值位保存在内存的低地址处,即小端存储,反之则为大端。
return *((char*)&num);
}
方式二:利用联合体的存储特性来判断:
int main()
{
//联合体这一结构的特点是:所有成员共用同一块内存空间,所有成员的首地址都一样,都是联合体变量的首地址
因此:提供两个变量,一个为int,一个为char,int变量的初始值设置为1,判断char类型的元素值即可得到结果。
union u
{
 int a;
 char b;
};
union u uu;
uu.a=1;
if(uu.b==1)
{
  printf("小端\n");
}
else
{
  printf("大端\n");
}
return 0;
}

(4)主机字节序:

指机器本身的字节序,如果是大端,则主机字节序为大端,
如果是小端,则主机字节序为小端。
网络字节序:规定网络传输数据时采用大端字节序进行传输。

4.主机字节序与网络字节序的相互转换:

(1)原因:

既然网络字节序是大端字节序,现在假设有AB两台主机,他们之间需要通过网络进行通信,分析A向B发送消息这一单程。A向B发送的数据,通过网络传输时一定要转换为网络字节序,否则传输的数据可能会出错(这取决于主机A是大端还是小端机器)B从网络中接收A发送的数据时,也需要将数据从网络字节序转换为B主机的主机字节序。

(2)具体过程:

这个具体的转换过程不需要自己实现,OS提供了转换的接口:
a.主机字节序->网络字节序:
ip:uint32_t
uint32_t htol(uint32_t hostlong)
port:uint16_t
uint16_t htos(uint16_t hostshort);
b.网络字节序->主机字节序:
ip:uint32_t
uint32_t ntohl(uint32_t netlong)
port:uint16_t
uint16_t ntohs(uint16_t netsshort);

5.UDP_socket编程:

(1)编程流程:

a.服务端:
Ⅰ、创建套接字
Ⅱ、绑定地址信息
Ⅲ、收发消息
Ⅳ、使用完毕后关闭套接字
b.客户端:
Ⅰ、创建套接字
Ⅱ、不推荐绑定地址信息,不推荐在代码手动绑定地址信息
Ⅲ、收发消息
Ⅳ、使用完毕后关闭套接字
c.总结:
[1]为什么创建套接字:
将进程和网卡进行绑定,进程可从网卡中接收消息,也可通过网卡发送消息。
[2]绑定地址信息具体干什么:
绑定IP和端口。目的是为了在网络中表示一台主机和一个进程。这样对于接收方而言,发送数据的人就知道接受方在哪台机器的哪个进程;对于发送方而言,能标识网络数据是从哪台机器的哪个进程发送出去的。

(2)编程接口:

a.创建套接字:#include<sys/socket.h>
[1]原型:int socket(int domain, int type, int protocol)
[2]参数:
domain:地址域:
{1}选择一个具体的协议簇进行沟通。对于我们而言,UDP/TCP,可以认为在指定网络层使用什么协议。
{2}AF_UNIX:本地域套接字,在同一台机器使用文件进行通信,不用跨机器。
{3}AF_INET:IPV4版本的IP协议
{4}AF_INET6:IPV6版本的IP协议
type:套接字的类型:
{1}SOCK_DGRAM
{2}SOCK_STREAM
protocol:协议:
{1}0:表示按照套接字类型选择默认协议
SOCK_DGRAM:UDP协议
SOCK_STREAM:TCP协议
{2}IPPROTO_TCP:对应数字6,代表TCP协议
{3}IPPROTO_UDP:对应数字17,代表UDP协议
[3]返回值:返回套接字操作句柄,本质上时一个文件描述符。大于等于0:创建成功;小于0:创建失败。
b.绑定接口:
[1]原型:int bind(int sockfd,
                 const struct sockaddr* addr, 
                 socklen_t addrlen);
[2]参数:
sockfd:创建套接字时返回的套接字描述符
addr:绑定的地址信息(IP + port)
addrlen:绑定的地址信息的长度
注意:这里的struct sockaddr是一个通用的数据结构
结构如下:
struct sockaddr
{
//地址域,占用2字节(这两个字节不会发生变化)
  sa_family_t sa_family;
  char sa_data[14];
}
在组织参数时,传递的不是上面的这个通用数据结构,而是struct sockaddr_in这个结构体变量,
具体内容如下:
struct sockaddr_in
{
//地址族(域)
  sa_family_t sa_family;
//端口号
  uint16_t sin_port; 
//32位IP地址
struct in_addr sin_addr;-->struct in_addr{in_addr_t s_addr;}
//预留未使用
  char sin_zero[8];
}
c.发送接口:
[1]原型:
ssize_t sendto(int sockfd, const void* buf, size_t len,
            int flags,const struct sockaddr* dest_addr,
            socklen_t addrlen);
[2]参数:
sockfd:套接字描述符
buf:要发送的数据
len:要发送的数据长度
flags:0(阻塞发送)
dest_addr:地址信息结构,包含目的IP,目的端口,表示要把数据送到哪里去
addrlen:地址信息的长度
[3]返回值:成功:返回正常发送的数据;失败:返回-1。
d.2.2.4 接收接口
[1]原型:
ssize_t recvfrom(int sockfd, void* buf, size_t len, 
                 int flags,struct sockaddr* src_addr, 
                 socklen_t* addrlen);
[2]参数:
sockfd:套接字描述符。
buf:程序员准备的接收数据的缓冲区。
len:最大能接收数据的大小,一般为缓冲区的大小。
flags:0(阻塞接收)
src_addr:源IP+源端口
addrlen:是一个出参,返回地址信息的长度
f.关闭接口
int close(int fd)

3.TCP_socket编程

(1)编程流程

a.服务端:
[1]创建套接字
[2]绑定地址信息
[3]监听
[4]获取新连接
[5]收发数据
[6]关闭连接
b.客户端:
[1]创建套接字
[2]不推荐绑定地址信息
[3]发起连接
[4]收发数据
[5]关闭连接
c.总结:
[1]监听的含义:监听TCP客户端新的连接,同客户端建立TCP连接。(此时,TCP的建立在内核中就完成了)
[2]获取新连接的含义:获取新连接的套接字描述符,每一个TCP连接会产生一个新的套接字描述符
[3]发起连接的含义:向服务端发起连接

(2)编程接口(较UDP编程新增的接口):

a.监听接口:
[1]原型:int listen(int sockfd, int backlog)
[2]参数:
sockfd:套接字描述符,一般为服务端创建套接字时产生的套接字描述符。
backlog:
含义:
Linux2.2版本之前:未完成连接请求的数量
Linux2.2版本之后:TCP并发连接数即已完成连接的大小
未完成连接队列:还处于建立连接的连接被放到这个队列中;可以理解为正在进行3次握手的连接在该队列。
已完成连接队列:连接已经建立,可以正常通信的连接放在这个队列,可以理解为3次握手完毕的连接在该队列。可以通过修改/proc/sys/net/ipv4/tcp_max_syn_backlog当中的值,修改未完成连接队列的大小。
[3]返回值:成功—>0;失败—>-1
b.获取新连接:
[1]原型:int accept(int sockfd, struct sockaddr* addr,
                   socklen_t* addrlen);
[2]参数:
sockfd:套接字描述符
addr:地址信息结构体,用来描述客户端地址信息
addrlen:地址信息长度
[3]返回值:成功—>新连接的套接字;失败—>-1
[4]注意:该接口具有阻塞特性
{1}如果已完成连接队列中没有已经建立连接的连接,则阻塞。
{2}如果有,获取新连接后返回。
[5]关于获取新连接这一接口的理解:
{1}返回的新连接的套接字,是为了和客户进行通信的,只不过这个套接字没进行监听功能,同时有客户端的地址信息。
{2}多个客户端发起连接,在服务端会创建多个新连接的套接字。
{3}服务端使用socket创建的套接字描述符,是一个侦听套接字,主要职责是侦听是否有新的连接到来。
服务端使用accept创建出来的套接字,被称为连接套接字,主要职责是同客户端通信。
c.发起连接:
[1]原型:int connect (int sockfd, 
        const struct sockaddr* addr,socklen_t addrlen)

[2]参数:
sockfd:套接字描述符,一般是客户端创建套接字时的返回值。
addr:地址信息结构,藐视服务端的地址信息(服务端的IP和port)
addrlen:地址信息长度
[3]返回值:成功—>0;失败—>-1
[4]该函数不仅可以完成连接功能,
如果客户没进行绑定,同时会绑定客户端的地址信息。
d.接收数据:
[1]原型:ssize_t recv(int sockfd, void* buf,
                     size_t len, int flags);
[2]参数:
sockfd:套接字描述符
buf:将接收到的数据存放在buf指定的空间,空间需要程序员提前开辟
len:期望接收的字节个数
flags:0(阻塞接收)
[3]返回值:成功—>收到字节的数量;失败—>0:对端关闭连接了;-1:接收错误。
[4]注意:
返回值为0表示对端关闭连接了,如果此时的对端指的是客户端,则服务端需要将对应的新套接字描述符关闭。
f.发送数据
[1]原型:ssize_t send(int sockfd, const void* buf,
                     size_t len, int flags)
[2]参数:
sockfd:套接字描述符
buf:发送buf指向的空间的内容
len:数据长度
flags:0(阻塞发送)
[3]返回值:成功—>发送的字节数量;失败—>-1
[4]注意:服务端在发送数据的时候,第一个参数sockfd传递的是新创建的套接字描述符,并不是侦听套接字的套接字描述符

(3)如何同时让多个客户端都享受服务:

让服务端的一个进程(线程)只负责与客户端建立连接,剩下的一批进程(线程)可以各自与一个客户端进行沟通。

(4)TCP+多进程:

a.主要更改是在服务端,通过创建子进程的方式来实现职责的分离,也就是父进程只负责与客户端建立连接,而子进程负责与客户端进行收发消息。
b.注意细节:
[1]子进程是拷贝父进程的PCB,因此需要父进程先与客户端建立连接,也即在父进程的PCB中的fd_array中,有了该套接字的文件描述符之后再创建子进程。
[2]子进程创建成功过,由于它只需要和客户端进行收发消息,因此只需要accept返回的新套接字描述符即可,所以需要将拷贝自父进程的侦听套接字关闭。
[3]客户端如果将连接关闭,则子进程需要将对应的套接字即文件描述符关闭,然后该进程需要退出。但是退出时,一定要通知父进程来回收子进程的退出状态信息,否则子进程就会编程僵尸进程。但不能采用wait或waitpid来回收。因为wait具有阻塞属性,而waitpid需要搭配循环来使用,均不符合我们的预期。我们可以通过信号量的方式来处理,即改写SIGCHLD信号。

五.五种IO模型以及select编程:

1.五种IO模型

1.1阻塞IO

在内核将数据准备好之前,系统调用会一直等待。所有的套接字,默认都是阻塞方式

1.2 非阻塞IO

如果内核还未将数据准备好,系统调用仍会直接返回,并且返回EWOULDBLOCK错误码。
非阻塞IO的返回,需要判断系统调用函数的返回值,判断当前函数是否将IO功能完成。
(1)没完成:一般会搭配循环继续调用(IO功能没有完成),但这样对于CPU的资源也是巨大的浪费。
(2)完成了:内核将数据准备好了,拷贝回来了(IO功能完成)

1.3 信号驱动I0

内核将数据准备好时,使用SIGIO信号通知应用程序进行I0操作。例如对于僵尸进程(僵尸进程就是子进程先于父进程退出,子进程的退出信息没有人回收)的处理可以配合信号当子进程退出时,自定义信号的处理方式。调用wait函数来回收子进程。

1.4 异步IO

由内核在数据拷贝完成时,通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据)。好比说钓鱼,信号IO就是相当于鱼竿上面帮了一个铃铛,铃铛响了的时候通知你来将鱼竿收起然后钓鱼,而异步IO则是帮你钓鱼,就是帮你找了一个人当他调好了鱼之后来通知你取鱼。

1.5 IO多路转接

虽然从流程图上看起来和阻塞IO类似。实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态。多路多路转接IO可以帮我们同时监控多个事件。

总结:任何IO过程中, 都包含两个步骤. 第一是等待, 第二是拷贝. 而且在实际的应用场景中, 
等待消耗的时间往往都远远高于拷贝的时间.让IO更高效, 最核心的办法就是让等待的时间尽量少.

2.多路转接IO(select)

2.1 接口

int select(int nfds, fd_set *readfds,  fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
nfds :取值为最大文件描述符的数值+1,作用是:控制select的轮询监控范围。也收到事件集合fd_set的限制。这里fd_set事件集合就是一个数组,这个数组的大小为16个long int而linux下long int大小为8个字节,这里数组当作位图使用那么就会有16*8*8个bit位,那么这里select最大的监控文件描述符的个数为1024。而且这个位图是从0开始到1023。

这里有一个问题:那么一个程序能不能创建1024个文件描述符呢?

这要看系统的限制,输入ulimit -a,可以查看到系统最大的open files为100001个文件描述符。

对于nfds的总结:
1.事件集合在内核当中是以数组定义的,但是使用方式是位图
2.位图的大小取决于内核宏二_FD_SETSIZE。
3.目前的位图的大小为1024比特位,所以select只能监控0~1023号文件描述符
readfds :读事件集合!
writefds : 写事件集合√execptfds :异常事件集合、
timeout:传递NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件; 
传递0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
传递特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回,秒/微秒NULL。
返回值:成功:返回就绪文件描述符的个数;失败:返回-1.

2.2 select使用事件集合方式

2.2.1 接口:
void FD_CLR(int fd,fd_set *set) ;
作用:将fd从事件集合set当中去除掉,本质就是将fd对应的比特位置为0
int FD_ISSET(int fd,fd_set *set) ;
作用:判断fd文件描述符,是否在set集合当中,本质是判断fd对应的比特位是否为0
返回值:0:表示fd不在set当中;1:表示fd在set当中
void FD_SET(int fd,fd_set *set) ;
作用:设置fd文件描述符到set事件集合当中,本质是将fd对应的比特位设置为1
void FD_ZERO(fd_set *set) ;
作用:清空事件集合.本质是将set当中所有的比特位都置为0
2.3 select使用方式:
1.select共有三个事件集合:读事件集合,写事件集合,异常事件集合。
2.当需要关注某个文件描述符的某个事件,则将来个文件描述符添加到对应的事件集合当中。
例如:关注(0号文件描述符的读事件,则将0号文件描述符添加到读事件集合当中readfds。
3.如果不关注某种事件,则给select传递参数的时候,传递NULL。
2.4 select的返回值:
1.返回值为就绪的文件描述符的个数
2.就绪的文件描述符存储在事件集合当中返回给调用者
注意: iselect会将未就绪的文件描述符从事件集合当中去除掉,因此,再次监控的时候需要重新添加,
这里的意思就是说:
当用select监控多个文件描述符的时候那么此时select就会扫描一遍事件集合的位图,如果这里监控三个文件描述符,而此时就绪的文件描述符只有一个,那么select扫描当前事件集合之后会将那两个未就绪的文件描述符从事件集合中抹去,也就是将相应的bit位,置为0。所以下次还要监控这三个文件描述符时,就要将事件集合当中这三个文件描述符对应的bit位都置为1。
这里我觉得是因为当select监控完事件集合当中还要检测那些事件就绪了,所以这里才要将没有就绪的事件从事件集合中移除。
3.返回值的事件集合的特性一定要注意,因为会去除未就绪的文件描述符


3.多路转接模型-poll:

1. 历史地位:

跨平台不如select,性能不如epoll,比较尴尬的地位。

2. 作用:

IO多路转接能够同时等待多个文件描述符的就绪状态,换句话说,可以帮助我们同时监控多个文件描述符。

3. 接口:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
3.1 参数
fds:事件结构数组,存放监控的文件描述符,关心的事件(例如关心文件的可读事件,可写事件,可读事件。例如调用recv函数的话那么必须要当接收缓冲区中有内容才可以IO(IO的本质其实就是等待与读取),又例如可写事件,必须要等),真实产生的事件。
nfds:描述fds数组当中有多少有效元素。也就是要关心在事件结构数组中的多少个元素。
timeout:超时事件单位s
>0:表示带有超时时间的等待。
==0:非阻塞,需要搭配循环来使用。
<0:阻塞监控
3.2 返回值:
>0∶表示多少文件描述符就绪。
==O:监控超时。
<0∶监控出错。
例如:无效的文件描述符。
3.3 事件结构(也就是在事件结构数组中存放的元素)
struct pollfd {
int fd;   
/* file descriptor */要监控的文件描述符。
short events;   
/* requested events */
关心文件描述符产生的事件: 
POLLIN:可读事件, 
POLLOUT:可写事件如果想要监控多个事件,则使用按位或的方式。
short revents; 
/*returned events*/
文件描述符真实产生的事件。可能产生POLLIN事件也可能产生POLLOUT事件,那么可以判断revents==POLLIN还是==POLLOUT来获取关心的事件。
};

4.poll的优缺点

4.1 优点
1.提出了事件结构的方式,在给poll函数传递参数的时候,不需要分别添加到“事件集合”当中。
select需要将要监控的事件添加到事件集合中,而poll这里只需要自己创建监控事件的数组,然后给其中的成员赋值。
2.事件结构数组的大小可以根据程序员自己进行定义,并没有上限的要求。select最大的可监控事件数目为1024个文件描述符。而poll这里可以自定义事件结构数组。然后对数组元素进行赋值。
3.不用在监控到就绪之后,重新添加文件描述符。而select则需要每次监控之后将事件集合更新
4.2 缺点
1.不支持跨平台
2.内核也是对事件结构数组监控的时候采用轮询遍历的方式。

4.多路转接模型epoll:

1. 历史定位

迄今为止,linux平台性能最好的IO多路转接模型。没有之一。

2. 接口

2.1 创建epoll句柄
int epoll_create(int size);
参数:size:目前没有含义了,但是需要大于0,兼容旧内核
返回值:返回的epoll操作句柄;
2.2 操作epoll
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数:
epfd :epoll操作句柄
op :当前要执行的行为
    EPOLL_CTL_ADD:添加一个文件描述符对应的事件结构到epoll当中
    EPOLL_CTL_MOD:修改一个文件描述符的事件结构
    EPOLL_CTL_DEL:从epoll当中删除一个文件描述符对应的事件结构
fd:待处理(添加,修改,删除)的文件描述。
event:文件描述符对应的事件结构event中events是要关心的事件,这里如果同时多个事件那么也用或的方式连接。
返回值:0->正常;-1->失败。
2.3 获取就绪事件结构
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
参数:
epfd:epoll的操作句柄
events:事件结构数组(集合),从epoll当中获取就绪的事件结构
maxevents:最多一次获取多少个事件结构
timeout:
>0:带有超时事件
=O:非阻塞
<0:阻塞
返回值:就绪的文件描述符个数

3.epoll监控的内部工作原理:

调用epoll_create创建红黑树和双向链表这两个数据结构,然后我们用epollctr将要监控的文件描述符添加到红黑树中,添加到红黑树中,然后epoll就再内核中帮我们一直监控遍历红黑树,有就绪事件发送就添加到双向链表中,然后通过epoll_wait从双向链表当中获取就绪事件。

4. epoll的工作模式:

默认工作模式:LT水平触发
LT︰水平触发(“亲妈”模式:只要达到触发事件的条件,则一定乐此不疲的触法。);友好
换个说法:当读就绪或者写就绪的时候,则一定乐此不疲的通知。
举个例子当我们用epoll监听新连接套接字时,当就新连接套接字就绪的时候也就是接收缓冲区中有消息时,那么这时epoll会一直通知处理。直到接收缓冲区中消息都被读出。
ET∶边缘触发(“后妈”模式:当到达触发事件的条件后,只会触发一次);要求程序员一次将数据全部读回来
换个说法:当读就绪或者写就绪时,只会通知一次。
举个例子当我们用epoll监听新连接套接字时,当就新连接套接字就绪的时候也就是接收缓冲区中有消息时,那么这时epoll只通知一次,也就是当用epoll_wait来获取就绪事件的数组时,只有这一次会将就绪事件的获取到。之后就无法获取。所以这就要求我要一次性将数据全部都读取回来。这里的水平触发和边缘触发是针对某个文件描述符来说的。这里在设置某个事件是水平触发还是边缘触发,只需要将文件描述符的关心事件events或一个EPOLLET就可以了。

六.面试题:

1.TCP拥塞控制机制?

拥塞窗口cwnd是客户端或者服务端维护的一个状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态的在变化。发送方让自己的发送窗口等于拥塞窗口和接收窗口的最小值。

(1)慢启动:

因为TCP链接刚建立之后不确定网络的负荷情况,因此,较好的方式就是先探测一下,即由小到大逐渐增大发送窗口,通常在刚开始发送报文的时候,先把拥塞窗口的大小设置为一个最大报文段MSS的数值,而在每收到一个对新的报文的确认后,就把拥塞窗口增加至多一个MSS的数值。(注意:因为是每一个ACK都会让MSS加1,所以cwnd = cwnd*2)为了防止拥塞窗口的cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量,当cwnd >= ssthresh时,就会进入“拥塞避免算法”

(2)拥塞避免:

避免增长过快导致网络拥塞,让拥塞窗口cwnd缓慢的增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd整体加上1,而不是加倍。
注意:无论是在满开始阶段还是拥塞避免阶段,只要发送方没有接收到确认信息,那么就是出现了网络拥塞,则需要将慢开始门限设置为出现拥塞时的发送方窗口值的一半(但不能小于2)。然后把拥塞窗口的大小重新设置为1,执行慢开始算法。

(3)快重传

发送方连续收到三次重复的确认就应当立即重传对方尚未收到的报文

(4)快恢复

当发送方连续收到三个重复的确认,把慢开始门限设置为此时拥塞窗口值的一半,由于发送方现在认为网络很可能没有发生拥塞,则此时不执行慢开始算法,而是将cwnd的大小设置为减半后的慢开始门限sstream的数值,开始执行拥塞避免算法。

2.TCP三次握手异常情况:

(1)client第一个syn包丢失,没收到server的ack:

client进行持续重传syn包。总尝试时间为75秒。

(2)server收到了client的syn,并发出了syn+ack包,syn+ack包丢失:

client方面,因为没收server的。将执行情况(1);
server方面,超时时间内没有收到client的ack包(或者数据包),会持续发送syn+ack包;

(3)当Client端收到Server的SYN+ACK应答后,其状态变为ESTABLISHED,并发送ACK包给Server;如果此时ACK在网络中丢失:

那么Server端该TCP连接的状态为SYN_RECV,并且依次等待3秒、6秒、12秒后重新发送SYN+ACK包,以便Client重新发送ACK包,以便Client重新发送ACK包。Server重发SYN+ACK包的次数,可以通过设置/proc/sys/net/ipv4/tcp_synack_retries修改,默认值为5。如果超过重发指定次数后,仍然未收到ACK应答:那么一段时间后,Server自动关闭这个连接。如果此时client向server发送数据包,server能正常接收数据。并认为连接已正常。应用层编写socket代码时,三次握手发生在client的connect,所以为了避免长时间(75秒)无响应连接,应设置为非阻塞socket,
同时用select检测设置合适的超时时间。

3.四次挥手异常情况:

(1)client发的FIN包丢了

对于client,因为没收对应的ACK包,应当一直重传(像普通包一样),直至到达上限次数,直接关闭连接;
对于server,它应该无任何感知;

(2)server回client的ACK包丢了

对于client,将执行(1);对于server将像丢普通的ack一样,再次收到FIN后,再发一个ACK包;

(3)如果client收到ACK后,server直接跑路。

client将永远停留在这个状态(半打开状态,就像client关闭了输出一样)。
linux有tcp_fin_timeout这个参数,设置一个超时时间 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看,
默认60s,可否修改看linux具体版本; windows 注册表有TcpTimedWaitDelay,win10默认值30s;

(4)server发的FIN包丢了

对于server,像丢普通的包一样,重传。
若此时client早已跑路且与其他人建立的连接,client应会不认识这个FIN包,直接回个RST包给server。如若client没跑路,且没收到server的FIN包,如(3)描述;

(5)client回复ACK后

按道理来说,可以跑路了,但防止回复的ACK包丢失(丢失后,server因为没收FIN的ACK,所以会再发一个FIN),将等待2MSL(最大报文存活时间)(RFC793定义了MSL为2分钟,Linux设置成了30s)

(6)为什么要这有TIME_WAIT?为什么不直接给转成CLOSED状态呢?

主要有两个原因:
1)TIME_WAIT确保有足够的时间让对端收到了ACK,如果被动关闭的那方没有收到Ack,就会触发被动端重发Fin,一来一去正好2个MSL,
2)有足够的时间让这个连接不会跟后面的连接混在一起(你要知道,有些自做主张的路由器会缓存IP数据包,如果连接被重用了,那么这些延迟收到的包就有可能会跟新连接混在一起),这期间如若再收到server的FIN,则再回复ACK;

4.为什么要三次握手,而不是二次握手:

为了避免已失效的连接报文段又到达服务器。假设TCP连接是两次握手。客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。

5.三次握手流程:

刚开始客户端处于closed状态,服务端处于Listen状态。
进行三次握手:
第一次握手:客户端给服务端发一个SYN报文,并指明客户端的初始化序列号。此时客户端处于 SYN_SEND 状态。首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。
第二次握手:服务器收到客户端的SYN报文后,会以自己的SYN报文作为应答,并且也是指定了自己的初始化序列号。同时会把客户端的ISN+1作为ACK的值,表示自己已经收到了客户端的SYN,此时服务器处于SYN_REVD的状态。在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y。
第三次握手:客户端收到SYN报文之后,会发送一个 ACK 报文,当然,也是一样把服务器ISN+1作为ACK的值,表示已经收到了服务端的SYN报文,此时客户端处于ESTABLISHED状态。服务器收到ACK报文之后,也处于ESTABLISHED状态,此时,双方已建立起了连接。确认报文ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。发送第一个SYN的一端将执行主动打开,接收这个SYN并发回下一个SYN的另一端执行被动打开。在socket编程中,客户端执行connect()时,将触发三次握手。

6.Server端易受到SYN攻击?

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击,SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并Server不断地发送SYN包,Server则回复SYN+ACK确认包,并等待Client确认回复ACK,而这些大量的IP是不存在的,并不会向服务端发送ack确认包,所以会大量的占领半连接队列资源,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。

7.预防SYN攻击:

开启SYN cookies技术、增加最大半连接和缩短超时时间。SYN cookies应用于linux、FreeBSD等操作系统,当半连接队列满时,SYNcookies并不丢弃SYN请求,而是通过加密技术来标识半连接状态。在TCP实现中,当收到客户端的SYN请求时,服务器需要回复SYN+ACK包给客户端,客户端也要发送确认包给服务器。通常,服务器的初始序列号由服务器按照一定的规律计算得到或采用随机数,但在SYN cookies中,服务器的初始序列号是通过对客户端IP地址、客户端端囗、服务器IP地址和服务器端囗以及其他一些安全数值等要素进行hash运算,加密得到的,称之为cookie。当服务器遭受SYN攻击使得半连接状态队列满时,服务器并不拒绝新的SYN请求,而是回复cookie(回复包的SYN序列号)给客户端,如果收到客户端的ACK包,服务器将客户端的ACK序列号减去1得到cookie比较值,并将上述要素进行一次hash运算,看看是否等于此cookie。如果相等,直接完成三次握手。(注意:此时并不用查看此连接是否属于半连接状态队列)

8.四次挥手流程:

刚开始双方都处于 ESTABLISHED 状态,假如是客户端先发起关闭请求。

四次挥手的过程如下:

第一次挥手:客户端发送一个FIN报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。

第二次挥手:服务端收到FIN后,会发送ACK报文,且把客户端的序列号值+1作为ACK报文的序列号值,表明已经收到客户端的报文,此时服务端处于CLOSE_WAIT状态。即服务端收到连接释放报文段后发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。

第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。

第四次挥手:客户端收到FIN之后,一样发送一个ACK报文作为应答,且把服务端的序列号值+1作为自己ACK报文的序列号值,此时客户端处于TIME_WAIT状态。需要过一阵子以确保服务端收到自己的ACK报文之后才会进入CLOSED状态,服务端收到ACK报文之后,就处于关闭连接了,处于 CLOSED 状态。即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。收到一个FIN只意味着在这一方向上没有数据流动。客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态。在socket编程中,任何一方执行close()操作即可产生挥手操作。

9.挥手为什么需要四次?

因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。

10.2MSL等待状态

TIME_WAIT状态也成为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL,它是任何报文段被丢弃前在网络内的最长时间。这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段。

对一个具体实现所给定的MSL值,处理原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。

11.四次挥手释放连接时,等待2MSL的意义

MSL可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。

两个理由:
(1)保证客户端发送的最后一个ACK报文段能够到达服务端。这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,服务端超时重传FIN+ACK报文段,而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重新启动2MSL计时器,最后客户端和服务端都进入到CLOSED状态,若客户端在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后立即释放连接,则无法收到服务端重传的FIN+ACK报文段,所以不会再发送一次确认报文段,则服务端无法正常进入到CLOSED状态。
(2)防止“已失效的连接请求报文段”出现在本连接中。客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。

12.为什么TIME_WAIT状态需要经过2MSL才能返回到CLOSE状态

理论上,四个报文都发送完毕,就可以直接进入CLOSE状态了,但可能网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

13.三次握手过程中可以携带数据吗

第三次握手时,是可以携带数据的。但第一次、第二次握手不可以携带数据。假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发SYN报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于ESTABLISHED状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。

14.什么是半连接队列

服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

这里在补充一点关于SYN-ACK 重传次数的问题:
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…

15.初始化序列(ISN)是固定的吗

当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号。ISN随时间而变化,因此每个连接都将具有不同的ISN。ISN可以看作是一个32比特的计数器,每4ms加1。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做错误的解释。三次握手的其中一个重要功能是客户端和服务端交换ISN,以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果ISN是固定的,攻击者很容易猜出后续的确认号,因此ISN是动态生成的

16.SYN攻击是什么

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN攻击是一种典型的DoS/DDoS攻击。检测SYN攻击非常的方便,当在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在Linux/Unix上可以使用系统自带的netstats命令来检测SYN攻击。netstat -n -p TCP | grep SYN_RECV

常见的防御 SYN 攻击的方法有如下几种:
(1)缩短超时(SYN Timeout)时间
(2)增加最大半连接数
(3)过滤网关防护
(4)SYN cookies技术

17.HTTP与HTTPS

1.HTTP:

超文本传输协议,是互联网上应用最为广泛的一种网络协议,所有W3C文件必须遵守这个标准,设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法,但是它的协议传输方式是利用明文进行传输的,存在安全隐患。

2.HTTP协议连接方式:

(1)客户端的浏览器首先要通过网络与服务器建立连接,该连接是通过TCP来完成的,一般TCP连接的端口号是80。建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URI)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和许可内容。
(2)服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。

3.HTTPS:

HTTPS是基于HTTP协议提高了安全性的版本,HTTPS提供了一个系列的加密+认证+完整性保护的操作,从而保证了数据在传输过程中的安全。而其安全基础是SSL 协议。SSL协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。

SSL 协议可分为两层:SSL 记录协议,它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。SSL 握手协议,它建立在 SSL 记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。

4.HTTP与HTTPS的区别:

(1)HTTPS协议需要到CA请证书,一般免费证书较少,因而需要一定费用。
(2)HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL加密传输协议。
(3)HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
(4)HTTP的连接很简单,是无状态的。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。无连接的意思是指通信双方都不长久的维持对方的任何信息。)

5.一段HTTP报文包含三部分:

报文首部+空行+报文主体;报文首部又可划分为请求报文和响应报文。
请求报文包括: 请求行+请求头+空行+请求数据

请求行:

请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。

常用的请求方法:
GET:请求指定的页面信息
HEAD:类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头。
POST:向指定资源提交数据进行处理请求。数据被包含在请求体中。

GET和POST的区别:
GET:
(1)请求参数会显示在地址栏中
(2)参数长度有限制
(3)不太安全
POST:
(1)请求参数不会在地址栏中,会封装在请求体中
(2)参数长度无限制
(3)较为安全

请求头:
Accept:浏览器通过这个头,告诉服务器它所支持的数据类型
Accept-Charset:浏览器通过这个头,告诉服务器它采用的字符集
Accept-Encoding:浏览器通过这个头,告诉服务器,它所支持的压缩格式
Accept-Language:浏览器通过这个头,告诉服务器,它所采用的语言
Host:浏览器通过这个头,告诉服务器,我想访问服务器哪台主机
If-Modified-Since:浏览器通过这个头,告诉服务器,它缓存数据时间是多少。
Referer:浏览器通过这个头,告诉服务器,我是从哪个网页点过来的(防盗链)

Connection:连接方式

响应报文:响应报文包含: 状态行+响应头+空行+请求数据

状态行:
状态码
1xx:信息性状态码,接收的请求正在处理
2xx:成功状态码,请求正常处理完毕
200 OK:表示从客户端发来的请求在服务器端被正常处理了。
204 No Content:该状态码代表服务器接收的请求已经成功处理,但在返回的响应报文中不含实体的主体部分。
206 Partial Content:该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的请求。响应报文中包含由Content-Range指定范围的实体内容。
3xx:重定向状态码,需要进行附加操作以完成请求
301 Moved Permanently:永久性重定向。该状态码表示请求的资源已被分配了新的URI,以后应该使用现在资源所指的URI。
302 Found:临时性重定向。该状态码表示请求的资源已被分配了新的URI,希望用户本次能使用新的URI访问。
303 See Other:该状态码表示由于请求对应的资源存在着另外一个URI,应使用GET方法定向获取请求的资源。
304 Not Modified:该状态码表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但因发生请求未满足条件的情况后直接返回304。
304状态码返回时,不包含任何响应的主体部分。
304虽然被划分在3XX类别中,但是和重定向没有关系。
307 Temporary Redirect:临时重定向。该状态码与302 Found有着相同的含义。
4xx:客户端错误状态码,服务器无法处理请求
400 Bad Request:该状态码表示请求报文中存在语法错误。
401 Unauthorized:该状态码表示发送的请求需要有通过HTTP认证的认证信息。另外若之前已进行过1次请求,则表示用户认证失败。
403 Forbidden:该状态码表明对请求资源的访问被服务器拒绝了。未获得文件系统的访问授权,访问权限出现某些问题等情况都可能发生403。
404 Not Found:该状态码表明服务器上无法找到请求的资源。除此之外,也可以在服务器端拒绝请求且不想说明理由时使用。
5xx:服务端错误状态码,服务器处理请求出错
500 Internal Server Error:该状态码表明服务器端在执行请求时发生了错误,也有可能是Web应用存在的Bug或某些临时的故障。
503 Service Unavailable:该状态码表明服务器暂时处于超负荷或正在进行停机维护,现在无法处理请求。

HTTP响应头:
Location:这个头通常配合302状态码使用,它用于告诉浏览器你去找谁。
Server:告诉浏览器,服务器的类型
Content-Encoding: 服务器通过这个头,告诉浏览器,回送的数据采用的压缩格式。
Content-Length: 返回数据的长度
Content-Language: 返回数据的语言
Content-Type:这个头用于告诉浏览器,回送数据的类型
Last-Modified:这个头用于告诉浏览器,数据的最后修改时间
Refresh: :这个头用于控制浏览器定时刷新
Content-Disposition: 用于通知浏览器,以下载方式打开回送的数据
Transfer-Encoding: 用于通知浏览器,数据是以分块形式回送的
ETag: 缓存相头的头
Expires: 用于说明网页的失效时间,如果该值为一个<0的值,则服务器是通知浏览器不要缓存
Cache-Control: no-cache 通知浏览器不要缓存

18.如何使用UDP实现TCP:

安全机制:确认应答机制、超时重传机制、连接管理机制、流量控制机制、拥塞控制机制
效率:滑动窗口、延迟应答、捎带机制

1.确认应答机制

主机A发送数据给主机B,每个数据都带了数据序号,主机B返回ACK应答。每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。
作用:
1.保证安全:保证‘我’发送的消息,对方必须确认并恢复
2.保证多条数据确认信息的安全(告诉发送者,这次回应是对哪些数据,下次数据发送应该从什么时候开始)

2.超时重传机制

超时重传机制触发:主机A发送数据给主机B,如果主机A在一个特定的时间间隔内没有收到来自主机B的确认应答,就会进行数据重发。没有收到确认应答的情况:
1. 主机A的数据报在发送的过程中丢了
2. 主机B的ACK应答丢了

超时时间的确定:TCP会根据当时的网络状态,动态的计算数据发送的速度,得到单次数据报发送的最大生存时间(MSL),超时时间即为(2MSL)
了解:如果一直接收不到ACK,超时时间会如何处理?Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍。如果重发一次之后, 仍然得不到应答, 等待 2500ms 后再进行重传。如果仍然得不到应答, 等待 4500ms 进行重传。依次类推, 以指数形式递增(2的指数倍)。累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接。

3.连接管理机制

三次握手与四次挥手

滑动窗口

如果没有滑动窗口,网路数据传输就是串行的方式(发送一次之后,等待应答,这个时间内,主机A无事可做,主机B也一样),效率比较差。
1. 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值.
2. 发送前四个段的时候, 不需要等待任何ACK, 直接发送;
3. 收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推;
4. 操作系统内核为了维护这个滑动窗口, 需要开辟发送缓冲区 来记录当前还有哪些数据没有应答;只有确认应答过的数据, 才能从缓冲区删掉;
5. 窗口越大, 则网络的吞吐率就越高;

4.流量控制机制

接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送, 就会造成丢包, 继而引起丢包重传等等一系列连锁反应。

1. 接收端将自己可接收的缓冲区大小放入TCP首部中的 “窗口大小” 字段, 通过ACK端通知发送端;

2. 窗口大小字段越大, 说明网络的吞吐量越高;

3. 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;

4. 发送端接受到这个窗口之后, 就会减慢自己的发送速度;

5. 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨柟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值