HTTP
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
(一)http通信请求流程
客户端去发起一次请求,是通过域名来访问我们的目标服务器,域名是一种并不能直接去访问,而是需要基于DNS去解析,通过域名去访问,会先访问DNS解析器,然后DNS解析器会返回我们一个ip和端口,然后我们基于ip和端口就可以访问目标服务器了.
为什么需要DNS,正常不用域名其实也可以访问目标服务器的,为什么要用域名,是因为我们要记录一个纯数字的网站比我们去记录一个纯字符的网站要更难记住.所以会去设计一个域名来做对服务器的访问.
为什么要基于数字ip去访问目标服务器呢?因为数字是计算机最容易处理的,因为计算机底层是0101的二进制.
(二)HTTP协议
我们使用http来访问Web上某个资源,比如html/文本、word、avi电影、其他资源。这些资源就有个媒体类型的一个说法.
http头里面有个Content-Type ,表示这次访问的资源是什么类型
这里是text/html表示HTML网页。请注意,浏览器就是依靠Content-Type来判断响应的内容是网页还是图片,是视频还是音乐。浏览器并不靠URL来判断响应的内容,所以,即使URL是http://example.com/abc.jpg,它也不一定就是图片,真正决定我传输类型的还是要看Content-Type。
URI和URL的区别:
HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息。
URL,全称是UniformResourceLocator, 中文叫统一资源定位符,是互联网上用来标识某一处资源的地址。
URI是统一资源标识符,URI是个纯粹的句法结构,用于指定标识Web资源的字符串的各个不同部分。URL是特殊类型的URI,它包含了定位Web资源的足够信息。URL是统一资源定位符.
其他URI,比如
mailto:cay@horstman.com
则不属于定位符,因为根据该标识符无法定位任何资源。
URI 是统一资源标识符,而 URL 是统一资源定位符。因此,笼统地说,每个 URL 都是 URI,但不一定每个 URI 都是 URL。这是因为 URI 还包括一个子类,即统一资源名称 (URN),它命名资源但不指定如何定位资源。上面的 mailto就是一个URN 的示例。
URL是uniform resource locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
一个完整的URL
包括以下几部分:
http://www.enjoyedu.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name
http是协议部分: ,是访问用什么协议,你是http协议还是ftp还是其它的自定义协议
这代表网页使用的是HTTP协议。在Internet中可以使用多种协议,如HTTP,FTP等等本例中使用的是HTTP协议。在”HTTP”后面的“//”为分隔符
“www.enjoyedu.com”是域名部分:一个URL中,也可以使用IP地址作为域名使用,也可以用字符串作为域名.如果是IP地址的话浏览器就会直接通过IP去访问,如果是域名部分的话,浏览器还需要把域名解析为一个具体的IP部分
8080是端口部分:跟在域名后面的是端口,域名和端口之间使用“:”作为分隔符。端口不是一个URL必须的组成部分,如果没用到端口部分,将采用默认端口(80端口)
端口号后面就是资源访问路径,news是虚拟目录部分,虚拟目录也不是一个URL必须的部分.
“index.asp”表示的是文件名的部分,然后news和index.asp组合起来就是路径的部分.
?号后面就是参数部分了,表示我这次访问的连接我所需要的参数,参数允许有多个,中间用&进行连接
锚部分:从“#”开始到最后,都是锚部分。本例中的锚部分是“name”。锚部分也不是一个URL必须的部分,用的更少一点.
(三)HTTP请求的传输过程
当你在浏览器访问地址的时候,应用层http数据把报文交给传输层 TCP(包上一个TCP首部),然后调用网络层(包上一个IP首部),交给数据链路层,然后数据链路层包上以太网首部,然后在往回传输
(四)一次完整http请求的过程(面试题)
首先当我们在地址栏里面敲下了http某个网址url的时候,浏览器首先是先做DNS域名解析,这是必须要做的.
域名解析首先分为几个部分:
先去本地浏览器缓存里面检索一次,假如我们敲下www.enjoydu.com的时候,我们对于任何的机器来讲,它只是认识ip地址,并不认识所谓的域名,这个域名,浏览器要把它解析成一个具体的ip地址,它首先去本地浏览器的缓存地址里面去查找,如果没有的话浏览器就会去操作系统缓存里面去查找,如果操作系统缓存里面也没有,这个时候浏览器就回去DNS服务器上面去查询,由DNS服务器去告诉它你当前的域名(当前访问的url地址)对应哪个ip地址,拿到ip地址以后我们知道http是建立在tcp的基础之上的,所以说在http开始工作之前,客户端首先要通过网络和服务器建立连接,也就是所谓的通过三次握手和我们的服务器建立连接.
一旦进入了TCP连接以后,客户端就会向你浏览器访问的url路径的服务器上发送相关的请求的命令.
• 客户端向服务器发送请求命令
一旦建立了TCP连接,客户端就会向服务器发送请求命令;
例如:GET/sample/hello.jsp HTTP/1.1
• 客户端发送请求头信息
客户端发送其请求命令之后,还要以头信息的形式向服务器发送一些别的信息,之后客户端发送了一空白行来通知服务器,它已经结束了该头信息的发送;
服务器收到客户端发过来的应答以后就进行一个响应,响应同样也有相关的格式,
会有头信息:
例如: HTTP/1.1 200 OK
还有服务器向客户端发送的数据
数据发送完成以后,服务器再通过四次挥手关闭这个tcp连接.
以上就是一次http请求的全过程了,当然也是http1.0的模式.
同时在http1.1以及1.0带一些相关的参数,叫做所谓的 keep-alive ,就是开启保持连接.
也就是说发起一次http以后,这个keep-alive 服务器呢就不会关闭TCP连接了,那么通过一次请求建立连接以后呢,在浏览器和服务器之间可以进行多次的请求及应答了.这就是所谓的keep-alive(保持http连接的一种方式,但是大家要注意http协议在本身上是一个单向性的和无连接的)
(五)HTTP 协议报文结构
对于http来讲,一个报文,不管是应答还是请求,一定会包括一个所谓的报文首部,包括一个空行和报文主体 三个部分 .
用于 HTTP 协议交互的信息被称为 HTTP 报文。请求端(客户端)的 HTTP 报文叫做请求报文;响应端(服务器端)的叫做响应报文。HTTP 报文本身是由多行(用 CR+LF 作换行符)数据构成的字符串文本。
报文首部主要干什么的?
比如我们请求的Get方法,Get http 1.1写在报文首部里面,我们的应答 返回的200 也写在 报文首部里面,报文首部和报文主体之间就有个所谓的空行进行划分, 同时报文主体不一定有,可以没有,但是报文首部是一定要有的
(六)请求报文结构
请求报文的首部内容由以下数据组成:
请求行 —— 包含用于请求的方法、请求 URI 和 HTTP 版本 三个.
首部字段 —— 包含表示请求的各种条件和属性的各类首部。(通用首部、请求首部、实体首部以及RFC里未定义的首部如 Cookie 等)
(七)响应报文结构
状态行 —— 包含表明响应结果的状态码、原因短语和 HTTP 版本。
首部字段 —— 包含表示请求的各种条件和属性的各类首部。(通用首部、响应首部、实体首部以及RFC里未定义的首部如 Cookie 等)
Https协议
http协议服务端和客户端是明文传输,就会出现个问题,可能被拦截,一旦数据被拦截掉,可以就会被修改或者窃取,这个时候就需要保证传输的安全性.
非加密的http协议的分层是http TCP IP ,而https协议的分层是http ssl/tls TCP IP , 而 ssl/tls这层就是负责去加解密的过程的,https端口默认是443
怎么判断证书有没有被篡改,给这个证书添加个md5,
在浏览器或者操作系统中,内置了受信人的根证书,
服务端首先先配置证书,服务端先生成一个公钥A和私钥A, 然后给公钥A发送给CA机构,然后CA也会生成公钥和私钥, 然后CA机构会使用CA的公钥给服务端的公钥A进行加密,此时目的是为了防止在交换公钥A的时候被别人偷窥窃取到.
然后CA机构会给生成的CA证书给服务端,这个时候服务器会配置443的监听和CA证书和私钥A
然后客户端正式发送一个Https请求,首先会建立三次握手等等,然后服务端会返回CA证书给客户端,客户端拿到CA证书之后会通过浏览器内置的根证书(CA公钥)去验证服务端发过来的CA证书的合法性,防止被篡改,验证以后会得到一个公钥A(CA去解密证书来拿到公钥A),此时客户端会生成随机数,这个随机数会基于公钥A加密,然后把这个被公钥A加密的随机数发送给服务端,当然在传输的过程中会增加一些摘要的算法(这个摘要算法也是第一次服务器传送给客户端的时候发送过去的.)
然后服务端会用私钥A来解密公钥A加密的内容,解密以后也会拿到随机数,这个随机数就是对称加密的密钥.客户端会在每次请求的过程中会去携带一个随机数,最后客户端生成随机数通过验证以后
1.SSL/TLS
SSL 是洋文 “Secure Sockets Layer” 的缩写,中文叫做 “安全套接层”。它是在上世纪 90 年代中期,由网景公司设计的。(顺便插一句,网景公司不光发明了 SSL,还发明了很多 Web 的基础设施——比如“CSS 样式表” 和“JS 脚本”) 为啥要发明 SSL 这个协议捏?因为原先互联网上使用的 HTTP 协议是明文的,存在很多缺点——比如传输内容会被偷窥(嗅探)和篡改。发明 SSL 协议,就是为了解决这些问题。
到了 1999 年,SSL 因为应用广泛,已经成为互联网上的事实标准。IETF 就在那年把 SSL 标准化。标准化之后的名称改为 TLS(是 “Transport Layer Security” 的缩写),中文叫做“传输层安全协议”。
很多相关的文章都把这两者并列称呼(SSL/TLS),因为这两者可以视作同一个东西的不同阶段。
2.HTTPS是什么
说白了就是 “HTTP 协议” 和“SSL/TLS 协议”的组合。你可以把 HTTPS 大致理解为——“HTTP over SSL”或“HTTP over TLS”(反正 SSL 和 TLS 差不多)。
网络协议
(一)计算机网络体系结构
1.OSI七层模型
开放系统互连参考模型 (Open System Interconnect 简称OSI)是国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)联合制定的开放系统互连参考模型,为开放式互连信息系统提供了一种功能结构的框架。这里所说的开放系统,实质上指的是遵循OSI参考模型和相关协议能够实现互连的具有各种应用目的的计算机系统。
OSI目的是为了我们不同的计算机,比如说windows Linux Unix 苹果机 等等,它们之间的网络通讯提供一个共同的基础和标准框架.
OSI采用了分层的结构化技术,共分七层,物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
没有哪一个系统或者哪一种实际使用的过程中真正实现了这七层模型.
OSI七层模型我们并不需要去深入的去了解,没啥用,它是极为高明的技术或本领,但是在现实中用不到,我们真正实际开发中用的是TCP/IP的四层模型.
2.TCP/IP模型
OSI模型比较复杂且学术化,所以我们实际使用的TCP/IP模型,共分4层,链路层、网络层、传输层、应用层。两个模型之间的对应关系如图所示:
1. TCP/IP模型的链路层对应着OSI模型的链路层和物理层
2. TCP/IP模型的网络层对应着OSI模型的网络层
3. TCP/IP模型的传输层对应着OSI模型的传输层
4. TCP/IP模型的应用层涵盖了OSI模型的应用层,表示层,会话层
无论什么模型,每一个抽象层建立在低一层提供的服务上,并且为高一层提供服务,假如:
应用层需要传输层提供的服务上面.而传输层依赖网络层,网络层依赖链路层.同时链路层为我们上层提供相关的服务
比如说链路层,假如说在windows里面你需要网络通讯,首先你需要网卡,这是第一点,然后你还需要网卡的驱动程序,所以你可以理解,网卡还有网卡的驱动程序都在链路层里面
3.TCP/IP协议族
TCP/IP 主要是传输层或者网络层的协议.
Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。协议采用了4层的层级结构。然而在很多情况下,它是利用 IP 进行通信时所必须用到的协议群的统称。也就是说,它其实是个协议家族,由很多个协议组成,并且是在不同的层, 是互联网的基础通信架构。
Http就是应用层的协议,TCP就是传输层协议,IP就是网络层协议,PPP(在使用宽带在进行网络拨号的时候)就是链路层协议
TCP和UDP
TCP是面向连接的、可靠的流协议,如果你两台机器发起通讯的话,你两台机器之间必须建立所谓的连接,建立连接以后才能进行数据的传输,在传输过程中,TCP采用“带重传的肯定确认”技术来实现传输的可靠性。TCP还采用一种称为“滑动窗口”的方式进行流量控制,发送完成后还会关闭连接。所以TCP要比UDP可靠的多。
TCP协议的特点
TCP:传输控制协议
特点:面向有连接,安全,数据是完整,但是效率低
实际应用:迅雷
UDP是面向无连接的通讯协议.UDP(User Datagram Protocol的简称, 中文名是用户数据报协议).
两台机器之间不会建立连接,当A服务器要发送数据的时候,是把数据打包成一个数据包直接发出去,而不管对方是不是在接收,也不管对方是否能接收的了,也不需要接收方确认,属于不可靠的传输,可能会出现丢包现象,实际应用中要求程序员编程验证。
现在又出现了UDP的升级版,它是UDT,UDT是带验证的面向连接的通讯协议,相比UDP多了个验证方面的功能.
UDP协议的特点
UDP:用户数据报协议
特点:面向无连接,不安全,数据是不完整,但是效率高
实际应用:共屏,QQ视频
注意:
我们一些常见的网络应用基本上都是基于TCP和UDP的,这两个协议又会使用网络层的IP协议。但是我们完全可以绕过传输层的TCP和UDP,直接使用IP,比如Linux中LVS,甚至直接访问链路层,比如tcpdump程序就是直接和链路层进行通信的.
平时应用上,比如说Http,它走的就是TCP协议,在线视频走的就是UDP,还有就是DNS用UDP.
我们平时在上网的过程中,在地址栏里面输入http://www.aaa.com,对于我们用户来讲看到的只是域名,对于实际访问的过程中,背后来说,需要给这个域名转换成IP地址.
IP地址是四个数字串儿组成的,比如(192.168.1.143),那么IP地址之间的映射有谁来做?就用DNS来做,DNS在全球有专门的服务器来进行域名和IP地址之间的转换,
浏览器只有在拿到IP地址以后,它才真正的知道我去连接具体服务器的一个地址.这个去DNS服务器用的就是UDP.还有就是在线视频也是用UDP.
为什么用UDP,因为对于在线视频来说,有时候会出现丢帧,画面比较模糊,但是我们还会继续往下看,对我们人眼来讲短暂的模糊可以接受.所以UDP丢包的情况我们是可以接收的,而且UDP没有面向连接的概念,速度相比TCP是比较快的.所以在视频上用的就是UDP.
不是任何网络通讯都用TCP和UDP,比如Ping命令,直接是访问的网络层,比如说Linux的LVS(放在Linux内核负载均衡用的,负载均衡是使用IP来实现的,它跳过了传输层).可以说我们的应用程序甚至可以直接访问链路层.不要认为应用程序只能访问传输层.
Linux直接访问链路层的程序有tcpdump(抓包用的)等等
上图中,其他一些协议的名称解释,了解即可:
ICMP 控制报文协议
IGMP internet组管理协议
ARP 地址解析协议
RARP 反向地址转化协议
4.地址和端口号
我们常听说 MAC 地址和 IP 地址。
什么是MAC地址
MAC地址就是在媒体接入层上使用的地址,也叫物理地址、硬件地址或链路地址,由网络设备制造商生产时写在硬件内部。MAC地址与网络无关,也即无论将带有这个地址的硬件(如网卡、集线器、路由器等)接入到网络的何处,都有相同的MAC地址,它由厂商写在网卡的BIOS里,从理论上讲,除非盗来硬件(网卡),否则是没有办法冒名顶替的。
什么是IP地址
IP 地址后者用来识别 TCP/IP 网络中互连的主机和路由器。也就是网络上的某一台机器的地址,IP地址基于逻辑(可以自己修改),比较灵活,不受硬件限制,也容易记忆(毕竟可以自己修改)。
在传输层也有这种类似于地址的概念,那就是端口号。端口号用来识别同一台计算机中进行通信的不同应用程序。因此,它也被称为程序地址。
一台计算机上同时可以运行多个程序。传输层协议正是利用这些端口号识别本机中正在进行通信的应用程序,并准确地将数据传输。
端口号的确定
端口就是本机程序的地址,
在一台机器上面,会有多个网络应用(连接网络的程序)在机器上面去跑,如果从别的机器发送过来一个数据包过来了,到底归属于哪个程序?这就是一个问题了,机器可以收到了,但是机器怎么知道这个数据包是给哪个程序来使用的呢?
为了区分一台机器里面的不同的网络应用程序(连接网络的程序),所以就有了端口号这个东西,端口号可以理解为程序的地址,在一台计算机里面,端口号是不能有重复的,它是唯一的.
分配端口号
1. 标准既定的端口号:这种方法也叫静态方法。它是指每个应用程序都有其指定的端口号,比如http是80,ftp是21。但并不是说可以随意使用任何一个端口号。例如 HTTP、FTP、TELNET 等广为使用的应用协议中所使用的端口号就是固定的。这些端口号被称为知名端口号,分布在 0~1023 之间;除知名端口号之外,还有一些端口号被正式注册,它们分布在 1024~49151 之间,不过这些端口号可用于任何通信用途。
2. 时序分配法:服务器有必要确定监听端口号,但是接受服务的客户端没必要确定端口号。在这种方法下,客户端应用程序完全可以不用自己设置端口号,而全权交给操作系统进行分配。动态分配的端口号范围在 49152~65535 之间。
我们在进行网络编程的时候,写服务器程序的时候,我们要指定一个端口号(标识我在这个服务器的唯一身份.),我们的程序在那个设计好的端口号进行监听.
我们在写客户端的程序的时候,我们不需要设计端口号,因为我们在和服务器连接完成以后,我们就断开了,这个端口号就没必要定死.这时候我们的操作系统会给客户端程序单独临时分配一个端口号,这就是动态端口号.动态端口号的数字一定会大于49151.(动态分配的端口号范围在 49152~65535 之间)
端口号与协议
• 端口号由其使用的传输层协议决定。因此,不同的传输层协议可以使用相同的端口号。
• 此外,那些知名端口号与传输层协议并无关系。只要端口一致都将分配同一种应用程序进行处理。
TCP/IP
TCP是面向连接的通信协议,通过三次握手建立连接,通讯完成时要拆除连接,由于TCP是面向连接的所以只能用于端到端的通讯。
TCP不是想象中的发送一个信号就过去了,它是通过三次握手来建立连接,连接建立完成以后,通讯完成以后,还要拆除连接.
TCP提供的是一种可靠的数据流服务,采用“带重传的肯定确认”技术来实现传输的可靠性。TCP还采用一种称为“滑动窗口”的方式进行流量控制,所谓窗口实际表示接收能力,用以限制发送方的发送速度。
如果IP数据包中有已经封好的TCP数据包,那么IP将把它们向‘上’传送到TCP层。TCP将包排序并进行错误检查,同时实现虚电路间的连接。TCP数据包中包括序号和确认,所以未按照顺序收到的包可以被排序,而损坏的包可以被重传。
TCP将它的信息送到更高层的应用程序,例如Telnet的服务程序和客户程序。应用程序轮流将信息送回TCP层,TCP层便将它们向下传送到IP层,设备驱动程序和物理介质,最后到接收方。
面向连接的服务(例如Telnet、FTP、rlogin、X Windows和SMTP)需要高度的可靠性,所以它们使用了TCP。DNS在某些情况下使用TCP(发送和接收域名数据库),但使用UDP传送有关单个主机的信息。
3.TCP/IP中的数据包
包是全能性术语;
帧用于表示数据链路层中包的单位;
片是 IP中数据的单位;
段则表示 TCP 数据流中的信息;
消息是指应用协议中数据的单位。
每个分层中,都会对所发送的数据附加一个首部,在这个首部中包含了该层必要的信息,如发送的目标地址以及协议相关信息。通常,为协议提供的信息为包首部,所要发送的内容为数据。在下一层的角度看,从上一层收到的包全部都被认为是本层的数据。
网络中传输的数据包由两部分组成:一部分是协议所要用到的首部,另一部分是上一层传过来的数据。首部的结构由协议的具体规范详细定义。在数据包的首部,明确标明了协议应该如何读取数据。反过来说,看到首部,也就能够了解该协议必要的信息以及所要处理的数据。
① 应用程序处理
首先应用程序会进行编码处理,这些编码相当于 OSI 的表示层功能;
编码转化后,邮件不一定马上被发送出去,这种何时建立通信连接何时发送数据的管理功能,相当于 OSI 的会话层功能。
② TCP 模块的处理TCP 根据应用的指示,负责建立连接、发送数据以及断开连接。TCP 提供将应用层发来的数据顺利发送至对端的可靠传输。为了实现这一功能,需要在应用层数据的前端附加一个 TCP 首部。
③ IP 模块的处理IP 将 TCP 传过来的 TCP 首部和 TCP 数据合起来当做自己的数据,并在 TCP 首部的前端加上自己的 IP 首部。IP 包生成后,参考路由控制表决定接受此 IP 包的路由或主机。
④ 网络接口(以太网驱动)的处理
从 IP 传过来的 IP 包对于以太网来说就是数据。给这些数据附加上以太网首部并进行发送处理,生成的以太网数据包将通过物理层传输给接收端。
⑤ 网络接口(以太网驱动)的处理
主机收到以太网包后,首先从以太网包首部找到 MAC 地址判断是否为发送给自己的包,若不是则丢弃数据。
如果是发送给自己的包,则从以太网包首部中的类型确定数据类型,再传给相应的模块,如 IP、ARP 等。这里的例子则是 IP 。
⑥ IP 模块的处理IP 模块接收到 数据后也做类似的处理。从包首部中判断此 IP 地址是否与自己的 IP 地址匹配,如果匹配则根据首部的协议类型将数据发送给对应的模块,如 TCP、UDP。这里的例子则是 TCP。
另外吗,对于有路由器的情况,接收端地址往往不是自己的地址,此时,需要借助路由控制表,在调查应该送往的主机或路由器之后再进行转发数据。
⑦ TCP 模块的处理
在 TCP 模块中,首先会计算一下校验和,判断数据是否被破坏。然后检查是否在按照序号接收数据。最后检查端口号,确定具体的应用程序。数据被完整地接收以后,会传给由端口号识别的应用程序。
⑧ 应用程序的处理
接收端应用程序会直接接收发送端发送的数据。通过解析数据,展示相应的内容。
4.TCP的通讯原理
Socket套接字
ip地址加端口就是套接字.
Socket 的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)。
区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。通过将这3个参数结合起来,与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
套接字对是一个定义该连接的两个端点的四元组:本地IP地址、本地TCP端口号、外地IP地址、外地TCP端口号。套接字对唯一标识一个网络上的每个TCP连接。
TCP缓冲区
每个TCP的Socket的内核中都有一个发送缓冲区和一个接收缓冲区。现在我们假设用write()方法发送数据,使用 read()方法接收数据。
write()并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。
TCP协议独立于 write()函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。
read()也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。
总得来说,I/O缓冲区在每个TCP套接字中单独存在;I/O缓冲区在创建套接字时自动生成;
5.TCP 的可靠性
在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个已收到消息的通知。这个消息叫做确认应答(ACK)。当发送端将数据发出之后会等待对端的确认应答。如果有确认应答,说明数据已经成功到达对端。反之,则数据丢失的可能性很大。
在一定时间内没有等待到确认应答,发送端就可以认为数据已经丢失,并进行重发。由此,即使产生了丢包,仍然能够保证数据能够到达对端,实现可靠传输。
未收到确认应答并不意味着数据一定丢失。也有可能是数据对方已经收到,只是返回的确认应答在途中丢失。这种情况也会导致发送端误以为数据没有到达目的地而重发数据。
此外,也有可能因为一些其他原因导致确认应答延迟到达,在源主机重发数据以后才到达的情况也屡见不鲜。此时,源主机只要按照机制重发数据即可。
对于目标主机来说,反复收到相同的数据是不可取的。为了对上层应用提供可靠的传输,目标主机必须放弃重复的数据包。为此我们引入了序列号。
序列号是按照顺序给发送数据的每一个字节(8位字节)都标上号码的编号。接收端查询接收数据 TCP 首部中的序列号和数据的长度,将自己下一步应该接收的序列号作为确认应答返送回去。通过序列号和确认应答号,TCP 能够识别是否已经接收数据,又能够判断是否需要接收,从而实现可靠传输。
6.TCP中的滑动窗口
发送方和接收方都会维护一个数据帧的序列,这个序列被称作窗口。发送方的窗口大小由接收方确认,目的是控制发送速度,以免接收方的缓存不够大导致溢出,同时控制流量也可以避免网络拥塞。
在TCP 的可靠性的图中,我们可以看到,发送方每发送一个数据接收方就要给发送方一个ACK对这个数据进行确认。只有接收了这个确认数据以后发送方才能传输下个数据。
存在的问题:如果窗口过小,当传输比较大的数据的时候需要不停的对数据进行确认,这个时候就会造成很大的延迟。
如果窗口过大,我们假设发送方一次发送100个数据,但接收方只能处理50个数据,这样每次都只对这50个数据进行确认。发送方下一次还是发送100个数据,但接受方还是只能处理50个数据。这样就避免了不必要的数据来拥塞我们的链路。
因此,我们引入了滑动窗口。滑动窗口通俗来讲就是一种流量控制技术。
它本质上是描述接收方的TCP数据报缓冲区大小的数据,发送方根据这个数据来计算自己最多能发送多长的数据,如果发送方收到接收方的窗口大小为0的TCP数据报,那么发送方将停止发送数据,等到接收方发送窗口大小不为0的数据报的到来。
首先是第一次发送数据这个时候的窗口大小是根据链路带宽的大小来决定的。我们假设这个时候窗口的大小是3。这个时候接受方收到数据以后会对数据进行确认告诉发送方我下次希望手到的是数据是多少。这里我们看到接收方发送的ACK=3(这是发送方发送序列2的回答确认,下一次接收方期望接收到的是3序列信号)。这个时候发送方收到这个数据以后就知道我第一次发送的3个数据对方只收到了2个。就知道第3个数据对方没有收到。下次在发送的时候就从第3个数据开始发。
此时窗口大小变成了2 。
于是发送方发送2个数据。看到接收方发送的ACK是5就表示他下一次希望收到的数据是5,发送方就知道我刚才发送的2个数据对方收了这个时候开始发送第5个数据。
这就是滑动窗口的工作机制,当链路变好了或者变差了这个窗口还会发生变话,并不是第一次协商好了以后就永远不变了。
所以滑动窗口协议,是TCP使用的一种流量控制方法。该协议允许发送方在停止并等待确认前可以连续发送多个分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。
只有在接收窗口向前滑动时(与此同时也发送了确认),发送窗口才有可能向前滑动。
收发两端的窗口按照以上规律不断地向前滑动,因此这种协议又称为滑动窗口协议。
UDP协议
UDP协议(用户数据报协议)是无连接,不可靠的,传输的顺序也是不固定的,它的速度相对来说会比较快,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接(不需要像TCP那样三次握手),所以可以实现广播发送。
UDP通讯时不需要接收方确认,属于不可靠的传输,可能会出现丢包现象,实际应用中要求程序员编程验证。
UDP协议以数据报作为数据传输的载体.当我们使用UPD进行数据传输时,会将数据封装成Datagram(数据报)当中去标识指明数据所达的目的地以及相应的端口号,然后将数据报传输出去.
UDP与TCP位于同一层,但它不管数据包的顺序、错误或重发。因此,UDP不被应用于那些面向连接的服务,UDP主要用于那些面向查询—-应答的服务,例如NFS。相对于FTP或Telnet,这些服务需要交换的信息量较小。使用UDP的服务包括NTP(网络时间协议)和DNS(DNS也使用TCP),包总量较少的通信(DNS、SNMP等);2.视频、音频等多媒体通信(即时通信);3.限定于 LAN 等特定网络中的应用通信;4.广播通信(广播、多播)。
常用的QQ,就是一个以UDP为主,TCP为辅的通讯协议。
TCP 和 UDP 的优缺点无法简单地、绝对地去做比较:TCP 用于在传输层有必要实现可靠传输的情况;而在一方面,UDP 主要用于那些对高速传输和实时性有较高要求的通信或广播通信。TCP 和 UDP 应该根据应用的目的按需使用。
DatagramoPacket
表示数据报包,用来表示UDP通信中的数据单元
DatagramSocket
进行端到端通信的类,实现基于UDP的Socket通信
服务器和客户端一对一通信
报文组成(了解就行)
报文组成
1.源端口:源端口号,在需要对方回信时选用,不需要时可用全0。
2.目的端口:目的端口号,这在终点交付报文时必须要使用到。
3.长度: UDP用户数据包的长度,其最小值是8(仅有首部)。
4.校验和:检测UDP用户数据报在传输中是否有错,有错就丢弃。
2、Java原生网络编程
(一)一些常见术语
编程中的Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议(底层的TCP和UPD我们就不管了)。
主机 A 的应用程序要能和主机 B 的应用程序通信,必须通过 Socket 建立连接,而建立 Socket 连接必须需要底层TCP/IP 协议来建立 TCP 连接。建立 TCP 连接需要底层 IP 协议来寻址网络中的主机。我们知道网络层使用的 IP 协议可以帮助我们根据 IP 地址来找到目标主机,但是一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通信就要通过 TCP 或 UPD 的地址也就是端口号来指定。这样就可以通过一个 Socket 实例唯一代表一个主机上的一个应用程序的通信链路了。
短连接:
连接->传输数据->关闭连接 HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接(一次请求响应完成以后就关闭连接,这个就是所谓的典型的短连接)。
长连接:
连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。
长连接指建立SOCKET连接后不管是否使用都保持连接,不断的进行数据发送,但安全性较差。
什么时候用长连接,短连接?
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,处理时直接发送数据包就OK了,不用建立TCP连接。
例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。
而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源(对服务器是有一定压力的,任何一个连接的建立对操作系统来讲,它都要消耗各种资源的,不管是内存,还是文件句柄数等等,都需要有压力,连接数一般不可能维持的太多),而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。
为什么数据库用长连接,因为数据库对数据的操作是很频繁的.
为什么http网站应用上要用短连接呢,因为长连接对我们的服务器一定是消耗一定的资源的,我们希望web网站能够支持成千上万的用户访问,我们使用短连接能够迅速的把服务器资源给释放掉,来去服务更多的用户,所以这是短连接的好处
短连接和长连接就是在我们连接完成以后,我要传输数据, 我传输数据以后我是马上断开连接呢,还是保持着连接不放呢,这就是短连接和长连接的区别,其实其它的都没有太大的区别.
总之,长连接和短连接的选择要视情况而定。
长连接短连接缺点
长连接缺点:
对服务器是有一定压力的,任何一个连接的建立对操作系统来讲,它都要消耗各种资源的,不管是内存,还是文件句柄数等等,都需要有压力,连接数一般不可能维持的太多.
短连接缺点:
每一次的连接都需要三次握手,需要消耗时间,断开连接之后我还需要四次挥手,我还需要时间,短连接在时间的消耗上对网络的带宽往往还是一种压力
2.TCP通讯原理
Socket套接字
TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket),在网络中唯一可以标识唯一通讯的就是Socket套接字对:源IP地址和源端口号 加上目标的IP地址和目标的端口号.
TCP缓冲区
每个TCP的Socket的内核中都有一个发送缓冲区和一个接收缓冲区
在我们的实际网络通讯里面,当我们每当new出来一个 socket通讯的时候, 操作系统会为我们实例化两个缓冲区出来: 一个是输入缓冲区,一个是输出缓冲区,任何网络通讯传递过来的数据首先会到输入缓冲区,当我们通过write()方法或者通过其它的方法去往网络上写的时候,其实是写在输出缓冲区里面. 同时TCP是一个全双工的协议,意味着我同时的输入流和输出流可以同时进行(并行操作).
TCP的可靠性与高效性
有了缓冲区之后我们怎样传输才能确保高效与可靠?
通过序列号和确认应答来提高可靠性,也就是说我主机A向主机B发送的数据包,一定要主机B给主机A一个所谓的应答,没有应答的话,主机A就会考虑所谓的重发.
没有应答有两种情况,有可能主机A发送给主机B的数据包丢了,也有可能是主机B发送给主机A的确认包丢了,这两种情况主机A都会把上一个数据包进行重发.
第二种情况是主机A发送数据包过去,主机B做了应答,这时候主机A在等待时间内没有收到应答它会重发上一个数据包,这个时候主机B收到了数据包会怎么做,因为在主机B的缓存里面主机B是已经收到了上一个序号为1000的(假设上一个序号为1000的),所以主机B会吧刚刚发过来的数据包丢弃掉,然后继续发送应答包告诉主机A,你下次发送的是从1001号开始的(假设上一个序号为1000的).
窗口机制(以后去研究研究)
滑动窗口
发送方和接收方都会维护一个数据帧的序列,这个序列被称作 窗口 ,这个窗口是一个变动的窗口
(二)Linux网络IO模型
因为我们项目是部署到Linux上面的,所以windows的IO模型是不需要理会的,我们只需要理会Linux的IO模型.
1.同步和异步,阻塞和非阻塞
同步和异步
关注的是结果的通知机制
同步:同步的意思就是调用方需要主动等待结果的返回
异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等。
阻塞和非阻塞
关注的是结果返回以前,我调用方的状态
阻塞:程序发起调用之后在等待请求结果的时候,线程会被挂起(不能做任何的事情),调用线程只有在得到结果之后才会返回(往下进行下面的业务逻辑).
非阻塞:虽然不能立马得到结果,线程可以解放起来去做一些其他事,不会被挂起,此线程还可以干其他事.
两者的组合
- 同步阻塞:
同步阻塞基本也是编程中最常见的模型,打个比方你去商店买衣服,你去了之后发现衣服卖完了,那你就在店里面一直等,期间不做任何事(包括看手机),等着商家进货,直到有货为止,这个效率很低。
方法调用就是一个同步阻塞
2. 同步非阻塞:
同步非阻塞在编程中可以抽象为一个轮询模式,你去了商店之后,发现衣服卖完了,这个时候不需要傻傻的等着,你可以去其他地方比如奶茶店,买杯水,但是你还是需要时不时的去商店问老板新衣服到了吗。
3. 异步阻塞:
异步阻塞这个编程里面用的较少,有点类似你写了个线程池,submit然后马上future.get(),这样线程其实还是挂起的。有点像你去商店买衣服,这个时候发现衣服没有了,这个时候你就给老板留给电话,说衣服到了就给我打电话,然后你就守着这个电话,一直等着他响什么事也不做。这样感觉的确有点傻,所以这个模式用得比较少。
我们把一个任务投放到一个线程池里面去了以后,这个时候由线程池里面的线程来执行任务,我们当前的线程就继续往前面走,做些我们其它的事情,在未来的某一时刻,我的事情做完了,我们通过所谓的FutureTask.get()方法去获取任务的结果,这是我们使用线程池的目的.
4. 异步非阻塞:
异步非阻塞。好比你去商店买衣服,衣服没了,你只需要给老板说这是我的电话,衣服到了就打。然后你就随心所欲的去玩,也不用操心衣服什么时候到,衣服一到,电话一响就可以去买衣服了。
2.五种I/O模型
重点关注 阻塞IO 和IO复用 , 其它的几种用的比较少了.
阻塞I/O模型
应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。
当调用recv()函数时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态。
这种情况效率是比较低下的.内核在拷贝数据的时候,当前线程是无法干别的事情的,只能等待数据来了之后,当前线程才能接着干活儿.
非阻塞IO模型 (了解)
当获取数据没数据的时候,应用程序不停的调用内核,每隔几毫秒不停的去调用内核看看有没有数据,会不停的调用,直到内核里面有数据为止,此时操作系统会把内核空间里面的数据拷贝到我们的应用程序的空间,这个时候我的程序会接着往下走.
这种情况下问题,在等待数据来的过程中也没阻塞,但是我们的线程不断的去问内核有没有数据,这是不断测试的过程,这个过程是非常的大量的占据CPU的时间.
所以,这种模型是绝对不推荐的,也用的很少.
IO复用模型(重点)
当我们调用select到内核去要数据的时候,没有数据准备好的时候,其实也是被阻塞的,但是好处是内核里面当有数据包准备好了以后,会告诉应用程序说,当前内核里面已经有数据了,于是应用程序会调用系统,去读写内核里面相关的数据包.
IO复用模型是两次系统调用,问内核是否有数据到来是一次调用,从内核里面获取数据又是一次调用.
IO复用模型最大优势是 可以允许一个线程去检测多个网络通讯,而在阻塞式IO里面,一个线程只能应付一个网络通讯.
Linux下面主要使用IO复用模型.
简介:主要是select和epoll两个系统调用;对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听;
I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。
当用户进程调用了select,那么整个进程会被block;而同时,kernel会“监视”所有select负责的socket;当任何一个socket中的数据准备好了,select就会返回。这个时候,用户进程再调用read操作,将数据从kernel拷贝到用户进程。
这个图和blocking IO的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用(select和recvfrom),而blocking IO只调用了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。(多说一句:所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)
信号驱动IO(了解)
简介:两次调用,两次返回;
首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。
异步IO模型(了解)
最先进的一种IO,为什么不用呢,因为Linux操作系统没有提供对应的支持,真正提供了异步IO是Windows下面的IOCP 模型.
异步IO是什么概念呢?
当前我去读某个通讯连接上的数据,我只要向操作系统发送数据调用,系统内核会马上给我一个数据的返回,于是我们当前的线程就可以解放出来了,我们可以马上完全自由的做自己任何事情,然后当数据来的时候,这个操作系统不会通知我们,操作系统会进行默默的数据等待,数据来了以后内核还帮你把数据从它的内核空间里面拷贝到用户空间以后,拷贝完了以后再来通知当前线程.
这就是一个所谓的异步IO,对于应用程序来讲,效率是非常高的.我只要往内核注册所谓的异步通知的一种所谓的信号,内核帮我们把所有的事情都全部做完了再来通过我们当前的工作线程.
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作
3.5个I/O模型的比较
不同I/O模型的区别,其实主要在等待数据和数据复制这两个时间段不同,图形中已经表示得很清楚了。
select、poll、epoll的区别?(Java程序员不需要太了解)
一线互联网公司会问到
1、支持一个进程所能打开的最大连接数
select | 单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响。 |
---|---|
poll | poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的 |
epoll | 虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接 |
2、FD剧增后带来的IO效率问题
select | 因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。 |
---|---|
poll | 同上 |
epoll | 因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。 |
3、 消息传递方式
select | 内核需要将消息传递到用户空间,都需要内核拷贝动作 |
---|---|
poll | 同上 |
epoll | epoll通过内核和用户空间共享一块内存来实现的。 |
总结:
综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。
1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善
补充知识点:
Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!
Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!
select(),poll()模型都是水平触发模式,信号驱动IO是边缘触发模式,epoll()模型即支持水平触发,也支持边缘触发,默认是水平触发。
4.网络编程里通用常识
既然是通信,那么是肯定是有两个对端的,(就和James老师去大保健一样的,一个人怎么大保健呢?必须要有james老师和技师两个人才能进行,james老师总不能在大保健里自娱自乐,那还去大保健干嘛?那么在大保健里提供服务的场所叫会所或者某某中心,具体提供服务的那个人叫技师,享受服务的那个人叫james老师)。在通信编程里提供服务的叫服务端,连接服务端使用服务的叫客户端。在开发过程中,如果类的名字有Server或者ServerSocket的,表示这个类是给服务端用的,如果类的名字只有Socket的,那么表示这是负责具体的网络读写的。那么对于服务端来说ServerSocket就只是个场所,具体和客户端沟通的还是一个一个的socket,所以在通信编程里,ServerSocket并不负责具体的网络读写,ServerSocket就只是负责接收客户端连接后,新启一个socket来和客户端进行沟通。这一点对所有模式的通信编程都是适用的。
在通信编程里,我们关注的其实也就是三个事情:连接(客户端连接服务器,服务器等待和接收连接)、读网络数据、写网络数据,所有模式的通信编程都是围绕着这三件事情进行的。
5.原生JDK网络编程BIO
服务端提供IP和监听端口,客户端通过连接操作想服务端监听的地址发起连接请求,通过三次握手连接,如果连接成功建立,双方就可以通过套接字进行通信。
传统的同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。
传统BIO通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理没处理完成后,通过输出流返回应答给客户端,线程销毁。即典型的一请求一应答模型。
该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,Java中的线程也是比较宝贵的系统资源,线程数量快速膨胀后,系统的性能将急剧下降,随着访问量的继续增大,系统最终就死-掉-了。
为了改进这种一连接一线程的模型,我们可以使用线程池来管理这些线程,实现1个或多个线程处理N个客户端的模型(但是底层还是使用的同步阻塞I/O),通常被称为“伪异步I/O模型“。
我们知道,如果使用CachedThreadPool线程池(不限制线程数量,如果不清楚请参考文首提供的文章),其实除了能自动帮我们管理线程(复用),看起来也就像是1:1的客户端:线程数模型,而使用FixedThreadPool我们就有效的控制了线程的最大数量,保证了系统有限的资源的控制,实现了N:M的伪异步I/O模型。
但是,正因为限制了线程数量,如果发生读取数据较慢时(比如数据量大、网络传输慢等),大量并发的情况下,其他接入的消息,只能一直等待,这就是最大的弊端。
如何使用,参见模块bio下的代码
6.BIO应用-RPC框架
为什么要有RPC?
我们最开始开发的时候,一个应用一台机器,将所有功能都写在一起,比如说比较常见的电商场景。
随着我们业务的发展,我们需要提示性能了,我们会怎么做?将不同的业务功能放到线程里来实现异步和提升性能。
但是业务越来越复杂,业务量越来越大,单个应用或者一台机器的资源是肯定背负不起的,这个时候,我们会怎么做?将核心业务抽取出来,作为独立的服务,放到其他服务器上或者形成集群。这个时候就会请出RPC,系统变为分布式的架构。
为什么说千万级流量分布式、微服务架构必备的RPC框架?和LocalCall的代码进行比较,因为引入rpc框架对我们现有的代码影响最小,同时又可以帮我们实现架构上的扩展。现在的开源rpc框架,有什么?dubbo,grpc等等
当服务越来越多,各种rpc之间的调用会越来越复杂,这个时候我们会引入中间件,比如说MQ、缓存,同时架构上整体往微服务去迁移,引入了各种比如容器技术docker,DevOps等等。最终会变为如图所示来应付千万级流量,但是不管怎样,rpc总是会占有一席之地。
什么是RPC?
RPC(Remote Procedure Call ——远程过程调用),它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络的技术。
一次完整的RPC同步调用流程:
1)服务消费方(client)以本地调用方式调用客户端存根;
2)什么叫客户端存根?就是远程方法在本地的模拟对象,一样的也有方法名,也有方法参数,client stub接收到调用后负责将方法名、方法的参数等包装,并将包装后的信息通过网络发送到服务端;
3)服务端收到消息后,交给代理存根在服务器的部分后进行解码为实际的方法名和参数
4) server stub根据解码结果调用服务器上本地的实际服务;
5)本地服务执行并将结果返回给server stub;
6)server stub将返回结果打包成消息并发送至消费方;
7)client stub接收到消息,并进行解码;
8)服务消费方得到最终结果。
RPC框架的目标就是要中间步骤都封装起来,让我们进行远程方法调用的时候感觉到就像在本地调用一样。
如果上面看不懂看下面的
当客户端想要调用远程的服务的时候,客户端会以本地调用的方式调用在客户端的一个所谓的存根,存根(Client Stub)会把方法名和方法参数打包成可以在网络上传输的0101串儿,然后把这个0101串通欧网络传输到服务器端.
服务器也会有服务器的存根(Server Stub),它负责把网络上0101串转化成为方法名和方法参数的等等东西,根据解码后的方法名调用在服务器上某一个具体的服务,然后服务器再把结果通过服务器的存根再通过网络传给我们的客户端存根.
客户端的存根再把结果解码返回给我们真正意义上的调用的客户端(服务的消费方).
RPC框架目的
给客户端调用服务器的过程封装起来,让我们在进行远程调用的服务器(服务的提供者)方法服务,感觉像是在调用自己本地的方法. 这就是RPC框架的目的,不管你用Dubbo还是其它的PRC框架无外乎就是在做上面的事情,把所有复杂的网络调用相关的部分都封装起来.只不过是实现上和性能上各有不同而已.
RPC和HTTP
rpc字面意思就是远程过程调用,只是对不同应用间相互调用的一种描述,一种思想。具体怎么调用?实现方式可以是最直接的tcp通信,也可以是http方式,在很多的消息中间件的技术书籍里,甚至还有使用消息中间件来实现RPC调用的,我们知道的dubbo是基于tcp通信的,gRPC是Google公布的开源软件,基于最新的HTTP2.0协议,底层使用到了Netty框架的支持。所以总结来说,rpc和http是完全两个不同层级的东西,他们之间并没有什么可比性。
实现RPC框架
①实现RPC框架需要解决的那些问题
1)代理问题
代理本质上是要解决什么问题?要解决的是被调用的服务本质上是远程的服务,但是调用者不知道也不关心,调用者只要结果,具体的事情由代理的那个对象来负责这件事。既然是远程代理,当然是要用代理模式了。
代理(Proxy)是一种设计模式,即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。那我们这里额外的功能操作是干什么,通过网络访问远程服务。
jdk的代理有两种实现方式:静态代理和动态代理。
2)序列化问题
序列化问题在计算机里具体是什么?我们的方法调用,有方法名,方法参数,这些可能是字符串,可能是我们自己定义的java的类,但是在网络上传输或者保存在硬盘的时候,网络或者硬盘并不认得什么字符串或者javabean,它只认得二进制的01串,怎么办?要进行序列化,网络传输后要进行实际调用,就要把二进制的01串变回我们实际的java的类,这个叫反序列化。java里已经为我们提供了相关的机制Serializable。
3)通信问题
我们在用序列化把东西变成了可以在网络上传输的二进制的01串,但具体如何通过网络传输?使用JDK为我们提供的BIO。
4)登记的服务实例化
登记的服务有可能在我们的系统中就是一个名字,怎么变成实际执行的对象实例,当然是使用反射机制。
反射机制是什么?
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射机制能做什么
反射机制主要提供了以下功能:
•在运行时判断任意一个对象所属的类;
•在运行时构造任意一个类的对象;
•在运行时判断任意一个类所具有的成员变量和方法;
•在运行时调用任意一个对象的方法;
•生成动态代理。
最后成型的代码参见模块rpc-client和rpc-server
//*
网络编程(Socket)
1.网络程序设计
网络程序设计是指编写与其他计算机进行通信的程序,Java已经将网络程序所需要的东西封装成了不同的类,只要创建这些类的对象,使用相应的方法,即使设计人员不具备有关的网络知识,也可以编写出高质量的网络通信程序.
两台计算机要想通过网络进行通信,它们需要满足一些必然的条件
1. 两台主机需要唯一的标识,用来表示它们所处的身份,所在的位置(就是ip地址)
2. 需要共同的语言,否则就会出现言语不通,无法交流(这就是协议)
3. 每台主机都需要有相应的端口号,一台主机上可以运行多台不同的应用程序,如果辨别不同的运行程序的通信,我们需要使用端口号来进行区分
TCP/IP是目前世界上应用最为广泛的协议.,它是以TCP和IP为基础的不同的层次上的多个协议的集合.也称为 TCP/IP协议族或者TCP/IP协议栈.
两个主机之间要进行通讯,都要遵守 TCP 和IP的协议.
TCP全称为 传输控制协议 (Transmission Control Protocol)
IP全称为 互联网协议 (Internet Protocol)
TCP/IP模型
5层
1. 物理层:
网线,双脚线,网卡等等
2.数据链路
3.网络
4.传输
5.应用
HTTP超文本传输协议
TCP/IP协议
FTP 文件传输协议
SMTP 简单邮件传送协议
Telnet 远程登录服务
IP地址
每台计算机的唯一标识,类似于每个人的手机号码, 张三想和李四进行通信,就得知道手机号码,通过手机号定位到李四的人, ip地址也是这样.如果想换另外一个计算机通信,就得知道那个人的ip地址.
ip地址标准:
ipv4
长度为32位的二进制 比如192.168.0.1
ipv6
端口
区分不同的应用程序,一个主机可以同时运行多个程序,比如qq和迅雷,旺旺等等,如何保证一台主机给另外一台主机发送的信息被对方程序正确的接收. 每一个应用都有唯一的端口号来标识不同的应用程序,
端口号的范围为0~65535,其中0~1023为系统所保留下来分配给系统用的服务.我们自定义端口号建议用1023后面的端口号
ip地址和端口号组成了所谓的Socket,Socket是网络上运行的程序直接双向通信链路的终结点,是TCP和UDP的基础.
什么是网络通信协议
网络通信协议 类似于 交通系统中的交通法规,网络通信协议规定网上传输的数据格式 传输速度,数据大小等规定.
常用网络通信协议:
TCP/IP协议
UDP协议
其他协议:FTP,HTTP,HTTPS
(二)Java中的网络支持
Java提供的网络功能的四大类:
1. InetAddress : 用于标识网络上的硬件资源(比如标识Ip地址相关信息)
2. URL : 统一资源定位符,通过URL可以直接读取或写入网络上的数据.
3. Socket: 使用TCP协议实现网络通信的Socket相关的类.
4. Datagram: 使用UDP协议,使用Datagram进行通信时,数据保存在数据报中,通过在网络中发送数据报进而实现网络的通信.
1.InetAddress 类
InetAddress类用于标识网络上的硬件资源,表示互联网协议(IP)地址.
java.net包中的InetAddress类是与ip地址相关的类,利用该类可以获取ip地址主机地址等信息
| /**
- 静态方法获取本机实例
* - @throws UnknownHostException
/
@Test
public void ceui1() throws UnknownHostException {
InetAddress localHost = InetAddress.getLocalHost();//获取本机的实例(直接syso输出的话获取的是 计算机名和ip地址的信息)
//输出:AS5I1LSDKRB5TOU/192.168.192.1
String hostName = localHost.getHostName(); //获取主机名
//输出:AS5I1LSDKRB5TOU
String hostAddress = localHost.getHostAddress();//获取主机地址
//输出:192.168.192.1
byte[] address = localHost.getAddress();//获取字节数组形式的ip地址
String toString = Arrays.toString(address);
//输出:[-64, -88, -64, 1]
}
/* - 根据机器名获取InetAddress实例
* - @throws UnknownHostException
/
@Test
public void ceui() throws UnknownHostException {
/根据机器名获取InetAddress实例/
InetAddress address = InetAddress.getByName(“AS5I1LSDKRB5TOU”);//AS5I1LSDKRB5TOU/192.168.192.1
String hostName = address.getHostName(); //计算机名 输出:AS5I1LSDKRB5TOU
String hostAddress = address.getHostAddress(); //ip地址 输出:192.168.192.1
}
/* 根据ip地址获取实例
/
@Test
public void ceui2() throws UnknownHostException {
InetAddress byName = InetAddress.getByName(*”192.168.192.1”);//输出:AS5I1LSDKRB5TOU/192.168.192.1
String hostName = byName.getHostName(); //计算机名 输出 AS5I1LSDKRB5TOU
String hostAddress = byName.getHostAddress();//ip地址 输出 192.168.192.1
} | | —- |2.URL类
URL
1.Uniform Resource Locator 统一资源定位符,表示Internet上某一资源的地址
2.URL由两部分组成:协议名称和资源名称,中间用冒号隔开.
网站: http://www.imobile.com.cn
http: 表示 http协议
www.imobile.com.cn : 表示资源名称
| /**
- URl 类的基本使用
@throws MalformedURLException
/
@Test
public void ceui1() throws MalformedURLException {
//创建 URL实例
URL imooc = new URL(“https://www.imooc.com“);
/ 根据已有的url实例创建新的url
?后面表示参数表示锚点
- /
URL url = new URL(imooc, “/index.html?username=tom#test”);
String protocol = url.getProtocol();//获取协议信息.输出https
String host = url.getHost();//资源所在的主机 输出www.imooc.com
/为什么端口号是 -1 , 因为我们在指定url资源的时候并没有指定端口号, - 没有指定端口号就使用的是协议默认的端口号,http协议使用的默认是80端口号
*为什么端口号返回的是 -1 呢?
- 因为我们在创建url实例的时候并没有指定端口号,它用的确实是默认端口号,也就是80
- 然后对于url.getPort() 方法而言,如果未指定端口号,它的返回值就是-1
- /
*int port = url.getPort();//url的端口号 输出-1
String path = url.getPath();//获取文件路径 输出 /index.html
String file = url.getFile();//文件名 输出/index.html?username=tom
String ref = url.getRef();//相对路径 输出test
String query = url.getQuery();//查询字符串 输出username=tom
} | | —- |
使用URL读取网页内容
1. 通过URl对象的openStream()方法可以得到指定资源的输入流
2. 通过输入流可以读取,访问网络上的数据.
| /**
* 使用url读取网页内容<br />
*/<br />
@Test
public void ceui2() throws IOException {
URL url = new URL(“http://www.baidu.com“);
InputStream is = url.openStream(); //获取url对象所表示的资源的字节输入流
InputStreamReader isr = new InputStreamReader(is, “utf-8”);//将字节输入流转换为字符输入流
//给字符流添加缓冲,提高读取效率
BufferedReader br = new BufferedReader(isr);
String data = br.readLine();//一次读取一行
while (data != null) {
System.out.println(data);
data = br.readLine(); //读取下一行
}
br.close();
isr.close();
is.close();
/*控制台信息就是html页面 :
* <!DOCTYPE html><br />
………. 因为太长就不显示了,直接逗号省略 */
} |
| —- |
3.基于TCP的Socket用法
TCP协议是面向连接,可靠的,有顺序的,以字节流的方式发送数据
基于TCP协议实现网络通信的类:
1.客户端的Socket类
2.服务器端的ServerSocket类
Socket通信模型
两台主机需要通信,就必然的存在着一个服务端,另外一个是客户端.
1. 服务端建立一个ServerSocket绑定相应的端口,并且在指定的端口进行监听,等待客户端的连接
2. 客户端创建连接的socket并且向服务器端发送请求
3. 服务器收到请求,并且接受到客户端的请求信息,一旦接到客户端的请求以后会创建一个连接的socket用来和客户端的socket进行通信.
4. 服务端和客户端通过相关的输入流和输出流开始通信,进行数据的交换,数据就发送和接收以及数据的响应等等.
5. 在客户端和服务器端双方通信完以后,我们需要分别关闭客户端和服务端的socket进行通信的端口.
Socket实现步骤
1. 创建ServerSocket和Socket建立,这是实现网络通信的基础
2. 打开连接到Socket的输入/输出流进行相关的通信
3. 根据相应的协议,对Socket进行读/写操作
4. 在通信完成以后关闭输入输出流,关闭Socket
一个服务端和一个客户端通信
一个服务端和多个客户端通信(使用多线程)
4.网络编程总结
1. 关于多线程优先问题
服务器需要不停的与多个客户端进行通信,服务器端程序需要不停的监听客户端的请求,就需要使用多线程了,既然是死循环,线程就需要优先级问题.如果没有设置优先级可能会导致在运行时效率低下,速度比较慢.
2. 是否关闭输出流和输入流
对于同一个socket,如果关闭了输出流,则与该输出流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可
3.客户端向服务器端发送的数据都是通过字符串的形式进行交互的,而实际的运用当中,客户端向服务端发送的信息更多的都是以对象的形式进行传输,我们把用户名密码等等信息可以封装成user对象,传递的是一个user对象,这就是面向对象的思想,这时候就可以去使用ObjectOutputStream ObjectInputStream来实现对象的传输.
4. socket传递文件
可以使用 FileOutputStream 去读取文件中的数据,