• Netty


    OSI七层网络模型介绍

    • 应用层:Http协议、电子文件传输、文件服务器等
    • 表示层:解决我们不同系统之间语法的通讯
    • 会话层:建立与应用程序之间的通讯
    • 传输层:提供了端口号和接口协议TPC/UDP
    • 网络层:为数据包选择路由 路由器、交换机,定义了ip地址,可以根据ip地址找到对应的服务器
    • 数据链路层:传输有地址的帧以及错误检测功能
    • 物理层:以二进制形式,在物理机器上实现传输(光纤、各种物理介质传输)

    Socket是什么?

    socket(套接字)是两个程序之间通过双向信道进行数据交换的端,可以理解为接口。使用socket编程也称为网络编程,socket只是接口并不是网络通信协议。

    TCP、UDP的区别(白话文和socket技术中的)?

    TCP是面向连接的可靠协议、通过三次握手建立连接,通讯完成时拆除连接
    UDP是面向无连接通讯协议,udp通讯时不需要接受方确定,属于不可靠传输,可能会存在丢包的现象。

    Http协议长连接和短连接的区别?

    Http协议1.0: 规定客户端与浏览器保持短暂的连接,服务器完成数据传输数据之后就会立马关闭连接,如果频繁的向服务器端发送请求的话,这时候是非常消耗服务器资源。
    Http协议1.1: Http协议从1.1开始就支持长连接,会根据特定时间保持当前连接状态,不会立马关闭掉tcp连接;
    Http协议建立长连接之后,默认情况下在300s为空闲状态情况下会主动断开连接;

    http和https的区别?

    1、https协议需要到ca申请bai证书,一般免费证书较少,因而需要一定费用自费。
    2、http是超文本传输dao协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
    3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
    4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

    HTTP的组成部分?

    HTTP 请求的组成
    状态行(请求方法、路径、http版本)、请求头(编码、域名、连接、请求得代理)、消息主体(请求参数)三部分组成。

    HTTP 响应的组成
    状态行、响应头、响应正文

    HTTP请求的状态码有哪些?

    1XX:通知
    1XX系列响应代码仅在与HTTP服务器沟通时使用。

    • 100 :这是对HTTP LBYL(look-before-you-leap)请求的一个可能的响应。该响应代码表明:客户端应重新发送初始请求,并在请求中附上第一次请求时未提供的(可能很大或者包含敏感信息的)表示。客户端这次发送的请求不会被拒绝。对LBYL请求的另一个可能的响应是417(“Expectation Failed”)。
      请求报头:要做一个LBYL请求,客户端必须把Expect请求报头设为字符串"100-continue"。除此以外,客户端还需要设置其他一些报头,服务器将根据这些报头决定是响应100还是417。
    • 101 :当客户端通过在请求里使用Upgrade报头,以通知服务器它想改用除HTTP协议之外的其他协议时,客户端将获得此响应代码。101响应代码表示“行,我现在改用另一个协议了”。通常HTTP客户端会在收到服务器发来的101响应后关闭与服务器的TCP连接。101响应代码意味着,该客户端不再是一个HTTP客户端,而将成为另一种客户端。
      尽管可以通过Upgrade报头从HTTP切换到HTTPS,或者从HTTP1.1切换到某个未来的版本,但实际使用Upgrade报头的情况比较少。Upgrade报头也可用于HTTP切换到一个完全不同的协议(如IRC)上,但那需要在Web服务器切换为一个IRC服务器的同时,Web客户端切换为一个IRC的客户端,因为服务器将立刻在同一个TCP连接上开始使用新的协议。
      请求报头:客户端把Upgrade报头设置为一组希望使用的协议。
      响应报头:如果服务器同意切换协议,它就返回一个Upgrade报头,说明它将切换到那个协议,并附上一个空白行。服务器不用关闭TCP链接,而是直接在该TCP连接上开始使用新的协议。

    2XX:成功
    2XX系列响应代码表明操作成功了。

    • 200 :一般来说,这是客户端希望看到的响应代码。它表示服务器成功执行了客户端所请求的动作,并且在2XX系列里没有其他更适合的响应代码了。
      实体主体:对于GET请求,服务器应返回客户端所请求资源的一个表示。对于其他请求,服务器应返回当前所选资源的一个表示,或者刚刚执行的动作的一个描述。
    • 201 :当服务器依照客户端的请求创建了一个新资源时,发送此响应代码。
      响应报头:Location报头应包含指向新创建资源的规范URI。
      实体主体:应该给出新创建资源的描述与链接。若已经在Location报头里给出了新资源的URI,那么可以用新资源的一个表示作为实体主体。
    • 202 :客户端的请求无法或将不被实时处理。请求稍后会被处理。请求看上去是合法的,但在实际处理它时有出现问题的可能。
      若一个请求触发了一个异步操作,或者一个需要现实世界参与的动作,或者一个需要很长时间才能完成且没必要让Web客户端一直等待的动作时,这个相应代码是一个合适的选择。
      响应报头:应该把未处理完的请求暴露为一个资源,以便客户端稍后查询其状态。Location报头可以包含指向该资源的URI。
      实体主体:若无法让客户端稍后查询请求的状态,那么至少应该提供一个关于何时能处理该请求的估计。
    • 203 :这个响应代码跟200一样,只不过服务器想让客户端知道,有些响应报头并非来自该服务器–他们可能是从客户端先前发送的一个请求里复制的,或者从第三方得到的。
      响应报头:客户端应明白某些报头可能是不准确的,某些响应报头可能不是服务器自己生成的,所以服务器也不知道其含义。
    • 204 :若服务器拒绝对PUT、POST或者DELETE请求返回任何状态信息或表示,那么通常采用此响应代码。服务器也可以对GET请求返回此响应代码,这表明“客户端请求的资源存在,但其表示是空的”。注意与304(“Not Modified”)的区别。204常常用在Ajax应用里。服务器通过这个响应代码告诉客户端:客户端的输入已被接受,但客户端不应该改变任何UI元素。
      实体主体:不允许。
    • 205 :它与204类似,但与204不同的是,它表明客户端应重置数据源的视图或数据结构。假如你在浏览器里提交一个HTML表单,并得到响应代码204,那么表单里的各个字段值不变,可以继续修改它们;但假如得到的响应代码205,那么表单里的各个字段将被重置为它们的初始值。从数据录入方面讲:204适合对单条记录做一系列编辑,而205适于连续输入一组记录。
    • 206 :它跟200类似,但它用于对部分GET请求(即使用Range请求报头的GET请求)的响应。部分GET请求常用于大型二进制文件的断点续传。
      请求报头:客户端为Range请求报头设置一个值。
      响应报头:需要提供Date报头。ETag报头与Content-Location报头的值应该跟正常GET请求相同。
      若实体主体是单个字节范围(byte range),那么HTTP响应里必须包含一个Content-Range报头,以说明本响应返回的是表示的哪个部分,若实体主体是一个多部分实体(multipart entity)(即该实体主体由多个字节范围构成),那么每一个部分都要有自己的Content-Range报头。
      实体主体:不是整个表示,而是一个或者多个字节范围。

    3XX:重定向
    3XX系列响应代码表明:客户端需要做些额外工作才能得到所需要的资源。它们通常用于GET请求。他们通常告诉客户端需要向另一个URI发送GET请求,才能得到所需的表示。那个URI就包含在Location响应报头里。

    • 300 :若被请求的资源在服务器端存在多个表示,而服务器不知道客户端想要的是哪一个表示时,发送这个响应代码。或者当客户端没有使用Accept-*报头来指定一个表示,或者客户端所请求的表示不存在时,也发送这个响应代码。在这种情况下,一种选择是,服务器返回一个首选表示,并把响应代码设置为200,不过它也可以返回一个包含该资源各个表示的URI列表,并把响应代码设为300。
      响应报头:如果服务器有首选表示,那么它可以在Location响应报头中给出这个首选表示的URI。跟其他3XX响应代码一样,客户端可以自动跟随Location中的URI。
      实体主体:一个包含该资源各个表示的URI的列表。可以在表示中提供一些信息,以便用户作出选择。
    • 301 :服务器知道客户端试图访问的是哪个资源,但它不喜欢客户端用当前URI来请求该资源。它希望客户端记住另一个URI,并在今后的请求中使用那个新的URI。你可以通过这个响应代码来防止由于URI变更而导致老URI失效。
      响应报头:服务器应当把规范URI放在Location响应报头里。
      实体主体:服务器可以发送一个包含新URI的信息,不过这不是必需的。
    • 302 :这个响应代码是造成大多数重定向方面的混乱的最根本原因。它应该是像307那样被处理。实际上,在HTTP 1.0中,响应代码302的名称是”Moved Temporarily”,不幸的是,在实际生活中,绝大多数客户端拿它像303一样处理。它的不同之处在于当服务器为客户端的PUT,POST或者DELETE请求返回302响应代码时,客户端要怎么做。
      为了消除这一混淆,在HTTP 1.1中,该响应代码被重命名为"Found",并新加了一个响应代码307。这个响应代码目前仍在广泛使用,但它的含义市混淆的,所以我建议你的服务发送307或者303,而不要发送302.除非你知道正在与一个不能理解303或307的HTTP 1.0客户端交互。
      响应报头:把客户端应重新请求的那个URI放在Location报头里。
      实体主体:一个包含指向新URI的链接的超文本文档(就像301一样)。
    • 303 :请求已经被处理,但服务器不是直接返回一个响应文档,而是返回一个响应文档的URI。该响应文档可能是一个静态的状态信息,也可能是一个更有趣的资源。对于后一种情况,303是一种令服务器可以“发送一个资源的表示,而不强迫客户端下载其所有数据”的方式。客户端可以向Location报头里的URI发送GET请求,但它不是必须这么做。
      303响应代码是一种规范化资源URI的好办法。一个资源可以有多个URIs,但每个资源的规范URI只有一个,该资源的所有其他URIs都通过303指向该资源的规范URI,例如:303可以把一个对http://www.example.com/software/current.tar.gz的请求重定向到http://www.example.com/software/1.0.2.tar.gz。
      响应报头:Location报头里包含资源的URI。
      实体主体:一个包含指向新URI的链接的超文本文档。
    • 304 :这个响应代码跟204(“No Content”)类似:响应实体主体都必须为空。但204用于没有主体数据的情况,而304用于有主体数据,但客户端已拥有该数据,没必要重复发送的情况。这个响应代码可用于条件HTTP请求(conditional HTTP request).如果客户端在发送GET请求时附上了一个值为Sunday的If-Modified-Since报头,而客户端所请求的表示在服务器端自星期日(Sunday)以来一直没有改变过,那么服务器可以返回一个304响应。服务器也可以返回一个200响应,但由于客户端已拥有该表示,因此重复发送该表示只会白白浪费宽带。
      响应报头:需要提供Date报头。Etag与Content-Location报头的值,应该跟返回200响应时的一样。若Expires, Cache-Control及Vary报头的值自上次发送以来已经改变,那么就要提供这些报头。
      实体主体:不允许。
    • 305 :这个响应代码用于告诉客户端它需要再发一次请求,但这次要通过一个HTTP代理发送,而不是直接发送给服务器。这个响应代码使用的不多,因为服务器很少在意客户端是否使用某一特定代理。这个代码主要用于基于代理的镜像站点。现在,镜像站点(如http://www.example.com.mysite.com/)包含跟原始站点(如 http://www.example.com/)一样的内容,但具有不同的URI,原始站点可以通过307把客户端重新定向到镜像站点上。假如有基于代理的镜像站点,那么你可以通过把 http://proxy.mysite.com/设为代理,使用跟原始URI(http://www.example.com/)一样的URI来访问镜像站点。这里,原始站点example.com可以通过305把客户端路由到一个地理上接近客户端的镜像代理。web浏览器一般不能正确处理这个响应代码,这是导致305响应代码用的不多的另一个原因。
      响应报头:Location报头里包含代理的URI。
    • 306 :306 响应代码没有在HTTP标准中定义过。
    • 307 :请求还没有被处理,因为所请求的资源不在本地:它在另一个URI处。客户端应该向那个URI重新发送请求。就GET请求来说,它只是请求得到一个表示,该响应代码跟303没有区别。当服务器希望把客户端重新定向到一个镜像站点时,可以用307来响应GET请求。但对于POST,PUT及DELETE请求,它们希望服务器执行一些操作,307和303有显著区别。对POST,PUT或者DELETE请求响应303表明:操作已经成功执行,但响应实体将不随本响应一起返回,若客户端想要获取响应实体主体,它需要向另一个URI发送GET请求。而307表明:服务器尚未执行操作,客户端需要向Location报头里的那个URI重新提交整个请求。
      响应报头: 把客户端应重新请求的那个URI放在Location报头里。
      实体主体:一个包含指向新URI的链接的超文本文档。

    4XX:客户端错误
    这些响应代码表明客户端出现错误。不是认证信息有问题,就是表示格式或HTTP库本身有问题。客户端需要自行改正。

    • 400 :这是一个通用的客户端错误状态,当其他4XX响应代码不适用时,就采用400。此响应代码通常用于“服务器收到客户端通过PUT或者POST请求提交的表示,表示的格式正确,但服务器不懂它什么意思”的情况。
      实体主体:可以包含一个错误的描述文档。
    • 401 :客户端试图对一个受保护的资源进行操作,却又没有提供正确的认证证书。客户端提供了错误的证书,或者根本没有提供证书。这里的证书(credential)可以是一个用户名/密码,也可以市一个API key,或者一个认证令牌。客户端常常通过向一个URI发送请求,并查看收到401响应,以获知应该发送哪种证书,以及证书的格式。如果服务器不想让未授权的用户获知某个资源的存在,那么它可以谎报一个404而不是401。这样做的缺点是:客户端需要事先知道服务器接受哪种认证–这将导致HTTP摘要认证无法工作。
      响应报头:WWW-Authenticate报头描述服务器将接受哪种认证。
      实体主体:一个错误的描述文档。假如最终用户可通过“在网站上注册”的方式得到证书,那么应提供一个指向该注册页面的链接。
    • 402 :除了它的名字外,HTTP标准没有对该响应的其他方面作任何定义。因为目前还没有用于HTTP的微支付系统,所以它被留作将来使用。尽管如此,若存在一个用于HTTP的微支付系统,那么这些系统将首先出现在web服务领域。如果想按请求向用户收费,而且你与用户之间的关系允许这么做的话,那么或许用得上这个响应代码。
    • 403 :客户端请求的结构正确,但是服务器不想处理它。这跟证书不正确的情况不同–若证书不正确,应该发送响应代码401。该响应代码常用于一个资源只允许在特定时间段内访问,或者允许特定IP地址的用户访问的情况。403暗示了所请求的资源确实存在。跟401一样,若服务器不想透露此信息,它可以谎报一个404。既然客户端请求的结构正确,那为什么还要把本响应代码放在4XX系列(客户端错误),而不是5XX系列(服务端错误)呢?因为服务器不是根据请求的结构,而是根据请求的其他方面(比如说发出请求的时间)作出的决定的。
      实体主体:一个描述拒绝原因的文档(可选)。
    • 404 :这也许是最广为人知的HTTP响应代码了。404表明服务器无法把客户端请求的URI转换为一个资源。相比之下,410更有用一些。web服务可以通过404响应告诉客户端所请求的URI是空的,然后客户端就可以通过向该URI发送PUT请求来创建一个新资源了。但是404也有可能是用来掩饰403或者401.
    • 405 :客户端试图使用一个本资源不支持的HTTP方法。例如:一个资源只支持GET方法,但是客户端使用PUT方法访问。
      响应报头:Allow报头列出本资源支持哪些HTTP方法,例如:Allow:GET,POST
    • 406 :当客户端对表示有太多要求,以至于服务器无法提供满足要求的表示,服务器可以发送这个响应代码。例如:客户端通过Accept头指定媒体类型为application/json+hic,但是服务器只支持application/json。服务器的另一个选择是:忽略客户端挑剔的要求,返回首选表示,并把响应代码设为200。
      实体主体:一个可选表示的链接列表。
    • 407 :只有HTTP代理会发送这个响应代码。它跟401类似,唯一区别在于:这里不是无权访问web服务,而是无权访问代理。跟401一样,可能是因为客户端没有提供证书,也可能是客户端提供的证书不正确或不充分。
      请求报头:客户端通过使用Proxy-Authorization报头(而不是Authorization)把证书提供给代理。格式跟Authrization一样。
      响应报头:代理通过Proxy-Authenticate报头(而不是WWW-Authenticate)告诉客户端它接受哪种认证。格式跟WWW-Authenticate一样。
    • 408 :假如HTTP客户端与服务器建立链接后,却不发送任何请求(或从不发送表明请求结束的空白行),那么服务器最终应该发送一个408响应代码,并关闭此连接。
    • 409 :此响应代码表明:你请求的操作会导致服务器的资源处于一种不可能或不一致的状态。例如你试图修改某个用户的用户名,而修改后的用户名与其他存在的用户名冲突了。
      响应报头:若冲突是因为某个其他资源的存在而引起的,那么应该在Location报头里给出那个资源的URI。
      实体主体:一个描述冲突的文档,以便客户端可以解决冲突。
    • 410 :这个响应代码跟404类似,但它提供的有用信息更多一些。这个响应代码用于服务器知道被请求的URI过去曾指向一个资源,但该资源现在不存在了的情况。服务器不知道该资源的新URI,服务器要是知道该URI的话,它就发送响应代码301.410和310一样,都有暗示客户端不应该再请求该URI的意思,不同之处在于:410只是指出该资源不存在,但没有给出该资源的新URI。RFC2616建议“为短期的推广服务,以及属于个人但不继续在服务端运行的资源”采用410.
    • 411 :若HTTP请求包含表示,它应该把Content-Length请求报头的值设为该表示的长度(以字节为单位)。对客户端而言,有时这不太方便(例如,当表示是来自其他来源的字节流时)。
      所以HTTP并不要求客户端在每个请求中都提供Content-Length报头。但HTTP服务器可以要求客户端必须设置该报头。服务器可以中断任何没有提供Content-Length报头的请求,并要求客户端重新提交包含Content-Length报头的请求。这个响应代码就是用于中断未提供Content-Lenght报头的请求的。假如客户端提供错误的长度,或发送超过长度的表示,服务器可以中断请求并关闭链接,并返回响应代码413。
    • 412 :客户端在请求报头里指定一些前提条件,并要求服务器只有在满足一定条件的情况下才能处理本请求。若服务器不满足这些条件,就返回此响应代码。If-Unmodified-Since是一个常见的前提条件。客户端可以通过PUT请求来修改一个资源,但它要求,仅在自客户端最后一次获取该资源后该资源未被别人修改过才能执行修改操作。若没有这一前提条件,客户端可能会无意识地覆盖别人做的修改,或者导致409的产生。
      请求报头:若客户但设置了If-Match,If-None-Match或If-Unmodified-Since报头,那就有可能得到这个响应代码。If-None-Match稍微特别一些。若客户端在发送GET或HEAD请求时指定了If-None-Match,并且服务器不满足该前提条件的话,那么响应代码不是412而是304,这是实现条件HTTP GET的基础。若客户端在发送PUT,POST或DELETE请求时指定了If-None-Match,并且服务器不满足该前提条件的话,那么响应代码是412.另外,若客户端指定了If-Match或If-Unmodified-Since(无论采用什么HTTP方法),而服务器不满足该前提条件的话,响应代码也是412。
    • 413 :这个响应代码跟411类似,服务器可以用它来中断客户端的请求并关闭连接,而不需要等待请求完成。411用于客户端未指定长度的情况,而413用于客户端发送的表示太大,以至于服务器无法处理。客户端可以先做一个LBYL(look-before-you-leap)请求,以免请求被413中断。若LBYL请求获得响应代码为100,客户端再提交完整的表示。
      响应报头:如果因为服务器方面临时遇到问题(比如资源不足),而不是因为客户端方面的问题而导致中断请求的话,服务器可以把Retry-After报头的值设为一个日期或一个间隔时间,以秒为单位,以便客户端可以过段时间重试。
    • 414 :HTTP标准并没有对URI长度作出官方限制,但大部分现有的web服务器都对URI长度有一个上限,而web服务可能也一样。导致URI超长的最常见的原因是:表示数据明明是该放在实体主体里的,但客户端却把它放在了URI里。深度嵌套的数据结构也有可能引起URI过长。
    • 415 :当客户端在发送表示时采用了一种服务器无法理解的媒体类型,服务器发送此响应代码。比如说,服务器期望的是XML格如果客户端采用的媒体类型正确,但格式有问题,这时最好返回更通用的400。
    • 416 :当客户端所请求的字节范围超出表示的实际大小时,服务器发送此响应代码。例如:你请求一个表示的1-100字节,但该表示总共只用99字节大小。
      请求报头:仅当原始请求里包含Range报头时,才有可能收到此响应代码。若原始请求提供的是If-Range报头,则不会收到此响应代码。
      响应报头:服务器应当通过Content-Range报头告诉客户端表示的实际大小。
    • 417 :此响应代码跟100正好相反。当你用LBYL请求来考察服务器是否会接受你的表示时,如果服务器确认会接受你的表示,那么你将获得响应代码100,否则你将获得417。

    5XX:服务端错误
    这些响应代码表明服务器端出现错误。一般来说,这些代码意味着服务器处于不能执行客户端请求的状态,此时客户端应稍后重试。有时,服务器能够估计客户端应在多久之后重试。并把该信息放在Retry-After响应报头里。
    5XX系列响应代码在数量上不如4XX系列多,这不是因为服务器错误的几率小,而是因为没有必要如此详细–对于服务器方面的问题,客户端是无能为力的。

    • 500 :

    TCP协议如何保证可靠传输?

    通过三次握手建立连接,通讯完成时拆除连接

    TCP的握手、挥手机制?

    握手机制:
    第一次握手:客户端会向服务器端发送码为syn=1,随机产生一个seq_number=x的数据包到服务器端 (syn)
    第二次握手:服务端接受到客户端请求之后,确认ack=x+1, 于是就向客户端发送syn(服务端独立生成 随机生成数字Y)+ack
    第三次握手:客户端接受syn+ack,向服务器端发送ack=y+1,此包发送完毕即可 建立tcp连接。

    白话文翻译:
    第一次握手:客户端向服务器端发送 问服务器你在不在?
    第二次握手:服务器端回应客户端说:我在的。
    第三次握手:客户端发送给服务器端:ok,那我开始建立连接的

    挥手机制:
    第一次挥手: 客户端向服务器端发送释放的报文,停止发送数据 fin=1、生成一个序列号seq=u;
    第二次挥手: 服务器端接受到释放的报文后,发送ack=u+1;随机生成的seq=v给客户端;当前状态为关闭等待状态客户端收到了服务器确认通知之后,此时客户端就会进入到终止状态,等待服务器端发送释放报文。
    第三次挥手:服务器端最后数据发送完毕之后,就向客户端发送连接释放报文,FIN=1,ack=u+1 当前为半关闭状态,随机生成一个随机树w
    第四次挥手:客户端必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

    白话文翻译四次挥手:
    第一次挥手 客户端向服务端发送一个释放连接通知;
    第二次挥手 服务端接受到释放通知之后,告诉给客户端说等待一下,因为可能存在有其他的数据没有发送完毕,等待数据全部传输完毕之后就开始 关闭连接;
    第三次挥手 服务器端所有的数据发送完毕之后,就告诉客户端说现在可以释放连接了。
    第四次挥手: 客户端确认是最终释放连接通知,ok 就开始 就向服务区端发送我们可以开始关闭连接啦;

    TCP的粘包/拆包原因及其解决方法是什么?

    什么是粘包:多次发送的消息,客户端一次合并读取 msg+msg
    什么是拆包:第一次完整消息+第二次部分消息组合 、第二次缺失的消息

    粘包/拆包原因:
    1、要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。
    2、待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。
    3、要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘 包。
    4、接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

    粘包、拆包解决办法:
    1、发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后, 通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
    2、发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区 中读取固定长度的数据就自然而然的把每个数据包拆分开来。
    3、可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包 拆分开。

    同步与异步、阻塞与非阻塞的区别?

    同步也就是程序从上往下实现执行;异步从新开启一个新分支,相互不会影响;
    阻塞式:当我们没有获取到数据的时候,整个应用程序会实现阻塞等待,不能实现做其他的事情。
    非阻塞式:不管是否有获取到数据,都必须立马获取到结果,如果没有获取数据的情况下,就会不断的重试获取数据

    说说网络IO模型?

    网络IO的本质是socket的读取,socket在linux系统被抽象为流,IO可以理解为对流的操作;
    IO其实我们并不陌生,站在操作系统的角度上说,io一般指访问磁盘数据,可以分为两步,以read操作举例的话:
    第一阶段:等待数据准备 (Waiting for the data to be ready)。
    第二阶段:将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)。

    而网络IO也是如此,只不过它是读取的不是磁盘,而是socket:
    1.第一步:通常涉及等待网络上的数据分组到达,然后被复制到内核的某个缓冲区。
    2.第二步:把数据从内核缓冲区复制到应用进程缓冲区。

    BIO模型存在哪些缺点?

    一个连接占用一个线程资源,并且线程资源得不到充分利用。线程开销大、利用率也不高。

    阻塞IO(BIO)和非阻塞IO(NIO)的区别?

    在这里插入图片描述

    传统的bio(同步阻塞 )是面向与流传输的,而NIO(同步非阻塞io)是面向与缓冲区非阻塞式的io,其中最大的亮点就是多路io复用机制。

    使用多线程实现异步io存在哪些优缺点?

    优点:可是适当支持并发请求访问;
    缺点:非常消耗服务器资源;

    Linux五种io模型

    Linux下的五种I/O模型

    1. 阻塞I/O(blocking I/O)
    2. 非阻塞I/O (nonblocking I/O)
    3. I/O复用(select 和poll) (I/O multiplexing)
    4. 信号驱动I/O (signal driven I/O (SIGIO))
    5. 异步I/O (asynchronous I/O (the POSIX aio_functions))

    前面四种都是同步io、第五种是异步IO;

    什么是Bio阻塞式io介绍?

    当我们在调用一个io函数的时候,如果没有获取到数据的情况下,那么就会一直等待;等待的过程中会导致整个应用程序一直是一个阻塞的过程,无法去做其他的实现。

    在这里插入图片描述

    Nio技术多路IO复用底层实现原理

    通道(Channel)
    通常我们nio所有的操作都是通过通道开始的,所有的通道都会注册到统一个选择器(Selector)上实现管理,在通过选择器将数据统一写入到 buffer中。

    缓冲区(Buffer)
    Buffer本质上就是一块内存区,可以用来读取数据,也就先将数据写入到缓冲区中、在统一的写入到硬盘上。

    选择器(Selector)
    Selector可以称做为选择器,也可以把它叫做多路复用器,可以在单线程的情况下可以去维护多个Channel,也可以去维护多个连接;

    实现原理:
    linux提供select/poll,进程通过将一个或多个fd传递给select或poll系统调用,select/poll会不断轮询所负责的所有socket,可以侦测许多fd是否就绪,但select和poll是顺序扫描fd是否就绪,并且支持的fd数量有限。linux还提供了epoll系统调用,它是基于事件驱动的方式,而不是顺序扫描,当某个socket有数据到达了,可以直接通知用户进程,而不需要顺序轮询扫描,提高了效率。
    在这里插入图片描述

    当进程调用了select,整个进程会被block,同时,kernel会监视所有select负责的socket,当任何一个socket的数据准备好了,select就会返回,这个图和阻塞IO的图其实并没有多大区别,事实上,还更差一点,因为这里需要使用两个System call,select和recvFrom,而blocking io只调用了一个system call(recvfrom),但是select的好处在与它可以同时处理多个connection,(如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)

    Redis为什么单线程能够支持高并发

    Redis的底层采用Nio中的多路IO复用的机制,能够非常好的支持这样的并发,从而保证线程安全问题;
    但是Nio在不同的操作系统上实现的方式有所不同,在我们windows操作系统使用select实现轮训时间复杂度是为o(n),而且还存在空轮训的情况,效率非常低, 其次是默认对我们轮训的数据有一定限制,所以支持上万的tcp连接是非常难。
    所以在linux操作系统采用epoll实现事件驱动回调,不会存在空轮训的情况,只对活跃的 socket连接实现主动回调这样在性能上有大大的提升,所以时间复杂度是为o(1)

    注意:windows操作系统是没有epoll,只有linux系统才有epoll

    NIO选择器、缓冲区分别核心的作用

    缓冲区(Buffer)
    Buffer本质上就是一块内存区,可以用来读取数据,也就先将数据写入到缓冲区中、在统一的写入到硬盘上。

    选择器(Selector)
    Selector可以称做为选择器,也可以把它叫做多路复用器,可以在单线程的情况下可以去维护多个Channel,也可以去维护多个连接;

    BIO、NIO、AIO分别是什么?

    BIO:
    同步阻塞,服务器实现模式是一个连接一个线程,当客户端发来连接时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情就会造成不必要的线程开销,当然线程池机制可以改善。

    NIO:
    同步非阻塞,服务器实现模式为多个请求一个线程,即客户端发来的请求都会注册到多路复用器上,多路复用器轮训的连接有io请求时才开启一个线程进行处理。

    AIO:
    异步非阻塞,服务器实现模式为多个有效请求一个线程。即客户端发来的请求由os处理完成才会通知服务器应用启动线程进行处理。

    select、poll、epoll的机制及其区别?

    (1)select==>时间复杂度O(n)
    select()的机制中提供一种fd_set的数据结构,实际上是一个long类型的数组,每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读。

    从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视 socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

    select机制的问题

    • 每次调用select,都需要把fd_set集合从用户态拷贝到内核态,如果fd_set集合很大时,那这个开销也很大
    • 同时每次调用select都需要在内核遍历传递进来的所有fd_set,如果fd_set集合很大时,那这个开销也很大
    • 为了减少数据拷贝带来的性能损坏,内核对被监控的fd_set集合大小做了限制,并且这个是通过宏控制的,大小不可改变(限制为1024)

    (2)poll==>时间复杂度O(n)
    poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述 符的状态进行处理,但是poll没有最大文件描述符数量的限制。也就是说,poll只解决了上面的问题3,并没有解决问题1,2的性能开销问题;
    poll改变了文件描述符集合的描述方式,使用了pollfd结构而不是select的fd_set结构,使得poll支持 的文件描述符集合限制远大于select的1024;

    (3)epoll==>时间复杂度O(1)
    epoll在Linux2.6内核正式提出,是基于事件驱动的I/O方式,相对于select来说,epoll没有描述符个数限制,使用一个文件描述符管理多个描述符,将用户关心的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

    select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

    epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现

    什么是序列化?反序列化

    序列化:把对象转换成字节的过程,称为对象序列化;
    反序列化:把字节恢复成对象的过程,称为反序列化;

    序列化有哪些实现方式?

    • Java实现序列化:序列化的类必须要实现Serializable接口 transient修饰变量不被序列化;
    • 将对象转换成json
    • 用XML格式序列化
    • ProtoBuf(谷歌第三方协议)

    网络传输序列化协议有哪些?

    xml、json、fastjson、thrift、avro、protobuf、

    MessagePack编码器与json有哪些区别?

    它像 JSON,但是更快更小。
    MessagePack 是一种高效的二进制序列化格式。它允许您在 JSON 等多种语言之间交换数据,但它更快速更小巧。小整数被编码为单个字节,典型的短字符串除了字符串本身之外只需要一个额外的字节。

    网络传输对象为什么需要序列化?

    网络传输只能传输二进制文件,客户端将对象序列化为字节(序列化),变成二进制的形式发送到服务器端端,服务器端接受到字节对象后,反序列化成对象

    Netty 是什么?

    Netty是业界最流行的NIO框架之一,它的健壮性、功能、性能、可定制性和可扩展性在同类框架中都是首屈一指的,它已经得到成百上千的商用项目验证,例如Hadoop的RPC框架Avro就使用了Netty作为底层通信框架,其他还有业界主流的RPC框架,也使用Netty来构建高性能的异步通信能力。

    Netty跟Java NIO有什么不同,为什么不直接使用JDK NIO类库?

    • NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
    • 需要具备其他的额外技能做铺垫,例如熟悉Java多线程编程。这是因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序。
    • 可靠性能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等问题,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐的工作量和难度都非常大。
    • JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该BUG发生概率降低了一些而已,它并没有被根本解决。

    Netty组件有哪些,分别有什么关联?

    • NioEventLoop:netty的核心组件,netty运行在NioEventLoop上
    • Channel:netty自定义的Channel,是对nio中的Channel的进一步封装
    • Unsafe:用于实现每一种Channel的读写抽象,实现类一般为各类型Channel中的内部类
    • Pipeline:ChannelHandler逻辑链,进行事件的传播
    • ChannelHandler:Pileline逻辑链的基本构成单位,事件处理组件。我们可以创建多个自定义的ChannelHandler对象添加到Pipeline逻辑链,从而介入Channel的数据处理流程中去。我们的业务代码写在这里面
    • ByteBuf:数据传输容器

    EventloopGroup 了解么?和 EventLoop 啥关系?

    EventLoop和EventLoopGroup描述了Netty程序运行所使用的线程模型,通过修改EventLoopGroup,可以让Netty在不同的线程模型间快速切换,甚至可以在Nio和Bio间一键切换。

    EventLoopGroup直译过来叫「事件循环组」,它管理着一组EventLoop。
    EventLoopGroup实现了Iterable接口,可以通过迭代器遍历它管理的EventLoop。

    Bootstrap 和 ServerBootstrap 了解么?

    ServerBootStrap是Netty服务端启动配置类,BootStrap是Netty客户端启动配置类。

    Bootstrap 、ServerBootstrap 是客户端和服务器端的启动引导类,通常ServerBootstrap 通常使⽤ bind()⽅法绑定本地的端⼝上,然后等待客户端的连接。

    Bootstrap 只需要配置⼀个线程组 EventLoopGroup ,⽽ ServerBootstrap 需要配置两个线

    程组EventLoopGroup ,⼀个⽤于接收连接(bossGroup),⼀个⽤于具体的处理(workerGroup)。

    Bootstrap和 ServerBootstrap的使用步骤大致包括设置信道,确定线程模型,添加ChannelInitializer ,绑定端口,设置一些参数等。

    NioEventLoopGroup 默认的构造函数会起多少线程?

    NioEventLoopGroup 默认的构造函数实际会起的线程数为 CPU*2

    Netty 线程模型了解么?

    设计模式:Reactor 模式

    从⼀个 主线程 NIO 线程池中选择⼀个线程作为 Acceptor 线程,绑定监听端⼝,接收客户端连接的连接,其他线程负责后续的接⼊认证等⼯作。连接建⽴完成后,从属 NIO 线程池负责具体处理I/O 读写。

    BossGroup负责 accept, 连接后交由 WorkerGroup负责读写处理等。
    BossGroup监听客户端的连接,WorkerGroup监听读写事件。

    Netty 服务端和客户端的启动过程了解么?

    Netty 长连接、心跳机制了解么?

    Netty长连接和http长连接都是基于TCP长连接的。

    长连接应用场景

    1. 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三次握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,再次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

    2. 而像WEB网站的http服务一般都用短连接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连接好。

    心跳机制
    在 TCP 保持⻓连接的过程中,可能会出现断⽹等⽹络异常出现,异常发⽣的时候, client 与server 之间如果没有交互的话,它们是⽆法发现对⽅已经掉线的。为了解决这个问题, 我们就需要引⼊心跳机制 。

    ⼼跳机制的⼯作原理是: 在 client 与 server 之间在⼀定时间内没有数据交互时, 即处于 idle 状态时, 客户端或服务器就会发送⼀个特殊的数据包给对⽅, 当接收⽅收到这个数据报⽂后, 也⽴即发送⼀个特殊的数据报⽂, 回应发送⽅, 此即⼀个 PING-PONG 交互。所以, 当某⼀端收到⼼跳消息后, 就知道了对⽅仍然在线, 这就确保 TCP 连接的有效性。

    简单的心跳机制实现:
    服务器监听信道,如果5s内没有客户端发过来的包,就认为客户端挂掉了,释放该信道。
    客户端和服务器建立连接后,在空闲时间(没有读写请求)每隔4s向服务器发送一个数据包,证明自己还“活着”。

    说说Netty的执行流程?

    在这里插入图片描述

    1. Netty 抽象出两组线程池 ,BossGroup 专门负责接收客户端的连接,WorkerGroup 专门负责网络的读写
    2. BossGroup 和 WorkerGroup 类型都是 NioEventLoopGroup
    3. NioEventLoopGroup 相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环是 NioEventLoop
    4. NioEventLoop 表示一个不断循环的执行处理任务的线程(selector监听绑定事件是否发生),每个 NioEventLoop 都有一个 Selector,用于监听绑定在其上的 socket 的网络通讯,比如NioServerSocketChannel绑定在服务器boosgroup的NioEventLoop的selector上,NioSocketChannel绑定在客户端的NioEventLoop的selector上,然后各自的selector就不断循环监听相关事件。
    5. NioEventLoopGroup 可以有多个线程,即可以含有多个 NioEventLoop
    6. 每个 BossGroup下面的NioEventLoop 循环执行的步骤有 3 步
      1.轮询 accept 事件
      2.处理 accept 事件,与 client 建立连接,生成 NioScocketChannel,并将其注册到某个 workerGroup NIOEventLoop 上的 Selector
      3.继续处理任务队列的任务,即 runAllTasks
    7. 每个 WorkerGroup NIOEventLoop 循环执行的步骤
      1.轮询 read,write 事件
      2.处理 I/O 事件,即 read,write 事件,在对应 NioScocketChannel 处理
      3.处理任务队列的任务,即 runAllTasks
    8. 每个 Worker NIOEventLoop 处理业务时,会使用 pipeline(管道),pipeline 中包含了 channel(通道),即通过 pipeline 可以获取到对应通道,管道中维护了很多的处理器。
    9. NioEventLoop 内部采用串行化设计,从消息的 读取->解码->处理->编码->发送,始终由 IO 线程 NioEventLoop 负责
    10. NioEventLoopGroup 下包含多个 NioEventLoop
      每个 NioEventLoop 中包含有一个 Selector,一个 taskQueue
      每个 NioEventLoop 的 Selector 上可以注册监听多个 NioChannel
      每个 NioChannel 只会绑定在唯一的 NioEventLoop 上
      每个 NioChannel 都绑定有一个自己的 ChannelPipeline
      NioChannel可以获取对应的ChannelPipeline,ChannelPipeline也可以获取对应的NioChannel。

    Netty高性能体现在哪些方面?

    1. IO线程模型 :同步非阻塞,用最少的资源做更多的事情,即IO多路复用。
    2. 内存零拷贝 :尽量减少不必要的内存拷贝,实现了更高效率的传输。
    3. 内存池设计 :申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。
    4. 串行化处理读写 :避免使用锁带来的性能开销。即消息的处理尽可能再同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整NIO线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队里-多个工作线程模型性能更优。
    5. 高性能序列化协议 :支持protobuf等高性能序列化协议。
    6. 高效并发编程的体现 :volatile的大量、正确使用;CAS和原子类的广泛使用;线程安全容器的使用;通过读写锁提升并发性能。

    39、Netty的线程模型是怎么样的?
    答:
    40、Netty的零拷贝提体现在哪里,与操作系统上的有什么区别?
    答:
    41、Netty的内存池是怎么实现的?
    答:
    42、Netty的对象池是怎么实现的?
    答:
    43、在实际项目中,你们是怎么使用Netty的?
    答:
    44、使用过Netty遇到过什么问题?
    答:
    45、完成手写一个Dubbo的思路
    答:
    46、手写RPC负载均衡器算法
    答:

  • 相关阅读:
    视频融合技术平台解决方案
    【目标检测】【边界框回归】Bounding-Box regression
    手把手带你刷好题(牛客刷题②)
    软件工程毕业设计课题(30)基于JAVA毕业设计JAVA助农商城销售平台系统毕设作品项目
    MySQL如何在不知道密码的情况下知道并修改密码
    容器化:MongoDB
    国内外接口文档工具哪家强?
    皕杰报表之语义层
    Spring Security根据角色在登录后重定向用户
    Abnova神经生物学研究:EFNB2 DNAxPab
  • 原文地址:https://blog.csdn.net/weixin_44044929/article/details/126274614