OSI
计算机通信开放系统互连(open system interconnection)模型,是一个七层模型
- 应用层:应用层的任务是
通过应用进程间的交互来完成特定网络应用
。应用层的协议定义了应用进程间通信和交互的基本规则
。(DNS析协议、HTTP协议) - 表示层:用于表示
信息的语法语义和他们之间的关联
,如加密解密,压缩解压。 会话层:不同机器上的用户之间建立及管理会话
。 - 传输层:
负责向两台主机之间的进程通信提供通用的数据传输服务
。(TCP协议、UDP协议) - 网络层:
负责为分组交换网上的不同主机提供通信服务
。发送数据时,网络层把运输层产生的报文或数据封装成分组或者包进行传送(IP数据报)(IP协议) - 数据链路层:
进行两个主机之间的数据传输
。 - 物理层:定义了
主机和物理设备的联系,以及比特流的传输
。
在网际网协议族中,OSI模型的底下两层是随系统提供的设备驱动程序和网络硬件。网络层由IPv4和IPv6两个协议来处理,可以选择TCP或UDP做为传输层协议,上面三层被统称为应用层。
【为什么套接字提供的是从OSI模型的顶上三层进入传输层的接口?】
- 顶上三层处理具体网络应用的所有细节,但不关注通信细节,底下四层整好相反,能处理所有的通信细节,发送数据、确认数据等。
- 顶上三层通常构成所谓的用户进程,底下四层通常作为操作系统内核的一部分。
IP层提供无连接不可靠
的数据报递送服务,尽自己最大努力把IP数据报传送到指定的目的地,但不能保证一定到达,也不能保证按序到达和每个数据报值到达一次。而它的可靠性只能由上层提供支持。如果是TCP则由TCP本身完成,如果是UDP则由应用程序完成,因为UDP本身是不可靠的。
【IPv6和Ipv4的区别】
-
IPv6报头的结构比IPv4简单
, IPv6报头中去掉了了IPv4报头中许多不常用的域,放入了可选项和报头扩展,其可选项有更严格的定义。 -
IPv6的地址空间更大
。IPv4中规定IP地址长度为32,即有232-1个地址。 而IPv6中IP地址的长度为128,即有2128-1个地址。 -
IPv6的路由表更小
。提高了路由器转发数据包的速度 -
IPv6的组播支持以及对流的支持增强
。这使得网络上的多媒体应用有了长足发展的机会,为服务质量控制提供了良好的网络平台。 -
IPv6具有更高的安全性
。在使用IPv6网络中,用户可以对网络层的数据进行加密并对IP报文进行校验,这极大地增强了网络安全。
TCP
上图的每个协议框中的协议功能;
- IPv4:网际协议版本4,使用32位地址,负责给TCP、UDP、ICMP等协议提供
分组传输服务
。 - IPv6:使用更大的12位地址来应对因特网的爆发性增长,同样给TCP、UDP、ICMP等协议提供
分组传输服务
。 - TCP:传输控制协议,面向连接的,提供可靠的传输协议,为用户提供可靠的全双工通信。有超时重传、关心确认等机制,但是是一种流套接字,无法维护消息边界
- UDP:用户数据报协议,是一个无连接、不可靠的协议,不能保证数据报最终传送到目的地。
- STCP:提供可靠的全双工关联的面向连接的协议,提供消息服务,维护消息边界。
- ICMP:网际控制消息协议,处理在
路由器和主机之间流通的错误和控制消息
,返回差错报文等。 - ARP:将IP地址解析成MAC地址
- RARP:将MAC地址解析成ARP地址
- BPF、DLPI:BSD分组过滤器,提供对于数据链路层的访问能力。
-
UDP(用户数据报协议):提供简单的、无连接的、不可靠的数据传输服务。
不可靠:不保证UDP数据报会达到最终目的地,也不保证其数据报顺序不变或者只到达一次,如果数据报到达后检测有误,则会被抛弃。
无连接:UDP客户与服务器之间不必存在任何长期的关系。 -
TCP:提供可靠的,面向连接的数据传输服务。
面向连接:TCP客户先与服务器建立连接,再进行数据传输,传输完毕终止连接
可靠的:具有超时重传机制
,数次重传失败才会放弃,同时为每个TCP分节编号
,接收端收到后进行重排序再传递,把重复的数据丢弃。同时提供流量控制机制
,TCP收发双方总会告知对方自己一次能够接受多少字节的数据,确保发送方发送的数据不会使接收缓冲区溢出。全双工的
,在给定的连接上应用可以在任何时刻在进出两个方向上即发送数据又接受数据。 -
TCP连接的建立和终止
- 三次握手:通常用到 socket、bind、listen、connect四个函数,客户端通过调用connect发起连接。服务端调用socket、bind、listen 来准备好接受外来连接。
- 每个SYN中可以含有多个TCP选项:
- MSS(maximum segment size)最大分节大小,表示本连接每个TCP分节能接受的最大数据量,发送端使用接收端的MSS作为发送的最大大小。
- 窗口规模选项。TCP连接任何一端都能告知对端的最大窗口大小是 65535
- 时间戳。可以防止失而复现的分组可能造成的数据损坏。
- 四次挥手:
- 三次握手:通常用到 socket、bind、listen、connect四个函数,客户端通过调用connect发起连接。服务端调用socket、bind、listen 来准备好接受外来连接。
-
TIME_WAIT
2MSL:端点停留在 这个状态的持续时间是【最长分节生命期】的2倍- 为了保证客户端发送的最后一个ACK报文段能够到达服务器端。该ACK报文段很有可能丢失,因而使处于在LIST—ACK状态的服务器端收不到对已发送的FIN+ACK报文段的确认,服务器端可能会重传这个FIN+ACK报文段,而客户端就在这2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重新启动2MSL计时器,最后客户端和服务器端都进入CLOSED状态。如果客户端在TIME—WAIT状态不等待一段时间就直接释放连接,到CLOSED状态,那么就无法收到服务器端重传的FIN+ACK报文段,也就不会再发送一次确认ACK报文段,服务器端就无法正常进入CLOSED状态。
- 防止已失效的请求连接出现在本连接中。在连接处于2MSL等待时,任何迟到的报文段将被丢弃,因为处于2MSL等待就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。这样就可以使下一个新的连接中不会出现这种旧的连接之前延迟的报文段。
-
端口号
1. 端口号分为众所周知的端口和临时端口。其中0~1023为众所周知的端口,相同的端口号就分配给同一给定的服务。1024 - 49151为已登记的端口,49152 - 65525是临时端口号
6.**缓冲区的大小和限制 **
-
影响IP数据报大小的限制。
- IPv4数据报的最大大小是65535,包括IPv4首部
- IPv4数据报的最大大小是65575,包括IPv6首部40个字节
- 许多网络有一个硬件规定的MTU
- 两个主机之间的最小MTU称为路径MTU,但两个主机之间的路径从两个方向看可以不一致
- 如果数据报超过了响应链路的 MTU,P协议就执行分片
- IPv4首部的 DF位 如果被设置了,就不允许对数据包进行分片,如果过大就返回ICMP报文
- IPv4和IPv6都定义了 最小重组缓冲区大小 。这是IPv4和IPv6任何实现都必须保证支持的最小数据报大小
- TCP有MSS最大分节大小,用来对端TCP通告在每个分节中能发送的最大TCP数据量。
-
TCP的缓冲区
TCP双方都存在一个缓冲区。当应用进程调用write
时,内核从该进程的缓冲区中复制所有数据到所写套接字的发送缓冲区(TCP发送方的发送缓冲区),如果发送缓冲区容不下所有数据,应用进程就会睡眠,直到发送缓冲区容的下再写入。从TCP套接字的write调用成功返回,表示我们可以重新使用应用进程缓冲区,而不是对端TCP成功接受到数据。
当对端的TCP接受到数据后,必须进行确认,向对方返回一个ACK,随着ACK到达,本端TCP才能从套接字发送缓冲区丢弃已确认的数据。TCP必须保留一个发送数据的副本,知道它被对端确认。
当数据超过MSS时就进行分片。当把数据传递给IP时,为每个数据块添加一个TCP首部,IP再给每个TCP数据块加上一个IP首部构成IP数据报。MSS选项的目的之一就是试图避免分片。 -
UDP的缓冲区:UDP也有发送缓冲区,但只是可以写到套接字的UDP数据报的大小上限,并不会保存一个副本,所以并不需要一个真正的缓冲区。
套接字地址结构
sock_开头的函数以协议无关方式使用套接字地址结构
- IPv4套接字地址结构
sin_len是增加对OSI协议的支持而添加的,在POSIX规范只需要这个结构中的:sin_family,sin_port,sin_addr三个字段。其中in_addr_t必须是32位的无符号整数类型,In_port_t必须是至少16位的无符号整数类型,sa_family_t可以是任何无符号整数类型。 - 通用套接字地址结构
- IPv6套接字地址结构
IPv4和IPv6的长度时固定的,而Unix域结构和数据链路结构是可变长度的。
套接字函数
-
socket函数:指定期望的通信协议类型
int socket ( int family, int type , int protocol )
family参数指协议族(协议域),type参数指套接字类型,protocol参数指某个协议类型常量。函数在成功时返回一个小的非负整数值——-套接字描述符
family常值如下:
type常值如下:
protocol常值: -
connect函数:TCP客户端用Connect建立与服务器的连接
int connect( int sockfd , const struct sockaddr *servaddr , socklen_t addrlen)
sockfd 是套接字描述符,是socket函数运行返回结果;servaddr 是一个指向套接字地址结构的指针;addrlen是该结构的大小。其中套接字地址结构必须含有 服务器IP地址和端口号。
TCP套接字调用Connect函数会触发三次握手过程,仅在连接建立成功或者出错时才返回。出错的情况包括以下几种;- TCP 客户没有收到 SYN 分节的响应,返回 ETIMEDOUT错误。
- 硬错误。若对客户的SYN的响应是RST (表示复位),则表明该服务器主机在我们指定的端口上没有进程等待连接。
- 软错误。若客户发出的SYN在中间的某个路由器上引发了一个“destination unreachable"(目的地不可达)ICMP错误。
【补充】:产生RST的三个条件:1. 目的地为某段口的SYN到达,然而该端口上没有正在监听的服务器(如前所述); 2. TCP想取消一个已有连接;3. TCP接收到一个根本不存在的连接上的分节。
在TCP连接中,调用connect函数会使当前状态由CLOSED状态转移到SYN_SENT状态,再成功就变成 ESTABLISHED状态。如果失败就关闭这个套接字,同时重新调用socket函数。
-
bind函数:绑定,把一个本地协议地址赋予一个套接字
int bind ( int sockfd, const struct sockaddr *myaddr , socklen_t addrlen)
第二个参数是一个指向特定于协议的地址结构的指针,第三个参数是该地址结构的长度。对于TCP,调用bind函数可以指定一个端口号,或指定一个IP地址,也可以两者都指定,还可以都不指定。- 如果一个TCP客户或服务器未曾调用bind捆绑一个端口,当调用connect或listen时,内核就要为相应的套接字选择一个临时端口。
- 进程可以把特定的IP地址捆绑到套接字上,不过必须是其所在主机的网络接口之一。对于TCP客户,这就为该套接字发送的IP数据报指派了源IP地址。对于TCP服务器,这就限定套接字只接搜哪些目的地为这个的客户连接。如果TCP客户端没有把IP地址绑定到套接字,那么内核把客户发送SYN的目的IP作为服务器的源IP地址。
- 如果指定端口号为0,那么内核就在bi nd被调用时选择一个临时端口。然而如果指定IP地址为通配地址,那么内核将等到套接字已连接(TCP)或已在套接字上发出数据报(UDP)时才选择一个本地IP地址。
-
listen函数:仅由TCP服务器调用
int listen(int sockfd,int backlog)
listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受该套接字的连接请求。backlog参数规定了内核应该为相应套接字排队的最大连接个数。这个值可以指定的比较大,因为随着客户端SYN分节的到来,未完成连接队列的项数会增长。
内核为监听套接字维护了两个队列:
- 未完成连接队列:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程。这些套接字处于SYN RCVD状态。
- 已完成连接队列:每个已完成TCP三次握手过程的客户对应其中一项。这些套接字处于ESTABLISHED状态。
当三次握手完成后,未完成连接队列中首项就转移到已完成连接队列的队尾。 -
accept函数:由TCP服务器调用,用于从已完成连接队列对头返回下一个已完成连接。
int accept (int sockfd, struct sockaddr *cliaddr, socklen _t *addrlen) ;
如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户TCP连接。它的返回值为已连接套接字
。 -
fork函数:派生出一个新进程。调用fork函数会返回2次,在调用进程中返回一次,返回新派生进程的进程ID号,在子进程返回一次,返回值为0,告知当前进程是子进程还是父进程。
fork的两种用法: 1. 一个进程创建一个自身的副本,这样每个副本都可以在另一个副本执行其他任务的同时处理各自的操作。2. 一个进程想要执行另一个程序。 - exec函数:exec把当前进程映像替换成新的程序文件,而且该新程序通常从main函数开始执行。
-
close函数:关闭套接字,终止TCP连接。
int close (int sockfd) ;
close一个TCP套接字的默认行为是把该套接字标记成已关闭,然后立即返回到调用进程。该套接字描述符不能再由调用进程使用,也就是说它不能再作为read或write的第一个 参数。
描述符引用计数:在并发服务器中,父进程关闭套接字指示导致引用计数值减1,只有当计数值为0时,才真正关闭并发服务器