OSI,TCP/IP,TCP,UDP,Socket基础知识整理与回顾
前言:相信很多基础比较薄弱的朋友实际开发时不只是会听到Http协议,还会经常会听到TCP协议,Socket,TCP/IP等等概念名词,但又经常会混淆这些概率,或者云里雾里
一:OSI模型
首先从OSI模型开始
全称开放式系统互联通信参考模型,这个模型是由国际标准化组织提出的,OSI模型将计算机网络体系结构(architecture)划分为以下七层
- 物理层(Physical,PH)传递信息需要利用一些物理传输媒体,如双绞线、同轴电缆、光纤等。物理层的任务就是为上层提供一个物理的连接,以及该物理连接表现出来的机械、电气、功能和过程特性,实现透明的比特流传输。在这一层,数据还没有组织,仅作为原始的比特流提交给上层——数据链路层。
- 数据链路层(Data-link,D)数据链路层负责在2个相邻的结点之间的链路上实现无差错的数据帧传输。每一帧包括一定的数据和必要的控制信息,在接收方接收到数据出错时要通知发送方重发,直到这一帧无差错地到达接收结点,数据链路层就是把一条有可能出错的实际链路变成让网络层看起来像不会出错的数据链路。实现的主要功能有:帧的同步、差错控制、流量控制、寻址、帧内定界、透明比特组合传输等。
- 网络层(Network,N)网络中通信的2个计算机之间可能要经过许多结点和链路,还可能经过几个通信子网。网络层数据传输的单位是分组(Packet)。网络层的主要任务是为要传输的分组选择一条合适的路径,使发送分组能够正确无误地按照给定的目的地址找到目的主机,交付给目的主机的传输层。
- 传输层(Transport,T)传输层的主要任务是通过通信子网的特性,最佳地利用网络资源,并以可靠与经济的方式为2个端系统的会话层之间建立一条连接通道,以透明地传输报文。传输层向上一层提供一个可靠的端到端的服务,使会话层不知道传输层以下的数据通信的细节。传输层只存在端系统中,传输层以上各层就不再考虑信息传输的问题了。
- 会话层(Session,S)在会话层以及以上各层中,数据的传输都以报文为单位,会话层不参与具体的传输,它提供包括访问验证和会话管理在内的建立以及维护应用之间的通信机制。如服务器验证用户登录便是由会话层完成的。
- 表示层(Presentation,P)这一层主要解决用户信息的语法表示问题。它将要交换的数据从适合某一用户的抽象语法,转换为适合OSI内部表示使用的传送语法。即提供格式化的表示和转换数据服务。数据的压缩和解压缩、加密和解密等工作都由表示层负责。
- 应用层(Application,A)这是OSI参考模型的最高层。应用层确定进程之间通信的性质以满足用户的需求,以及提供网络与用户软件之间的接口服务。
这个OSI模型的作用当然是为了规范,它试图使各种计算机在世界范围内互连为网络的标准框架,假如每个组织,或者每个国家,不同的设备都拥有自己的一套网络模型规范,那将会使不同网络之间数据的通讯变得更加复杂。
二:TCP/IP协议
TCP/IP协议不是单纯的某一个协议,也不是单指TCP协议,而是一个协议族,因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议,它一共分为如下四层,TCP/IP协议参照并简化了OSI模型
- 应用层——主要协议有DNS,URI,HTML,HTTP,SSL,SMTP,POP,IMAPTELNET,SSH,FTP,SNMP,是用来接收来自传输层的数据或者按不同应用要求与方式将数据传输至传输层;(应用程序相关)
- 传输层——主要协议有TCP,UDP,UDP-Lite,SCTP,DCCP,是使用者使用平台和计算机信息网内部数据结合的通道,可以实现数据传输与数据共享;(操作系统内核负责)
- 网络ip层——主要协议有ICMP、IP、IGMP,主要负责网络中数据包的传送等;(操作系统内核负责)
- 网络接口层——也叫网络访问层或数据链路层,网卡层(以太网协议),主要协议有ARP、RARP,主要功能是提供链路管理错误检测、对不同通信媒介有关信息细节问题进行有效处理等。(设备驱动程序及网络接口负责)
三:TCP/IP协议与OSI模型的关系
可以理解为TCP/IP协议参照并简化了OSI模型,TCP/IP 与 OSI 在分层模块上稍有区别。OSI 参考模型注重“通信协议必要的功能是什么”,而 TCP/IP 则更强调“在计算机上实现协议应该开发哪种程序”
如图为TCP/IP协议与OSI模型的关系
如图为实际数据在网络中从传输流程
四:TCP与UDP协议
- TCP(传输控制协议)特点:
-
TCP是面向连接的运输层协议。应用程序在使用TCP协议之前,必须先建立TCP连接,在传送数据完毕后,必须释放已经建立的TCP连接,在传送数据完毕后,必须释放已经连接的TCP连接。
-
每一条TCP连接只能有两个端点,即点对点的。
-
TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。
-
TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。
-
面向字节流。TCP中的“流”指的是流入到进程或从进程流出的字节序列。面向字节流的含义是:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但是TCP把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。
TCP运用场景:传输稳定可靠,常用于邮件,重要文件,文本等对数据准确高要求高,即时性要求不高的数据传输
- UDP(用户数据报协议)特点:
-
UDP是无连接的,即发送数据之前不需要建立连接(当然,发送数据结束时也没有连接可以释放),因此减少了开销和发送数据之前的时延。
-
UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态表(这里面有很多参数)。
-
UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这就是说,应用层交给UDP多长 的报文,UDP就照样发送,即一次发送一个报文。
-
UDP没有拥塞控制,因此网络出现的拥塞不会使源主机的发送速率降低。但是不使用拥塞控制功能的UDP有可能会引起网络产生严重的拥塞问题。
-
UDP支持一对一、一对多、多对一和多对多的交互通信。
-
UDP的首部开销小,只有8个字节,比TCP的20个字节的首部还要短。
UDP运用场景:传输偶尔可能不稳定,但是传输性能最优,即时通讯,速度要求高,并且此处完全不可以使用重发机制,常用于QQ聊天、在线视频直播、网络语音电话,游戏,广播通信等
五:简单理解Socket
我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。
能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,什么是Socket?Socket并不是一个新的协议,socket 是在TCP/IP协议的传输层之上(传输层和应用层之间)进行了封装,这样我们需要使用TCP,UPD等协议的时候会更加的方便,所以不管是C/C++ 还是java C# php ,我们都可以看到,有封装好的Socket类库可以使用,所以对于一般开发人员开说,只要理解Socket就是一个封装了TCP,UPD等协议类库就够了
我们常用到的socket通讯流程如下
socket编程API简单介绍
/*根据指定的地址族、数据类型和协议来分配一个socket的描述字及其所用的资源。
domain : 协议族,常用的有AF_INET、AF_INET6、AF_LOCAL、AF_ROUTE其中AF_INET代表使用ipv4地址
type : socket类型,常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等
protocol : 协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等*/
int socket(int domain, int type, int protocol);
/*把一个地址族中的特定地址赋给socket
sockfd:socket描述字,也就是socket引用
addr:要绑定给sockfd的协议地址
addrlen:地址的长度
通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
*/
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*监听socket
sockfd:要监听的socket描述字
backlog:相应socket可以排队的最大连接个数 */
int listen(int sockfd, int backlog);
/*连接某个socket
sockfd:客户端的socket描述字
addr:服务器的socket地址
addrlen:socket地址的长度 */
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*TCP服务器监听到客户端请求之后,调用accept()函数取接收请求
sockfd:服务器的socket描述字
addr:客户端的socket地址
addrlen:socket地址的长度*/
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*读取socket内容
fd:socket描述字
buf:缓冲区
count:缓冲区长度*/
ssize_t read(int fd, void *buf, size_t count);
/*向socket写入内容,其实就是发送内容
fd:socket描述字
buf:缓冲区
count:缓冲区长度
*/
ssize_t write(int fd, const void *buf, size_t count);
/*socket标记为以关闭 ,使相应socket描述字的引用计数-1,当引用计数为0的时候,触发TCP客户端向服务器发送终止连接请求。*/
int close(int fd);
六:简单理解TCP三次握手四次挥手
- 三次握手
- Client:在吗,李四,我是张三,听得到吗? (发送端首先发送一个带有SYN(synchronize)标志地数据包给接收方。)
- Server:在的,张三是吧,你能听到我的吗?(接收方接收后,回传一个带有SYN/ACK标志的数据包传递确认信息,表示我收到了。)
- Client:好的,听到,我们的通信可以开始了。(最后,发送方再回传一个带有ACK标志的数据包,代表我知道了,表示’握手‘结束。)
【问题1】三次握手的作用:同步连接双方的序列号和确认号并交换 TCP 窗口大小信息。
- 四次挥手
- Client:我所有东西都说完了(Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。)
- Server:我已经全部听到了,但是等等我,我还没说完(Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。)
- Server:好了,我已经说完了(Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。)
- Client:好的,那我们的通信结束(Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手)
【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
【问题3】为什么不能用两次握手进行连接?
答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。