当前位置: 首页 > news >正文

百度网站推广关键词怎么查标识设计是什么

百度网站推广关键词怎么查,标识设计是什么,wordpress 全站pjax,专门做医疗器械的网站目录 前言前置知识一、计算机网络体系结构二、TCP/IP协议族2.1 简介*2.2 TCP/IP网络传输中的数据2.3 地址和端口号2.4 小总结 三、TCP/UDP特性3.1 TCP特性TCP 3次握手TCP 4次挥手TCP头部结构体 3.2 UDP特性 四、总结 课程内容一、网络通信编程基础知识1.1 什么是Socket1.2 长连… 目录 前言前置知识一、计算机网络体系结构二、TCP/IP协议族2.1 简介*2.2 TCP/IP网络传输中的数据2.3 地址和端口号2.4 小总结 三、TCP/UDP特性3.1 TCP特性TCP 3次握手TCP 4次挥手TCP头部结构体 3.2 UDP特性 四、总结 课程内容一、网络通信编程基础知识1.1 什么是Socket1.2 长连接、短连接1.3 网络编程与生活常识类比 二、BIO2.1 BIO简介2.2 BIO结合多线程1普通线程2.3 BIO结合多线程2线程池*2.4 小结 三、NIO3.1 NIO简介3.2 与BIO的主要区别3.3 Java NIO没引入多路复用器之前3.4 Java NIO 多路复用 *四、拓展epoll简介 学习总结感谢 前言 我是有点怕网络编程的总有点【谈网色变】的感觉。为了让自己不再【谈网色变】所以我想过系统学习一下然后再做个笔记这样加深一下理解。但是真要系统学习其实还是要花费不少时间的所以这里也只是简单的尽可能地覆盖一下梳理一些我认为比较迫切需要了解的网络编程相关的知识。 其实单单网络编程跟TCP/IP协议族就可以拿来写一个独立的笔记了但是为了让知识点更耦合一点我还是决定写在一起。当然这样也有坏处那就是知识点比较密集然后导致笔记篇幅偏长所以阅读的朋友给点耐心。 另外为了让知识分层更加清晰一点基础网络编程跟TCP/IP那一块我还是放在【前置知识】里面。 前置知识 一、计算机网络体系结构 OSI七层网络模型 我相信大家对OSI七层模型都不陌生OSI全称开放系统互连参考模型是国际标准化组织ISO和国际电报电话咨询委员会CCITT联合制定的开放系统互连参考模型为开放式互连信息系统提供了一种功能结构的框架。其目的是为异种计算机互连提供一个共同的基础和标准框架并为保持相关标准的一致性和兼容性提供共同的参考。这里所说的开放系统实质上指的是遵循 OSI 参考模型和相关协议能够实现互连的具有各种应用目的的计算机系统。PS这是一个标准的参考模型 它的整体模型图如下 面试中可能比较常考这一点建议死记硬背。个人背诵方式是记每一层首字。即应表会传网数物、应表会传网数物、应表会传网数物。刚开始有点拗口多念几遍就记住了。 TCP/IP模型 严格来说OSI模型是一种比较复杂且学术化的【参考】模型实际上我们说的互联网中实际使用的是TCP/IP模型这是由工程师定义的。这个也是我们后面要着重讲的内容。 TCP/IP模型在网上又叫五层模型。它跟7层网络模型其实差不多就是把7层的前3层合在一起表示了。模型图如下 PS网上也有一些人把最后2层【数据链路层】和【物理层】合在一起叫做【网络接口层】最后结论为TCP/IP为4层网络模型。大家知道有这个东西就好 小总结 其实无论什么模型每一层的定义都是建立在低一层提供的服务上的并且为高一层提供服务。大致来说我们可以这么映射这个网络模型 物理层 —对应— 网卡、路由器、交换机 数据链路层 —对应— 网卡驱动 网络层、传输层 —对应— 这一部分已经被操作系统封装了 应用层 —对应— 一些通用的网络应用程序或者自己编写的应用程序 另外我相信通过对比大家也看到了OSI七层模型跟TCP/IP五层模型没啥特别大区别的记住其中一个模型就差不多了。而且在编程过程中其实我们通常只需要关注【应用层】就好了下面几层的封装其实操作系统早就帮我们封装好了。它就是Socket。 二、TCP/IP协议族 2.1 简介 TCP/IP协议族我希望大家将他拆分成三个部分来理解。 TCP/IPTransmission Control Protocol/Internet Protocol 的简写中译名为【传输控制协议/因特网互联协议】是 Internet 最基本的协议、Internet 国际互联网络的基础。由网络层的 IP 协议和传输层的 TCP 协议组成。协议采用了 5 层的层级结构。然而在很多情况下它是利用 IP 进行通信时所必须用到的协议群的统称也就是说它其实是个协议家族由很多个协议组成并且是在不同的层是互联网的基础通信架构协议协议是经过谈判、协商而制定的共同承认、共同遵守的文件。就可以认为是一种规范、标准族说明每一层都含有多个的意思 下图是TCP/IP模型以及对应的常见协议族 横向对比图 OSI参考模型TCP/IP模型TCP/IP协议族应用层应用层DNS、HTTP、FTP、Mysql、WebSocket表示层会话层传输层传输层TCP、UDP网络层网络层IP、ICMP、RIP、IGMP数据链路层数据链路层ARP、RARP、IEEE802.3、CSMA/CD物理层物理层FE自协商、Manchester、MLT-3、4A、PAM5 *2.2 TCP/IP网络传输中的数据 每个分层中都会对所发送的数据附加一个首部在这个首部中包含了该层必要的信息如发送的目标地址以及协议相关信息或者我们可以理解为数据再每个分层流转的时候都会包裹一层根据协议标准附带上当前层的一些关键数据这就是编程语言角度【协议】的本质。通常为协议提供的信息为包首部所要发送的内容为数据。在下一层的角度看从上一层收到的包全部都被认为是本层的数据。 网络中传输的数据包由两部分组成【协议首部】【上一层传过来的数据】。协议首部结构由协议的具体规范详细定义。在数据包的首部明确标明了协议应该如何读取数据。反过来说看到首部也就能够了解该协议必要的信息以及所要处理的数据。 举个简单的例子。我们用用户 A 发送用户 B 接受来说说明 1用户A使用某应用程序在【应用层】处理。首先应用程序会根据自定义编码处理产生的消息比如最简单的json序列化然后交给下面的【传输层】TCP协议处理 2用户A的【传输层】TCP处理。TCP根据应用的指示负责建立连接、发送数据以及断开连接。TCP 提供将应用层发来的数据顺利发送至对端的可靠传输。为了实现这一功能需要将【应用层数据】封装为【报文段segment】并附加一个 TCP 首部然后交给下面的【网络层】IP协议处理 3用户A的【网络层】IP模块的处理。IP 将 TCP 传过来的 TCP 首部和 TCP 数据合起来当做自己的数据并在 TCP 首部的前端加上自己的 IP 首部生成 IP 数据报datagram然后交给下面的【数据链路层】 4用户A【数据链路层】处理。从 IP 传过来的 IP 包对于数据链路层来说就是数据。给这些数据附加上数据链路层首部封装为链路层帧frame生成的链路层帧frame将通过物理层传输给接收端 5用户B【数据链路层】的处理。用户 B 主机收到链路层帧frame后首先从链路层帧frame首部找到 MAC 地址判断 是否为发送给自己的包若不是则丢弃数据如果是发送给自己的包则从以太网包首部中的类型确定数据类型再传给相应的模块如IP、ARP 等。这里的例子则是 IP 6用户B【网络层】IP 模块的处理。IP 模块接收到 数据后也做类似的处理。从包首部中判断此 IP 地址是否与自己的 IP 地址匹配如果匹配则根据首部的协议类型将数据发送给对应的模块如 TCP、UDP。这里的例子则是 TCP 7用户B【传输层】TCP 模块的处理。在 TCP 模块中首先会计算一下校验和判断数据是否被破坏。然后检查是否在按照序号接收数据请记住这个步骤后面推断三次握手要用到。最后检查端口号确定具体的应用程序。数据被完整地接收以后会传给由端口号识别的应用程序 8用户B【应用层】应用程序的处理。接收端应用程序会直接接收发送端发送的数据。通过解析数据展示相应的内容。 2.3 地址和端口号 在我们网络传输中有两个概念我们会经常接触就是所谓的地址跟端口号。地址地址我估计大部分人想到的是IP地址却忽略了Mac地址。可是有人知道为什么计算机里面要设计【Mac地址 IP地址 端口号】吗 我们知道IP地址通常是用来唯一标识一台机器的但我们同样应该知道不同内网中IP地址是可以重复的那我怎么知道这个IP地址就是这台机器呢IP地址又是如何而来的呢对的就是Mac地址。 Mac地址 MAC 地址全称叫做媒体访问控制地址也称为局域网地址LAN AddressMAC 位址以太网地址Ethernet Address或物理地址Physical Address它是由网络设备制造商在生产时就写在硬件内部的但是这不意味着就不能更改只是比较麻烦一般人也不会去改。它作用于【数据链路层】上 MAC 地址共 48 位6 个字节。前 24 位由 IEEE电气和电子工程师协会决定如何分配后 24 位由实际生产该网络设备的厂商自行制定。例如FF:FF:FF:FF:FF:FF 或 FF-FF-FF-FF-FF-FF 比如下面是我的电脑Mac地址信息打开cmd输入ipconfig IP地址 IP地址是啥不用过多介绍了。IP 地址分为IPv4 和 IPv6我们通常说的是IPv4。IP 地址是由 32 位的二进制数组成它们通常被分为 4 个【8 位二进制数】我们可以把它理解为 4 个字节格式表示为A.B.C.D。其中ABCD 这四个英文字母表示为 0-2552^8 - 1的十进制的整数。例如192.168.1.1。它作用于【网络层】上 那IP地址跟Mac地址有什么区别和联系呢 1对于网络中的一些设备路由器或者是 PC 及而言 IP 地址的设计是出于拓扑设计出来的只要在不重复 IP 地址的情况下它是可以随意更改的而 MAC 地址是根据生产厂商烧录好的它一般不能改动的一般来说当一台 PC 机的网卡坏了之后更换了网卡之后 MAC 地址就会变了 2在前面的介绍里面它们最明显的区别就是长度不同 IP 地址的长度为 32 位而Mac地址为 48 位 3它们的寻址协议层不同。IP 地址应用于 OSI 模型的【网络层】而Mac地址应用在OSI的【数据链路层】。 数据链路层协议可以使数据从一个节点传递到相同链路的另一个节点上通过 MAC 地址而网络层协议使数据可以从一个网络传递到另一个网络上 ARP 根据目的 IP 地址找到中间节点的 MAC 地址通过中间节点传送从而最终到达目的网络 4分配依据不同。 IP 地址的分配是基于我们自身定义的网络拓扑 MAC 地址的分配是基于制造商 端口号 端口号的存在不难理解毕竟无论是Mac地址还是IP地址都只能标识一台机器那怎么标识机器上特定应用程序呢就是端口号所以端口号又可以称为程序地址。它作用于【传输层】上 一台计算机上同时可以运行多个程序。传输层协议正是利用这些端口号识别本机中正在进行通信的应用程序并准确地将数据传输。 2.4 小总结 1TCP/IP每一层都有自己的协议甚至有多种 2协议是一种规范、约束。用Java WebMVC来说这种规范就是前后端约定了每一次交互必传手机号一样 3TCP/IP每个分层中都会对所发送的数据附加一个首部在这个首部中包含了该层必要的信息其实就是【协议】的体现 4在下一层的角度看从上一层收到的包全部都被认为是本层的数据 Q1为什么端口号只有65535个 答因为在TCP、UDP协议报文的定义中会分别有16位二进制合计32位4字节长度来存储元端口号和目的端口号所以端口个数只能是2^1665536个。通常0号端口用来表示所有端口所以实际可以用的端口号有65535个。详细的TCP、UPD头部结构体定义见下面的笔记内容【TCP头部结构体】 Q2客户端的端口号是多少如何确定 答是的我们通过客户端连接服务端的时候也需要端口号的这一点很容易被大家忽略。然后客户端的端口号虽然可以指定但是通常不指定因为没什么必要。所以都是由操作系统给自动分配的。并且分配的范围通常都是大于100000-1023通常被知名服务占用了比如FTP端口是21HTTP是80 三、TCP/UDP特性 在讲述TCP/UDP特性之前我们先给大家声明一个概念。那就是网络通信中的客户端和服务端。声明如下 客户端主动发起连接的一方为客户端服务端接收连接请求的一方为服务端 【你】向【我】发起连接肯定是因为想从【我】这里获得什么所以【你】是客户端【我】是服务端服务提供方这很合理。其实这才是广义上的客户端、服务端定义。请大家不要狭隘地跟Web开发中的客户端、服务端混淆。 3.1 TCP特性 TCPTransmission Control Protocol是面向连接的【全双工】通信协议通过三次握手建立连接然后才能开始数据的读写通讯完成时要拆除连接由于 TCP 是面向连接的所以只能用于端到端的通讯。 我们知道TCP相对于UDP最大的特点就是【可靠性】。那么为了实现【可靠性】TCP做了什么呢整体来说大概是2点【超时重传】和【消息应答确认机制】。那大家有思考过这两点是如何被实现的吗其实这个实现方式也并不是特别重要不过说到这个东西我突然间意识到一些东西。言归正传。 全双工即允许数据在两个方向上传输 半双工虽然数据也能在两个方向上传输但是一次只能一个方向流动 单工只允许数据从一个方向上传输 超时重传 超时重传机制中最最重要的就是重传超时RTORetransmission TimeOut的时间选择如何确定由于网络存在波动所以用一个固定的时间来测算是一种不经济的方式TCP通过引入一个叫做RTTround-trip time的定义根据最近的发包、收包时间动态测算、修改重试时间 消息应答机制 顾名思义就是当收到消息后给消息一个应答告诉对方我收到了。而对于没收到消息应答的一方来说一段时间后没收到应答就【超时重传】。那在复杂的网络通信中我可能每秒钟都有好多条消息交互我怎么知道这一条答复是对应哪一条请求呢对的序号这个序号也将在TCP三次握手中体现出来现在回过头来看RocketMQ的事务可能就是参照这个设计的。当然可能也不是毕竟这个设计思路很多地方都有用过 TCP 3次握手 TCP 提供面向有连接的通信传输。【面向有连接】是指在数据通信开始之前先做好两端之间的准备工作。 所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在 socket 编程中这一过程由客户端执行 connect 来触发所以网络通信中发起连接的一方我们称为客户端接收连接的一方我们称之为服务端。 下面是三次握手的流程图 流程解读 1第一次握手客户端将请求报文标志位 SYN 置为 1告诉服务端此时是在同步请求报文的 seqSequence Number字段中填入一个随机值 J表示此时是同步到第几条消息这个很重要很重要很重要并将该数据包发送给服务器端等待服务端的应答确认紧接着客户端进入SYN_SENT状态SYN即同步的意思 2第二次握手服务器端收到数据包后由请求报文标志位SYN1知道客户端请求建立连接服务器端将应答报文标志位 SYN 和 ACK 都置为1ACK1表示此条应答是针对客户端同步的应答SYN1则表示我也想跟你请求同步了【毕竟全双工啊同学们】双方都交换seq应答报文的 ackAcknowledgment Number字段中填入 ackJ1应答报文的 seq 中填入一个随机值 K同上面的J一样道理并将该数据包发送给客户端以确认连接请求服务器端进入 SYN_RCVD 状态 3第三次握手客户端收到应答报文后检查 ack 是否为 J1ACK 是否为 1如果正确则将第三个报文标志位 ACK 置为 1ackK1并将该数据包发送给服务器端服务器端检查 ack 是否为 K1ACK 是否为 1如果正确则连接建立成功客户端和服务器端进入ESTABLISHED 状态完成三次握手随后客户端与服务器端之间可以开始传输数据了 TCP三次握手大家都听过但是根据我的经验大部分人都【谈网色变】甚至搞不清楚三次握手传的参数是什么意思。所以这边再来个【小总结】希望大家别看了流程就算了。如下 三次握手中的SYN标志是同步的意思。SYN1表示我要跟你同步了 Q1为什么要同步同步什么 答主要是同步序号seq为什么要这个同步这个你看看【消息应答机制】提到的在复杂的网络通信中如果没有序号如何判断我这条应答对应的是你这条消息没有序号我怎么知道哪条消息已经接收到了哪些没有收到哪些消息又没有重复发送 Q2为什么三次握手而不是四次握手甚至更多次 答因为三次就够了已经完成原始数据同步了后面再来就没意义了。 三次握手的本质即是通信双方相互告知序列号起始值并确认对方已经收到了序列号起始值因为是TCP是全双工所以双方都要建立请求连接。这个在Java中的体现就是两边都有一个socket对象如果你跟我一样是死记硬背的选手请记住出现在图中的每一个字段的意思包括客户端、服务端在握手过程中状态的演变 TCP 4次挥手 其实这个相对来说没那么重要就算是面试也很少考这个但终归还是要了解一下的。 四次挥手即终止 TCP 连接就是指断开一个 TCP 连接时需要客户端和服务端总共发送 4 个包以确认连接的断开。在 socket 编程中这一过程由客户端或服务端任一方执行 close来触发。 跟握手一样由于TCP是全双工的因此每个方向都必须要单独进行关闭。首先进行关闭的一方将执行主动关闭而另一方则执行被动关闭。连接都是双方都请求连接关闭当然是两边都要关闭 流程图如下 我相信你们如果完整看过了【TCP3次握手】的话这边的流程图难不倒你。FIN即finish嘛。老样子注意状态变化哦。这里可能比较男的地方就是最后一次【挥手】的时候为什么要进入TIME-WAIT状态了。哎呀都是因为【全双工】啊等待一下确保那边也关了比较好另外也确保迟到的消息报文能被当前应用程序正确的识别并且丢弃。 Q1【被当前应用程序正确的识别并且丢弃】怎么理解 答我们前面介绍端口的时候说过客户端端口号可以不指定由系统自动分配的所以很可能你刚关闭就被新的其他应用程序客户端占取了这个端口结果就是如果没有这个TIME-WAIT状态就恰好再关闭之前有来自其他端的发送给上一个占用该端口的应用程序。啊现在这个应用程序就懵了咋回事啊这条消息完全“看不懂”啥意思啊 TCP头部结构体 为什么要介绍这个结构体 首先经过了【2.2 TCP/IP网络传输中的数据】的描述你应该知道在网络通信中每个分层都会裹上一层属于自己的数据各种协议规定的这就是编程语言角度【网络协议】的本质。 所以TCP在传输层中给出的【协议】即如下所示 这个图是不是很难看懂给大家一段C语言伪代码一般人都看得懂啥意思。 // TCP头首部信息 struct TCPHead {int32 port; // 端口号4字节int32 seq; // 序号4字节int32 ack; // 确认号4字节int32 offset; // 偏移数据4字节int32 crc; // 校验4字节byte[] data; // 协议数据边长 } 这里给出这该结构体的定义跟模型图也是为了方便大家去放飞思想想象一下其他各层的结构体以及又会裹上什么信息呢。 再然后通过一个简单的面试问题把之前的知识整合起来 Q1一台主机上只能保持最多 65535 个 TCP 连接对吗 答肯定是不对的。首先出于【传输层】的TCP协议肯定是包含了【网络层】IP层数据的如果你网络赶紧回去看看【2.2】。的虽然我没有贴出来【网络层】IP协议的头部数据但是你多多少少会知道肯定需要【源IP地址】跟【目标IP】地址才对。 所以定义一个【唯一TCP连接】的要素如下【源 IP 地址】【目标 IP 地址】【协议号协议类型】【源端口号】【目标端口号】 大伙看看上图不难看出当【源IP地址】跟【源端口号】变化时哪怕【目标IP地址】跟【目标端口号】【协议类型TCP固定为TCPUDP固定为UPD】固定TCP连接数量也会由于前两者的变化而变化。 3.2 UDP特性 UDPUser Datagram Protocol中文名是用户数据报协议。是把数据直接发出去而不管对方是不是在接收也不管对方是否能接收也不需要接收方确认属于不可靠的传输可能会出现丢包现象实际应用中要求程序员自己选择、比较使用。 UDP还有2个特性称为单播和广播。 单播和广播 单播的传输模式定义为发送消息给一个由唯一的地址所标识的单一的网络目的地。面向连接的协议和无连接协议都支持这种模式。由于通讯不需要连接所以可以实现广播发送所谓广播——传输到网络或者子网上的所有主机。 我相信大部分初次接触UDP的朋友可能会觉得UDP这么不可靠使用应该不多吧哈相反UDP因为没那么多复杂的机制反而深受大佬们喜爱甚至可以说很多大佬们会优先选择UDP除非迫不得已真的需要【可靠性】的支持才使用TCP。 四、总结 OK【前置知识】中关于网络编程的一些基础知识终于是捋完了。怎么说呢因为我也没看过更专业、更全的【网络编程基础】相关资料书记所以我也不好说我说的是否都是对的更遑论是否全面。我只是根据我一直以来学习网络编程以来遇到的问题。不过话说回来我遇到的这些问题有时候会受限于个人的眼界与技术功底。 共勉 课程内容 正文开始 一、网络通信编程基础知识 1.1 什么是Socket 我在前置知识中稍微提过一嘴由于Socket的存在直接帮我们封装了TCP/IP模型中的后4层我们只需要关注【应用层】就好了。甚至【应用层】也有很多成熟的产品了我们往往只需要关注如何写业务以及如何把它们应用到我们的代码中。言归正传。 Socket是应用层与 TCP/IP 协议族通信的中间软件抽象层它是一组接口一般由操作系统提供。从设计模式的角度看Socket其实就是一个门面模式它把复杂的 TCP/IP 协议处理和通信缓存管理等等都隐藏在 Socket 接口后面对用户来说使用一组简单的接口就能进行网络应用编程让Socket 去组织数据以符合指定的协议。主机 A 的应用程序要能和主机 B 的应用程序通信必须通过 Socket 建立连接。 给大家一个门面模式渲染图感受一下Socket 改成门面模式后不要怀疑下面这个是否合理如果你去过高端大气上档次的男科医院就知道什么是VIP服务了 客户端连接上一个服务端connect就会在客户端中产生一个 socket 接口实例服务端每接受一个客户端连接就会产生一个 socket 接口实例和客户端的 socket 进行通信有多个客户端连接自然就有多个 socket 接口实例。它们的通信模型大概如下 1.2 长连接、短连接 短连接 短连接的大概流程是这样的连接-传输数据-关闭连接。典型的如HTTP。 传统 HTTP 是无状态的浏览器和服务器每进行一次 HTTP 操作就建立一次连接但任务结束就断开。也可以这样说短连接是指 SOCKET 连接发送数据接收完数据后马上断开连接的一种连接。 长连接 长连接的大概流程是这样的连接-传输数据-保持连接 - 传输数据- 。。。 -关闭连接。典型的如Mysql连接协议。也可以这样说长连接指建立 SOCKET 连接后不管是否使用都保持连接的一种连接。 Q什么时候用长连接短连接 答长连接多用于操作频繁点对点的通讯毕竟TCP连接需要三次握手反复握手是一个很重型的操作。而且从代码角度来说反复创建Socket对象也不是一个经济的方式。而像 WEB 网站的 HTTP服务按照 HTTP协议规范早期一般都用短链接毕竟想WEB应用通常都不会频繁交互或者说每次交互都是短暂的操作。不过值得注意的是现在的 HTTP协议Http1.1尤其是 Http2、Http3 已经开始向长连接演化。 1.3 网络编程与生活常识类比 我们首先来看一个生活中的场景。网络编程可以类比成生活中心理咨询中心场景。 周瑜老师准备开一个心理咨询中心嘴上光喊没用只有到工商局注册“东吴心理诊所”并且在图灵大街 888 号挂牌了才算正式开张。为了开展电话业务申请了一个电话号码 88888888。 这里就类比于网络编程中我们要先在服务端声明一个ServerSocket绑定指定IP地址跟端口 由于疫情经济下滑等原因诸葛老师为了养家糊口没日没夜的工作久疾成病并且有了心理问题。于是打电话过来周瑜老师接了电话但是周瑜老师只是一个接待 这就类比于网络编程中ServerSocket只关注于网络连接事件 所以周瑜老师不懂心理咨询于是通过内部分机把电话转给请来的心理医生 A 负责接待诸葛老师心理医生 A 和诸葛老师通过电话进行沟通模式一般就是一个人说另个一人听两者进行沟通交流。Fox 老师也来了周瑜老师接了电话又把电话转给请来的心理医生 B 负责接待 Fox 老师心理医生 B 和 Fox 老师也通过电话进行沟通。 4、5就类比于网络编程中ServerSocket每接收一个连接事件后就新建一个Socket与它们交互 还算简单吧。这里有一个小结论要跟大家说下 整体来说在网络通信编程中我们只需要关注三件事 PS网络编程范式 PS网络编程范式 PS网络编程范式 1连接事件。客户端连接服务器服务器等待和连接 2读网络数据 3写网络数据 无论是后面学的BIO也好还是NIO又或者什么Netty网络编程所有模式的通信编程都是围绕着这三件事情进行的。服务端提供 IP 和监听端口客户端通过连接操作向服务端监听的地址发起连接请求通过三次握手连接如果连接成功建立双方就可以通过套接字进行通信。 二、BIO 2.1 BIO简介 BIO意为 Blocking I/O即阻塞的 I/O。 BIO 基本上就是我们上面所说的生活场景的直接实现。在 BIO 中类 ServerSocket 负责绑定 IP 地址启动监听端口等待客户连接客户端 Socket 类的实例发起连接操作ServerSocket接受连接后产生一个新的服务端 socket 实例负责和客户端 socket 实例通过输入和输出流进行通信。 那么BIO的阻塞体现在哪里呢我们来看一段简单的BIO代码。 /*** 服务端BIO代码* * author shen·buffet* date 2023-10-17* slogan 听天命尽人事*/ public class TestServer {final static int PORT 8585;public static void main(String[] args) throws IOException {// 第一步创建一个ServerSocketServerSocket serverSocket createServerSocket();System.out.println(第一步完成。 服务已启动);// 第二步监听连接事件handleConnect(serverSocket);}/*** 创建一个ServerSocket*/public static ServerSocket createServerSocket() throws IOException {ServerSocket serverSocket new ServerSocket();serverSocket.bind(new InetSocketAddress(PORT));return serverSocket;}/*** ServerSocket上的事件处理*/public static void handleConnect(ServerSocket serverSocket) throws IOException {int connectCount 0;try {while (true) {Socket clientSocket serverSocket.accept();System.out.println(连接事件来了。 当前连接数目 (connectCount));// 第三步读写网络事件handleClientReadWrite(clientSocket);}} finally {serverSocket.close();}}/*** Client上的读写事件*/public static void handleClientReadWrite(Socket clientSocket) throws IOException {// 实例化与客户端通信的输入输出流ObjectInputStream objectInputStream new ObjectInputStream(clientSocket.getInputStream());ObjectOutputStream objectOutputStream new ObjectOutputStream(clientSocket.getOutputStream());// 接收客户端的输出也就是服务器的输入String userName objectInputStream.readUTF();System.out.println(客户端读事件来了消息 userName);// 服务器的输出也就是客户端的输入objectOutputStream.writeUTF(hello userName);objectOutputStream.flush();} }/*** 客户端BIO代码** author shen·buffet* date 2023-10-17* slogan 听天命尽人事*/ public class TestClient {public static void main(String[] args) throws IOException {//客户端启动必备Socket socket null;// 实例化与服务端通信的输入输出流ObjectOutputStream output null;ObjectInputStream input null;// 服务器的通信地址InetSocketAddress addr new InetSocketAddress(127.0.0.1, 8585);try {socket new Socket();socket.connect(addr);System.out.println(连接服务器成功!!);output new ObjectOutputStream(socket.getOutputStream());input new ObjectInputStream(socket.getInputStream());System.out.println(客户端准备发送数据....);// 向服务器输出请求output.writeUTF(ZhangShen);output.flush();//接收服务器的输出System.out.println(服务端答复数据来了....);System.out.println(input.readUTF());} finally {if (socket ! null) {socket.close();}if (output ! null) {output.close();}if (input ! null) {input.close();}}} }其实这个阻塞就体现在上面代码中的2点TestServer#handleConnect下的serverSocket.accept()服务端等待客户端连接以及TestServer#handleClientReadWrite下的objectInputStream.readUTF()等待客户端socket发送消息。大家伙可以从方法点进去看看方法头上的注释就知道了。通常这种优秀的代码会在方法头上写清楚注释这个方法会阻塞 除了阻塞以外大家发现没有这个代码一条线程只能处理一个TCP连接请求这显然是无法忍受的。所以BIO在演进的过程中为了结合了多线程来实现同时处理多个TCP连接的目的。 如果想要使用我上面的代码实现模拟多条TCP连接失败的场景需要DEBUG住客户端发送消息那一块反正就是不要让它进入finally块关闭socket不然连接都关了服务端也要申请关闭TCP全双工特性还记得吧。 然后IDEA用户的话此时再新增一个Application启动如下图所示整体操作步骤应该不用我手把手再教学了吧… 2.2 BIO结合多线程1普通线程 话不多说先上个代码这里主要修改了TestServer类的handleConnect方法 /*** ServerSocket上的事件处理*/public static void handleConnect(ServerSocket serverSocket) throws IOException {int connectCount 0;try {while (true) {Socket clientSocket serverSocket.accept();System.out.println(连接事件来了。 当前连接数目 (connectCount));// 第三步读写网络事件new Thread(()-{try {handleClientReadWrite(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}}).start();}} finally {serverSocket.close();}}就是在调用handleClientReadWrite的时候新增一个线程。 好了如果看过我前面【并发专题】笔记的朋友或者本身就了解多线程的朋友肯定知道这么干是有性能瓶颈的。瓶颈造成的原因如下 Java线程目前JDK1.8采用的是【内核线程】方案其实JDK17也是所以Java线程与内核线程内核线程指的是PC操作系统的线程是1:1关系因为一个PC最多也就开几千条线程更何况难道这台PC也不可能只有你一个Java程序在用啊 所以这个方案肯定不行的。先上个该方案的模型图让大家理解一下 那不行还有高级一点的方案吗有的用线程池咯试试看 2.3 BIO结合多线程2线程池 先上代码。这里主要修改了TestServer类的handleConnect方法并且新增了一个线程池 final static ExecutorService executorService;static {int cores Runtime.getRuntime().availableProcessors();executorService Executors.newFixedThreadPool(cores);}/*** ServerSocket上的事件处理*/public static void handleConnect(ServerSocket serverSocket) throws IOException {int connectCount 0;try {while (true) {Socket clientSocket serverSocket.accept();System.out.println(连接事件来了。 当前连接数目 (connectCount));// 第三步读写网络事件executorService.submit(() - {try {handleClientReadWrite(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});}} finally {serverSocket.close();}}虽然我这里的线程池用的是newFixedThreadPool不过你们也可以根据需要选择其他API。但是无论你选择何种线程池API这始终是一个BIO并且它的性能受限于线程池。先来个模型图帮助大家理解 *2.4 小结 一定要看看 一定要看看 一定要看看 BIO小结一 大家也看到了BIO使用还是有多种限制的哪怕是上了多线程也一样。事实上我相信大家也看得出来真正让BIO受限的其实是BIO本身。而且我们开过天眼的啊知道有NIO这个东西所以关于BIO性能瓶颈的解决方案我们也能预知道。 BIO小结二 严格来说这里不是BIO小结而是Java Socket网络编程小结。 经过了【前置知识】的系统整合我终于把OSITCP/IP网络模型与Java Socket网络编程结合起来理解了。以前我看BIO、NIO、甚至Netty等网络编程的时候个人遇到的最大的两个问题是 为什么服务端这么多个Socket什么ServerSocket然后每次客户端连接进来的时候还要重新新建一个Socket保存Socket是什么我只能死记硬背真让我回答什么是Socket我也只能含糊其辞说是Java对网络编程的封装了各种流把我整蒙了。【谈网色变】一定程度上是因为【谈流色变】 当然【谈流色变】这个病我还没去治现阶段也只是解决了部分疑问。 第一个问题多个Socket是怎么回事。我在一开始介绍Socket的时候就说过它是对网络模型后面4层的封装使得我们无需关心IP、端口是如何被封装的。另外还记得一条TCP是如何标识唯一的吗我在【前置知识 3.1中讲了】也正是由于这个原因所以为了维护、标识每个客户端的唯一TCP使用一个Socket去保存。 BIO小结三 虽然我们一直说BIO有性能瓶颈话语中看似有不喜的意思但并不代表它就没用。因为它使用简单的特性像Zookeeper等分布式中间件在集群通信方案中都使采用了BIO。 三、NIO 3.1 NIO简介 NIO 库是在 JDK 1.4 中引入的。NIO 弥补了原来的 BIO 的不足它在标准 Java 代码中提供了高速的、面向块的 I/O。NIO 被称为 no-blocking io非阻塞IO也有人叫new io新IO。这无关紧要只是一个称呼。 3.2 与BIO的主要区别 NIO跟BIO相比有2个比较显著的区别 前者非阻塞后者阻塞 BIO是如何阻塞的在哪里阻塞我前面已经提过了。NIO则是因为把这些阻塞操作变成了非阻塞在没有连接或者读取事件过来的时候去做其他事情。比如接收新的客户端的连接或者读取事件。就是这样达到了【一条线程管理多个客户端的连接、输入输出】 前者面向缓冲后者面向流 这算是NIO与BIO最大的区别。 BIO是面向流的怎么理解呢BIO面向流意味着每次从流中读一个或多个字节直至读取所有字节它们没有被缓存在任何地方。此外它不能前后移动流中的数据。如果需要前后移动从流中读取的数据需要先将它缓存到一个缓冲区 NIO是面向缓冲区的NIO把数据读取到一个它稍后处理的缓冲区需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是还需要检查是否该缓冲区中包含所有需要处理的数据。而且需确保当更多的数据读入缓冲区时不要覆盖缓冲区里尚未处理的数据。 3.3 Java NIO没引入多路复用器之前 Java的NIO并非本来就这么强大的在没有引入多路复用之前他也仅仅只是非阻塞版本的BIO而已。代码示例如下 public class NioServer {// 保存客户端连接static ListSocketChannel channelList new ArrayList();public static void main(String[] args) throws IOException, InterruptedException {// 创建NIO ServerSocketChannel,与BIO的serverSocket类似ServerSocketChannel serverSocket ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(9000));// 设置ServerSocketChannel为非阻塞serverSocket.configureBlocking(false);System.out.println(服务启动成功);while (true) {// 非阻塞模式accept方法不会阻塞否则会阻塞// NIO的非阻塞是由操作系统内部实现的底层调用了linux内核的accept函数SocketChannel socketChannel serverSocket.accept();if (socketChannel ! null) { // 如果有客户端进行连接System.out.println(连接成功);// 设置SocketChannel为非阻塞socketChannel.configureBlocking(false);// 保存客户端连接在List中channelList.add(socketChannel);}// 遍历连接进行数据读取IteratorSocketChannel iterator channelList.iterator();while (iterator.hasNext()) {SocketChannel sc iterator.next();ByteBuffer byteBuffer ByteBuffer.allocate(128);// 非阻塞模式read方法不会阻塞否则会阻塞int len sc.read(byteBuffer);// 如果有数据把数据打印出来if (len 0) {System.out.println(接收到消息 new String(byteBuffer.array()));} else if (len -1) { // 如果客户端断开把socket从集合中去掉iterator.remove();System.out.println(客户端断开连接);}}}} }模型图就不画了。相比于BIO仅仅只是非阻塞而已然后呢相比于BIO的【一条线程管理一条连接】它实现了【一条线程管理多个连接】。不过我想细心的朋友已经看见了非阻塞 【while(true)】的组合那这样CPU使用率不是非常高对的这种方案CPU使用率很高而且每次都轮询所有的【与客户端连接的socket】就导致了大量无效的遍历。 3.4 Java NIO 多路复用 不过好在啊JAVA的NIO实现引入了多路复用器这个玩意。 Q什么是多路复用 答多路复用即【多路】【复用】。【多路】指的是多个TCP连接【复用】指的是复用一个或者多个线程。体现在IO中完整的概念IO多路复用的意思就是一条线程管理多个TCP连接的状态。只有当 socket 真正有读写事件时才真正调用实际的 IO 读写操作。因为在多路复用 IO 模型中只需要使用一个线程就可以管理多个socket系统不需要建立新的进程或者线程也不必维护这些线程和进程并且只有在真正有socket 读写事件进行时才会使用 IO 资源所以它大大减少了资源占用。至于怎么实现的IO多路复用这个是Linux内核的行为感兴趣自己去研究 先看看JavaNIO引入多路复用器之后的代码 public class NioSelectorServer {public static void main(String[] args) throws IOException, InterruptedException {// 创建NIO ServerSocketChannelServerSocketChannel serverSocket ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(9000));// 设置ServerSocketChannel为非阻塞serverSocket.configureBlocking(false);// 打开Selector处理Channel即创建epollSelector selector Selector.open();// 把ServerSocketChannel注册到selector上并且selector对客户端accept连接操作感兴趣serverSocket.register(selector, SelectionKey.OP_ACCEPT);System.out.println(服务启动成功);while (true) {// 阻塞等待需要处理的事件发生selector.select();// 获取selector中注册的全部事件的 SelectionKey 实例SetSelectionKey selectionKeys selector.selectedKeys();IteratorSelectionKey iterator selectionKeys.iterator();// 遍历SelectionKey对事件进行处理while (iterator.hasNext()) {SelectionKey key iterator.next();// 如果是OP_ACCEPT事件则进行连接获取和事件注册if (key.isAcceptable()) {ServerSocketChannel server (ServerSocketChannel) key.channel();SocketChannel socketChannel server.accept();socketChannel.configureBlocking(false);// 这里只注册了读事件如果需要给客户端发送数据可以注册写事件socketChannel.register(selector, SelectionKey.OP_READ);System.out.println(客户端连接成功);} else if (key.isReadable()) { // 如果是OP_READ事件则进行读取和打印SocketChannel socketChannel (SocketChannel) key.channel();ByteBuffer byteBuffer ByteBuffer.allocate(128);int len socketChannel.read(byteBuffer);// 如果有数据把数据打印出来if (len 0) {System.out.println(接收到消息 new String(byteBuffer.array()));} else if (len -1) { // 如果客户端断开连接关闭SocketSystem.out.println(客户端断开连接);socketChannel.close();}}//从事件集合里删除本次处理的key防止下次select重复处理iterator.remove();}}} }在这个代码中其实有三个比较核心的东西也是与上一版的NIO区别所在他们就是NIO的三大核心组件 Channel(通道)通道是一个【应用程序和操作系统交互事件、传递内容的渠道注意是连接到操 作系统】。那么既然是和操作系统进行内容的传递那么说明应用程序可以通过通道读取数据也可以通过通道向操作系统写数据而且可以同时进行读写Selector(多路复用器)选择器它允许一个单独的线程来监视多个输入通道你可以注册多个通道就是上面的Channel使用一个选择器Selectors然后使用一个单独的线程来操作这个选择器进而“选择”通道这些通道里已经有可以处理的输入或者选择已准备写入的通道。这种选择机制使得一个单独的线程很容易来管理多个通道应用程序将向 Selector 对象注册需要它关注的 Channel以及具体的某一个 Channel 会对哪些 IO 事件感兴趣。Selector 中也会维护一个“已经注册的 Channel”的容器Buffter缓冲区我们前面说过 JDK NIO 是面向缓冲的Buffer 就是这个缓冲用于和 NIO 通道进行交互。数据是从通道读入缓冲区从缓冲区写入到通道中的。缓冲区本质上是一块可以写入数据然后可以从中读取数据的内存其实就是数组。这块内存被包装成 NIO Buffer 对象并提供了一组方法用来方便的访问该块内存。一个通道对应一个缓冲区 引入多路复用器之后的Java NIO模型如下 PS我估计比较较真的同学对上面的代码中的selector、channel、selectionKey会有点疑惑这个写法上确实比较抽象。大家点开Selector跟SelectionKey就知道它们的关系了。我对这个设计思想也不是很熟悉就不说了。简单来说就是你中有我我中有你 NIO多路复用的代码如上流程我就不多说了基本上都写了注释。但还是要提一嘴这个方案比较核心的代码在于如下 // 创建多路复用器 Selector.open() // 将channel注册到多路复用器上并设置感兴趣的事件 socketChannel.register(selector, SelectionKey.OP_READ) // 阻塞等待需要处理的事件发生 selector.select()所以我是希望大伙可以稍微去看一下这个源码的也帮助大家理解selector、channel、selectionKey之间的关系。代码流程图如下下图是基于Linux操作系统的实现Windows实现大体相似但是实现类多少有点区别。因为Windows下没有epoll函数只有select函数 整体来说JavaNIO整个调用流程就是调用了【操作系统的内核函数】来创建Socket获取到Socket的文件描述符再创建一个Selector对象对应操作系统的【Epoll描述符】将获取到的Socket连接的文件描述符的事件绑定到Selector对应的Epoll文件描述符上进行事件的异步通知这样就实现了使用一条线程并且不需要太多的无效的遍历将事件处理交给了操作系统内核操作系统中断程序DMA来实现的大大提高了效率。 顺带提一嘴。上面说的Linux内核函数其实就是大名鼎鼎的epoll函数可曾听闻epoll模型。 *四、拓展epoll简介 关于epoll虽然这个属于拓展但是因为知乎上看到了一个硬核大佬写的文章从硬件层面开始给大家推演epoll的演进变化实在是太牛逼了而且讲得通俗易懂。如果你又恰好完整看了我的【前置知识】个人感觉阅读起来不会很困难当然硬件知识直接跳过所以我还是比较建议大伙可以学习学习的。文章传送门《如果这篇文章说不清epoll的本质那就过来掐死我吧》 学习总结 从OSI、TCP/IP模型开始学习网络编程学习了BIO、NIO的一些基础知识认识了NIO多路复用以及多路复用的三个核心组件个人在这两天学习得非常爽很满足因为这确实解决了不少个人在网络编程方面的疑问 感谢 感谢知乎大佬【作者罗培羽】的文章《如果这篇文章说不清epoll的本质那就过来掐死我吧》
http://www.dnsts.com.cn/news/204483.html

相关文章:

  • 婺源做微信网站seo查询 工具
  • 江苏省建设局网站首页wordpress写主题为何页面乱
  • 个人做电梯网站网站制作都有哪些
  • 青岛网站建站团队dedecms网站logo
  • 做商城网站需要什么视觉asp网站源码
  • 建设网站的简单编程语言如何制作属于自己的网页
  • 教育培训类网站建设如何做网站条幅闪图
  • 网站项目管理系统做网站为什么很复杂
  • 惠州网站建设英语python学了能干嘛
  • 建站之星网站建设系统网推技巧
  • 网站建设平台协议书wordpress媒体库空白
  • 常熟住房和城乡建设局网站首页菏泽网站备案拍照
  • 上海微网站建设公司注册查询系统
  • 企业建站模板多少钱建设标准网站
  • 找外地的做网站wordpress登陆后评论增加注册按钮
  • 电商网站毕业设计论文全球旅游网站排名
  • 兴城泳装电子商务网站建设万州论坛网站建设
  • 今科云平台网站建设技术鲁权屯网站建设
  • 福田网站建设有限公司wordpress quiz addon
  • 2个网站做的链接怎么用一个域名wordpress get_taxonomy
  • 网站后台管理的超级链接怎么做wordpress 论坛 整合
  • 怎么自己做网站教程亚马逊海外版网站
  • 公司网站免费建站做购物平台网站需要注意什么
  • 长治网站制作招聘信息山东网站备案公司
  • 网站转化低的原因工程施工合同电子版
  • 做搜索的网站有哪些手机网速慢怎么办
  • 加油站网架电子商务网站建设与管理学习心得
  • 网站建设 预算wordpress 子菜单顺序
  • 网站申请免费网站建设源代码交付
  • 网站建设 规范在线教育网站怎样建设