JAVA网络编程
Posted idempotent
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA网络编程相关的知识,希望对你有一定的参考价值。
网络通信的基础知识,JAVA常用的网络编程方法、类的使用。
一、相关概念
计算机网络:指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
??网络操作系统:是一种能代替操作系统的软件程序,是网络的心脏和灵魂,是向网络计算机提供服务的特殊的操作系统。借由网络达到互相传递数据与各种消息,分为服务器和客户端。服务器的主要功能是管理服务器和网络上的各种资源和网络设备的共用,加以统合并控管流量,避免有瘫痪的可能性。客户端就是有着能接收服务器所传递的数据来运用的功能,好让客户端可以清楚的搜索所需的资源。
??网络管理软件:就是能够完成网络管理功能的网络管理系统,简称网管系统。借助于网管系统,网络管理员不仅可以经由网络管理员与被管理系统中代理交换网络信息,而且可以开发网络管理应用程序。
??网络通信协议:是一种网络通用语言,为连接不同操作系统和不同硬件体系结构的互联网络提供通信支持。为进行网络中的数据交换而建立的规则、标准或约定。由语法(用户数据与控制信息的结构与格式,以及数据出现的顺序)、语义(解释控制信息每个部分的意义)、时序(对事件发生顺序的详细说明)三要素组成。
二、网络体系结构
体系结构则描述一组部件以及部件之间的联系。计算机网络采用分层的体系结构,一来计算机网络本身趋于复杂,要想完成两台计算机之间的通信,不仅仅要保证双方的网络连接,还要定义数据格式、安全控制等问题,也就是相互通信的计算机系统必须高度协调工作才行。二来没有明确的体系结构,不同的网络系统因不同的设计相互通信存在问题。采用分层的体系结构,不仅仅将网络通信将庞大的复杂问题转为若干较小的局部问题。层次间分功明确、高层使用底层服务的透明性更有易于研究和使用,同时也为计算机网络的设计提供标准。
应用层
应用层是体系结构中的最高层,是直接为应用程序提供服务的。其任务就是通过应用进程间的交互来完成特定的网络应用,相当于访问网络服务的接口。应用层协议则定义应用进程间通信和交互的规则。对于不同的网络应用有不同的应用层协议,常见的应用层协议有域名解析DNS协议、支持万维网应用的HTTP协议和支持电子邮件的SMTP协议等。
??应用层交互的数据单元称为报文,不同的应用层协议所传输的应用层报文格式也不一样,且相同的协议请求报文和响应报文也可能不一样,图中的就是DNS协议的报文格式。各个报文的语法不同、字段的语义含义也不同,应用层协议不仅仅定义传输的报文格式、还包括发送报文的时间、报文的解析等。
表示层
表示层提供数据格式的转换服务,包括数据的表示、安全、压缩。可确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取。
??在表示层,数据将按照网络能理解的方案进行格式化。格式包括JPEG、ASCII、加密格式等,格式化也因所使用网络的类型不同而不同。表示层同时管理数据的解密和加密,如系统口令的处理。
会话层
会话层的功能就是建立端连接并提供访问和会话管理。会话层利用传输层提供的服务,能够在本地主机与远程主机之间建立、管理和终止会话。
??会话层负责在网络中的两节点之间建立、维持和终止通信,包括对通信过程流畅的保证、节点对话的同步,同时可决定是否中断通信或重新发送。
运输层
运输层的任务就是负责向两台主机中进程之间的通信提供通用的数据传输服务。应用层应用利用该服务传送应用层报文,同时运输层根据不同的协议还提供可靠传输、流量控制、拥塞控制等功能,常见的运输层协议主要就是用户数据报协议UDP和传输控制协议TCP。
??运输层传输的数据根据协议的不同而不同,用户数据报协议UDP的传输单元为用户数据报,而基于传输控制协议TCP的数据传输单元是报文段。它们都是在应用层的数据上添加上首部变成自己的数据传输单元,UDP协议的首部开销小,只有8字节,而TCP协议的首部则达到20字节。
??运输层提供的通用的数据传输服务是指不针对某个特定网络应用,而是多种应用可以使用同一个运输层服务。由于一台主机可以运行多个进程,因此运输层有复用和分用的功能。复用就是多个应用层进程可同时使用下面运输层的服务。分用和复用相反,是运输层收到的信息分别交付上面应用层中的相应进程。
??为了让运输层可以将指定数据交付到应用程序,在一台计算机中需要有唯一的标识符来表示特定的应用程序。解决这个问题的方法就是运输层使用协议端口号。端口号是应用层的各种协议进程与运输实体进行层间交互的一种地址。因此只要将所传送的报文交到目的主机某个合适的端口,剩下的工作就由UDP或者TCP来完成。
网络层
网络层负责为分组交换网上的不同主机提供通信服务。在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组或包进行传送。即网络层向上只提供简单灵活、无连接的、尽最大努力交付的数据报服务。网络层使用的主要协议就是IP协议。
??网络层传输的数据叫IP数据报,数据部分是运输层组装好的用户数据报或报文段,首部部分则由网络层自己并接。其中包括了源主机地址、目标主机的地址和片偏移信息等。除了固定长度部分,其首部还有可选部分,因此实际上IP数据报的首部长度不是固定的。
??网络层将运输层传下来的分组分片后转发,因此片偏移信息则可确定其在原分组的位置,以便对收到的数据进行组装。网络层采用分组转发的策略,在源和目标主机之间不需要一直占用网络资源,这种基于无连接的方式提高了网络利用率,同时内部使用分层次的路由选择算法保证了传输的高效。
??为了在网络众多主机中找到目标主机,需要给互联网上的每一台主机或路由器的每一个接口分配一个全世界范围内唯一的32位标识符,即IP地址。IP地址的结构我们可以在互联网上很方便的进行寻址,互联网上的主机和路由器内部都各自维护着路由表。通过IP地址的分类和子网划分,可以很快的在路由表内找到到达目标主机的下一步转发地址,由此通过不断的分组转发,最终将数据报交付到目标主机。
数据链路层
数据链路层又称链路层。当两台主机之间要进行数据传输,数据总是在一段一段的链路上传送的,因此就需要有专门的链路层协议来控制两个相邻结点之间的数据传输。数据链路层使用的协议包括点对点传输PPP协议和基于广播信道的CSMA/CD协议。
??数据链路层传输的数据单元叫做帧,图中就是PPP协议传输的帧。数据部分由上层的IP数据报组成,同时添加了由特殊标识组成的首部和尾部以便数据链路层识别出完整的帧将数据上交给上层。
??实际上在计算机中有个网络接口卡即网卡或叫网络适配器,其具有自己的处理机和存储器,计算机接收帧和发送帧则均交由适配器处理。当适配器收到有差错的帧,会丢弃而不通知计算机。收到正确的帧时才中断将其交付到协议栈中的网络层。而当计算机要发送IP数据报,就由协议栈将数据报交给适配器组装成帧后发送到局域网中。
??适配器中存储着计算机的硬件地址或者叫做MAC地址,MAC地址是数据链路层和物理层使用的地址。封装IP数据报时会根据地址解析协议ARP获得目标MAC地址封装成帧,适配器则是根据MAC地址选择接口进行转发的。而当网络中其他适配器接收到MAC帧,则会用硬件检查MAC帧的目标地址,选择丢弃还是向上层解析,这样就不会浪费内存空间。
物理层
物理层考虑的是怎样在连接计算机的传输媒体上传输数据比特流,而不是指具体的传输媒体。
??物理层传输的单位是比特,其要决定具体比特的表示方法,接收方则要识别出发送方所发送的比特。同时物理层也要能识别出一个完整的帧比特流交给上层解析,因此物理层更多的是决定接线器物理特性、电压的范围、意义以及事件时序。而对于传输介质是在物理层之下的。
三、网络协议
数据链路层PPP协议
PPP是一种数据链路层协议,遵循HDLC(高级数据链路控制协议)族的一般报文格式。PPP是为了在点对点物理链路上传输OSI模型中的网络层报文而设计的。
??图中为一个PPP帧,PPP帧的首部和尾部分别为四个字段和两个字段。首部的标志字段F规定为0x7E,表示一个帧的开始。地址字段A规定为0xFF,由于PPP是点对点通信,因此不使用地址字段,MAC地址一般用于局域网范围内主机通信。控制字段C规定为0x03。协议字段占两个字节,根据内容来判断是IP数据报,还是网络层其他控制信息等。PPP帧数据部分长度是可变的,但是不得超过1500字节。尾部部分中第一个字段是使用CRC(循环冗余校验)的帧校验序列FCS。尾部中标志字段F也规定为0x7E表示帧结束。
??PPP协议为了防止在数据部分识别出标志字符,使用了填充的方式来进行转义。当PPP使用异步传输,则将转义字符定义为0x7D使用字节填充。当PPP使用同步传输,就使用零比特填充,只要发现5个连续的1,则立即填入一个0。因此PPP协议具有透明传输的特点。
??PPP协议同时还可以实现无比特差错的传输,采用循环冗余校验的差错检测技术。每次发送数据前将数据分组,计算出冗余码添加到帧的尾部字段。接收方收到帧数据时进行冗余码校验,出现错误则丢弃。因此实现了无差错传输,但并不是可靠传输。
网络层IP协议
IP协议是为了在分组交换计算机通信网络的互联系统中使用而设计的,为上层协议提供无状态、无连接、不可靠的服务。IP协议包括IP数据报首部的定义以及IP数据报的路由和转发,决定了数据报是否应该转发和如何转发。IP协议根据版本分为IPv4和IPv6。
??IPv4与IPv6比较大的区别就是IP地址的不同,在IPv4中IP地址是32位的,根据不同的用途分为A、B、C、D、E五类,同时为了方便寻址进一步划分出子网,将IP地址从两级变为三级,实际路由转发中会根据子网掩码转发到对应的子网。而IPv6的IP地址为128位。空间庞大,分为单播地址、组播地址、任播地址。另外可以发现IPv6的首部相比IPv4的首部取消了许多字段。IPv6首部固定是40字节因此取消了首部长度,同时用有效载荷长度代替总长度字段,有效载荷长度就包括扩展首部加上数据部分长度。IPv6取消服务类型字段,使用流标号和优先级可以实现,且将标识、标志和片偏移包含在扩展首部中。用下一个首部代替协议字段,用跳数限制代替生存时间。
??IPv4首部具有校验和字段,和PPP协议一样可以进行数据校验。但是IPv4的首部校验和只校验首部数据,当发现首部校验失败则丢弃报文。在IPv6中则取消了首部校验字段,加快了路由器对数据报的处理速度,实际校验则交给其他协议层完成。
??实际分组转发过程中,路由器每次收到数据报都会提取出IP地址,如果能够直接交付则结束转发。否则将IP地址先与本网络直接相连的网络子网掩码匹配,再与其他网络的子网掩码匹配,直至完成转发。
运输层UDP协议
UDP即用户数据报协议,是一种无连接的运输层协议,提供面向事务的简单不可靠信息传送服务。
??UDP协议分为首部字段和数据字段,其中首部字段只占用8个字节,分别是个占用两个字节的源端口、目的端口、长度和检验和。其中伪首部信息来自IP分组头并不属于UDP首部部分,主要用来计算校验和实现无差错检测。
??UDP是一个无连接协议,传输数据之前源端和终端不建立连接,尽最大努力的将数据交付给终端。UDP是面向报文的,发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此应用程序需要选择合适的报文大小。UDP没有拥塞控制,在网络质量差时数据包丢失会比较严重,但是其具有资源消耗小,处理速度快的优点,适用于音频、视频等数据的传输。
运输层TCP协议
TCP传输控制协议是一种面向连接的、可靠的、基于字节流的运输层通信协议,因此TCP协议可以保证接收端毫无差错地接收到发送端发出的字节流,为应用程序提供可靠的通信服务。
??TCP报文首部由20字节的固定部分加上可变部分,其中源端口和目的端口各占两字节。序号和确认号各占四字节,是TCP可靠传输服务的关键部分。保留字段后面有6个特殊的标识字段实现传输控制。和UDP一样,TCP计算校验和时同样要加上12字节的伪首部。
??TCP不仅仅提供有连接的可靠传输,同时还提供流量控制和拥塞控制机制。TCP是面向字节流的,当应用层传输下来的数据量过少时TCP会进行缓存,当数据量过多时会分块传输。也就是TCP会根据接收方的接收能力和网络的拥塞情况对报文段的长度进行划分。
??TCP连接使用三次握手协议建立连接。首先客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。其次服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。最后便是客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。三次握手成功之后,TCP客户端和服务器端成功地建立连接,可以开始传输数据。三次握手即确保了通信双方收发能力的正常,也保证失效的连接请求不会正常工作。
??TCP释放连接则要经过四个阶段。当应用程序不需要再发送数据,该端的TCP就发送一个FIN分节,表示数据发送完毕进入FIN-WAIT-1状态。服务器接收到FIN后要及时向客户端发出确认信号,然后进入CLOSE-WAIT状态。一段时间后服务器没有要传输的数据,才向客户端发生自己的FIN报文,进入LAST-ACK状态。客户端对服务端发出的FIN进行确认后,先进入THEN-WAIT状态,等等待计时器2MSL之后才进入CLOSED状态(确保自己最后发送的ACK报文不丢失)。
TCP可靠传输:保证TCP发送方传输的每个数据块可以无差错、无丢失的被接收方接收。且TCP接收方对于收到的重复数据可以进行过滤。
??保证可靠的一个方法便是使用停止等待协议,发送方每发送完一个数据单元就停止发送,等待接受方确认,在接收到接收方的确认信息后就继续发送下一个分组。每次发送完一个数据,TCP就会设置一个超时计时器。在超时计时器到时间之前收到数据确认信息,则该数据单元正确传输到接收者,否则则启动重传。发送方需要保留数据单元副本,以供超时重传。接收方接到重复的数据则丢弃不向上层交付,同时应坚持向发送方发送确。然而这种方式没发送一个数据都需要等待,信道利用率低。
??连续ARQ协议相比于停止等待协议信道利用率得到很大提高,且容易实现。发送方和接收方都维持着各自的滑动窗口。对于发送方而言,位于滑动窗口内的数据可以连续发送而不需要等待确认。接收方则采用累积确认的方式,收到几个分组后对按序到达的最后一个分组发送确认(这里的确认号应该是期望收到的下一个分组序号),同时将连续的数据交付到应用层。而接收方收到确认后就可以知道序号之前的数据已全部被接收,则可以往前移动滑动窗口继续发送数据。
流量控制:就是让发送端不要发送太快,要让接收端来得及接收。
??TCP流量控制是通过大小可变的滑动窗口实现的,发送端窗口大小不能超过接收端窗口大小的值。发送方与接收方传输数据过程中,接收方会发生滑动窗口值限制发送方发送的数据量,只要发送方收到零窗口通知,表示不能再发送数据,同时启动持续计时器。持续计时器时间到期,发送方会发送一个零窗口探测报文段,此时接收方返回确认给出新窗口值。
拥塞控制:指的是某段时间内,对网络中的某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变化。往往由于过多的数据注入到网络中,使网络中的路由器或链路过载。因此拥塞控制任务就是防止过多的数据注入到网络中,涉及到所有的主机、所有的路由器、以及与降低网络传输性能有关的所有因素。
??TCP的拥塞控制包括四个部分,分别是慢启动、拥塞避免、快速重传和快速恢复。慢启动指每当建立一个TCP连接时或一个TCP连接发生超时重传后,该连接便进入慢启动阶段。此时发送方发送窗口即拥塞窗口初始为1,之后每收到一个确认就将窗口值增1,所以拥塞窗口按指数增加。而当窗口值超过慢启动阐值或发生报文段丢失重传,则慢启动阶段结束。TCP连接便进入拥塞避免阶段,此阶段每一次发送的窗口内所有报文段被完全确认后,才将窗口值增1,因此此阶段呈线性增加。
??快速重传部分则是对超时重传的改进,当发送端收到对同一个报文的三个重复确认时,就确定一个报文段已经丢失。因此立刻重传丢失的报文段,而不必等到重传定时器超时。那么快速恢复就是对丢失恢复机制的改进,指的是在快速重传之后,不经过慢启动过程而直接进入拥塞避免阶段。
应用层HTTP协议
HTTP超文本传输协议是一个简单的请求-响应协议,它通常运行在TCP之上,指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII码形式给出,而消息内容则具有一个类似MIME的格式。
HTTP请求报文包括请求行、请求头部、空行 和 请求包体
??请求行由方法字段、URL字段和HTTP协议版本字段3个部分组成,之间使用空格隔开。常见的HTTP请求方法有GET、POST、HEAD、PUT、DELETE等。
GET:用于信息获取,向服务器请求指定的资源。GET方法是安全的和幂等的,所谓安全是指该操作只是用于获取信息而非修改信息,因此不会产生副作用。幂等是指使用同样的条件,一次和多次重复的请求对系统资源的影响是一致的。
POST:向指定资源提交数据,请求服务器处理,请求资源会被包含在请求体中。POST方法是非幂等的方法,因为该方法可能会创建新的资源或者修改现有资源。
HEAD:和GET方法一样,只是HEAD方法获取的是报文首部而不返回报文主体内容。可用于确认URI的有效性及资源更新的日期等。
PUT:会向指定资源上传内容,要求在请求报文的主体中包含内容,然后保存到请求URI指定的位置。PUT方法是幂等的
DELETE:用于请求服务器删除请求URI所标识的资源,与PUT操作刚好相反。同时DELETE方法也是幂等的。
??URI是统一资源标识符,用来唯一的标识一个资源。而URL是统一资源定位器,是一种具体的URI,包含了用于查找某个资源的足够的信息,是互联网上用来标识某一处资源的地址。URI是一种语义上的抽象概念,可以是绝对的,也可以是相对的。URL则必须提供足够的信息来定位,是绝对的。
1.协议部分:该URL的协议部分为"http:",表示的就是HTTP协议。其它协议表示的有"FTP://"、"MMS://"等。
2.域名部分:该URL的域名部分为"www.aspxfans.com",当然URL中也可以使用IP地址替换域名部分,有时主机名前允许携带用户名和密码,如FTP服务器的一个请求"ftp://joe:joepasswd@ftp.prep.edu/pub/name"。
3.端口部分:跟在域名后面,HTTP协议的默认端口号是80。
4.虚拟目录部分:从域名后的第一个"/"开始到最后一个"/"为止,是虚拟目录部分。
5.文件名部分:从域名后的最后一个"/"开始到"?"为止,是文件名部分,如果没有"?",则是从域名后的最后一个"/"开始到"#"为止,是文件部分,如果没有"?"和"#",那么从域名后的最后一个"/"开始到结束,都是文件名部分。
6.参数部分:从"?"开始到"#"为止之间的部分为参数部分,又称搜索部分、查询部分。
7.锚部分:从"#"开始到最后,都是锚部分。
??请求头部由关键字-值对组成,每行一对,关键字和值用英文冒号":"分隔。请求头部通知服务器有关于客户端请求的信息。常见的请求头字段有:
1.User-Agent:产生请求的浏览器类型。
2.Accept:客户端可识别的响应内容类型列表。
3.Accept-Language:客户端可接受的自然语言。
4.Accept-Encoding:客户端可接受的编码压缩格式。
5.Accept-Charset:可接受的应答的字符集。
6.Cookie:存储于客户端扩展字段,向同一域名的服务端发送属于该域的cookie。
??空行表示请求头部到这里为止,之后便是请求包体。对于GET方法而言,请求包体是没有数据的。
HTTP响应报文包括状态行、响应头部、空行和响应包体
??状态行由HTTP协议版本字段、状态码和状态码的描述文本3个部分组成,之间使用空格隔开。状态码由三位数字组成,第一位数字表示响应的类型。如以1开头的表示服务器已接收了客户端请求,客户端可继续发送请求。以2开头的表示服务器已成功接收到请求并进行处理。以3开头的状态码表示服务器要求客户端重定向。4开头的表示客户端的请求有非法内容。5开头则是指服务器未能正常处理客户端的请求而出现意外错误。常见的状态码有:
100:请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。
101:请求者已要求服务器切换协议,服务器已确认并准备切换。
200:服务器已成功处理了请求。
301:请求的网页已永久移动到新位置。服务器返回此响应(对GET或HEAD请求的响应)时,会自动将请求者转到新位置。
302:服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求,表示临时移动。
304:是对客户端有缓存情况下服务端的一种响应,表示自从上次请求后,请求的网页未修改过。
305:请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
400:错误请求,服务器不理解请求的语法。
401:表示未授权,请求要求身份验证。
403:服务器拒绝请求。
404:服务器找不到请求的网页。
405:表示方法禁用,禁用请求中指定的方法。
408:服务器等候请求时发生超时。
500:服务器内部错误。
502:服务器作为网关或代理,从上游服务器收到无效响应。
503:服务器目前无法使用(由于超载或停机维护)。
504:服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505:服务器不支持请求中所用的 HTTP 协议版本。
??响应头部和请求头部格式一样,常见的响应头部有:
1.Location:Location响应报头域用于重定向接受者到一个新的位置。
2.Server:Server响应报头域包含了服务器用来处理请求的软件信息及其版本。
3.Vary:指示不可缓存的请求头列表。
4.Connection:连接方式,对于请求来说close告诉 WEB 服务器或者代理服务器,在完成本次请求的响应后,断开连接,不等待本次连接的后续请求了。keepalive告诉WEB服务器或者代理服务器,在完成本次请求的响应后,保持连接,等待本次连接的后续请求。对于响应来说close表示连接已经关闭,keepalive表示连接保持着,在等待本次连接的后续请求。Keep-Alive表示如果浏览器请求保持连接,则该头部表明希望WEB 服务器保持连接多长时间。
5.WWW-Authenticate:WWW-Authenticate响应报头域必须被包含在401 (未授权的)响应消息中,这个报头域和前面讲到的Authorization 请求报头域是相关的,当客户端收到 401 响应消息,就要决定是否请求服务器对其进行验证。如果要求服务器对其进行验证,就可以发送一个包含了Authorization 报头域的请求。
6.Expires:主要针对HTTP1.0,指示可以缓存这个资源表示,直到指定的时间为止。
7.Cache-control:提供细粒度的缓存策略(若头部存在Expires,则会覆盖Expires的作用)。缓存策略有:
max-age表示从现在直至缓存项过期的秒数。
s-maxage表示从现在直至缓存项在共享缓存中过期之前的秒数(私有缓存可将缓存项存更长时间)。
public表示可以缓存一个经过认证的响应。
private仅单个用户缓存可以保存响应,而共享缓存不保存。
no-cache实际表示缓存项仍可以缓存,不过客户端每次访问时要用一个Etag或last-modified首部重新验证响应状态。
no-store表示不缓存
??空行用来断开响应头部,响应包体则是服务器返回给客户端的文本信息。
HTTP协议的工作方式
??HTTP有分非持久连接和持久连接,非持久连接对于每个连接只处理一个请求-响应事务。而持久连接的每个连接可以处理多个请求-响应事务。如HTTP1.0采用的就是非持久连接。如果用非持久连接访问一个包含1个html文件和10个JPEG图像,那么一次TCP建立连接到释放的过程只先获取了HTML文件和10个JPEG图像的引用,之后对于每个图像引用需要重复TCP连接、释放过程去请求资源,一次效率低下,且将大部分时间花在TCP连接、释放过程。
??对于高版本的HTTP都是默认使用持久连接,TCP持久连接又分为流水线方式和非流水线方式。流水线方式则指HTTP客户每碰到一个引用就立即发送一个请求,即HTTP客户可以一个接 一个挨着发送各个引用对象的请求。服务器收到这些请求后,也可以一个接一个的发送各个对象的响应。那么非流水线方式就是HTTP客户只有在收到前一个请求的响应后,才发出新的请求。因此带流水线的持久连接中服务器空等请求的时间较少。
HTTP协议的会话机制
??HTTP协议是无状态的协议,特指协议对于事务处理没有记忆功能。也就是说HTTP的每个请求都是独立的,与前面的请求和后面的请求都是没有直接联系的。最初HTTP协议只是用来浏览静态文件,因此无状态使得应答效率高且无需多余的开销来维护状态。然而随着WEB的发展,就需要让它变得有状态,因此引入了Cookie、Session等机制。
??Cookie是通过客户端保持状态的解决方案,Cookie就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。因此服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的Cookie得到客户端特有的信息,从而动态生成与该客户端相对应的内容。
??与Cookie相对的一个解决方案是Session,它是通过服务器来保持状态的。Session指的是服务器端为客户端所开辟的存储空间,在其中保存的信息就是用于保持状态。Session在服务器创建并保存,服务器会返回客户端Session id。客户端需要保存Session id(如保存在Cookie中),然后每次请求携带Session id发送数据到服务端。服务端就可以根据Session id获取对应的Session。
四、JAVA网络编程类库
InetAddress类
java.net.InetAddress类是JAVA对IP地址包括IPv4和IPv6的高层表示,大多数其他网络类都要用到这个类。它包括一个主机名和IP地址。
??InetAddress类没有公共构造函数,但是其提供一些静态工厂方法可以创建对象实例。最常用的就是InetAddress.getByName()。该方法不仅仅设置了InetAddress类内部的一个String私有字段,同时还会建立与本地DNS服务器的一个连接,来查找名字和数字地址。如果DNS找不到地址则抛出UnknownHostException异常。
//静态工厂方法
InetAddress.getByName(String host) //该方法不仅仅可以根据域名解析出主机名,同时支持传入一个点分四段地址进行反向解析。
InetAddress.getAllByName(String host) //该方法返回的是InetAddress数组,可以解析出一个域名的多个地址
InetAddress.getLocalHost() //该方法会解析本地机器的地址
InetAddress.getByAddress(byte[] addr) //用一个IP地址创建一个InetAddress对象,不保证IP一定存在,只有IP格式不对了才抛出异常
InetAddress.getByAddress(String hostname, byte[] addr) //提供的主机名和IP地址创建 InetAddress,不检测名称服务的地址有效性。
//获取方法
String getHostName() //返回主机的名字,只有在不知道主机名时才与DNS连接。
String getCanonicalHostName() //返回主机的名字,每次都会和DNS服务器连接。
byte[] getAddress() //以网络字节顺序将IP地址作为一个字节数组返回,可以根据字节数组长度判断是IPv4地址(4字节)还是IPv6地址(16字节)。
String getHostAddress() //返回一个字符串,包含点分四段格式的IP地址。
//判断方法
boolean isAnyLocalAddress() //是否是通配地址
boolean isLoopbackAddress() //是否回送地址
boolean isMulticastAddress() //是否组播地址
boolean isReachable(int timeout) //测试结点是否可达
??JAVA程序所在的应用层,完全不需要了解一个地址是IPv4还是IPv6,因此完全可以用InetAddress类指向Inet4Adress、Inet6Address对象。同时可以使用IP地址返回的字节数组大小判断是IPv4还是IPv6。
InetSocketAddress类
InetSocketAddress实现了SocketAddress抽象类,表示的是IP套接字地址(IP地址+端口号),不依赖任何协议。
//构造方法
InetSocketAddress(InetAddress addr, int port) //根据IP地址和端口号创建套接字地址。
InetSocketAddress(int port) //创建套接字地址,其中IP地址为通配符地址,端口号为指定值。
InetSocketAddress(String hostname, int port) //根据主机名和端口号创建套接字地址。
//获取方法
InetAddress getAddress() //获取InetAddress
String getHostName() //获取主机名
int getPort() //获取端口号
URL类
java.net.URL类是对统一资源定位符的抽象,是一个final类,不能对其派生子类。使用URL类使得模式(协议)、主机名、端口、路径、查询字符串等每个字段可以单独设置,比将URL直接表示成字符串灵活。且URL对象是不可变的,保证了线程安全。
//构造方法
URL(String spec) //根据String表示形式创建URL对象。
URL(String protocol, String host, int port, String file) //根据指定protocol、host、port号和file创建URL对象。
URL(String protocol, String host, String file) //根据指定的protocol名称、host名称和file名称创建 URL。
URL(URL context, String spec) //通过在指定的上下文中对给定的spec进行解析创建 URL。
//从URL获取数据
InputStream openStream() //连接到URL所引用的资源,返回一个InputStream对象,可以从这个流中读取数据。
URLConnection openConnection() //为指定的URL打开一个Socket,并返回一个URLConnection对象,利用URLConnection一样可以获取InputStream对象。
Object getContent() //是下载URL引用数据的第三种方法。获取由URL引用的数据,尝试由它建立某种类型的对象。
//获取方法
String getProtocol() //返回一个String,包含URL的模式。
String getHost() //返回URL的主机名。
int getPort() //返回URL中指定端口号。
String getFile() //返回URL的路径部分。
CookieManager类
CookieManager提供CookieHandler的具体实现,将cookie的存储区与围绕接受和拒绝cookie的策略分离开来。使用管理存储的CookieStore以及做出cookie接受/拒绝决策的CookiePolicy对象来初始化CookieManager。
//构造方法
CookieManager(CookieStore store, CookiePolicy cookiePolicy) //使用指定cookie存储区和cookie策略创建一个新cookie管理器
//常用方法
CookieStore getCookieStore() //获取当前cookie存储区。
setCookiePolicy(CookiePolicy cookiePolicy) //设置此cookie管理器的cookie策略。预定义的三个策略有CookiePolicy.ACCEPT_ALL接收所以cookie、CookiePolicy.ACCEPT_NONE不接收任何cookie、CookiePolicy.ACCEPT_ORIGINAL_SERVER只接受第一方cookie。
//使用CookieManager则用CookieHandler的setDefault()方法配置,传入一个CookieManager实例。
URLConnection类
URLConnection是一个抽象类,表示指向URL指定资源的活动连接。与URL相比,它对与服务器的交互提供了更多的控制,可以检查服务器的首部,并相应做出响应。同时可以设置服务端请求中使用的首部字段。在HTTP协议中,还可以用POST、PUT和其他HTTP请求方法向服务器发回数据。
??使用URLConnection类程序步骤就是先构建一个URL对象,通过URL对象的openConnection()方法获取该URL的URLConnection对象。之后可以配置URLConnection对象,获取流读取数据和写入数据最后关闭连接。URLConnection仅有的一个构造方法是保护类型的,且本身是抽象类,因此要通过调用openConnection()来获取实现。同时也可以继承类且要重写connect(),该方法建立与服务器的连接,因此依赖服务类型。实际上第一次构造URLConnection是未连接的,而connect()方法用来建立连接。不过对于getInputStream()、getContent()、getHeaderField()和其他要求打开连接的方法,若尚未连接则会调用connect()方法,因而很少直接调用connect()。
获取方法
InputStream getInputStream() //返回一个InputStream对象,可以读取和解析服务器发送的数据。
OutputStream getOutputStream() //返回一个OutStream对象,可以向服务器写入数据
String getContentType() //返回响应主体的MIME内容类型。
int getContentLength() //获取内容大小、如果没有Content-length首部,方法则返回-1。
String getContentEncoding() //返回内容编码方式,如果没有编码就返回null。
long getDate() //返回一个long,指定文档何时发送。
String getHeaderField(String name) //返回指定首部字段的值。
配置方法
setRequestProperty(String name, String value) //为HTTP首部增加首部字段
addRequestProperty(String name, String value) //为HTTP首部某个字段额外添加属性值
??HttpURLConnection是URLConnection的抽象子类,提供了设置请求方法、确定是否重定向、获取响应码和消息、以及是否使用代理服务器等额外功能。和URLConnection一样无法使用构造方法创建实例,不过如果使用HTTP的URL构造URL对象,其openConnection()方法返回的就是HttpURLConnection的一个实例。
setRequestMethod(String method) //设置HTTP的请求方法。
int getResponseCode() //放回响应信息的响应码。
String getResponseMessage() //返回响应消息。
InputStream getErrorStream() //遇到错误时返回错误内容,如果没有错误或消息为空则返回null。
setInstanceFollowRedirects(boolean followRedirects) //默认情况HttpURLConnection是跟随重定向的,但是可以通过此方法设置。
disconnect() //允许客户端断开连接。
Socket类
Socket允许程序员将网络连接看作是另一个可以读写的字节流,隐藏了底层错误检测、包分组、重传等细节。Socket类是JAVA完成客户端TCP操作的基础类,其他建立TCP网络连接的面向客户端的类都会调用这个类方法。主要提供连接远程主机、发送数据、接收数据、关闭连接这四个功能。
??一般便是通过构造函数创建Socket对象尝试连接远程主机。建立连接后,本地或远程主机就可以通过Socket创建输入、输出流来互相发送数据。
//构造方法
Socket(String host, int port) //创建一个流套接字并将其连接到指定主机上的指定端口号,会连接远程主机,失败会抛异常。
Socket(InetAdress host, int port) //创建一个流套接字并将其连接到指定 IP 地址的指定端口号,会连接远程主机,失败会抛异常。
Socket() //创建Socket对象,先不连接。连接则调用connect()方法并传入InetSocketAddress对象。
//获取方法
InputStream getInputStream() //返回一个InputStream,用它从socket读取字节
OutputStream getOutputStream() //返回一个OutputStream,用它可以向socket的另一端写数据。
InetAddress getInetAddress() //获取远程主机地址,对应getLocalAddress()为本地主机。
int getPort() //获取远程主机连接的端口号,对应getLocalPort()方法获取本地连接的端口,通常本地连接端口由本地空闲端口中选择。
//判断方法
boolean isClosed() //判断Socket是否关闭。
//设置方法
setSoTimeout(int milliseconds) //设置连接超时时间,超时抛出异常。
setKeepLive(boolean on) //设置了SO_KEEPLIVE,客户端偶尔会发生空闲数据报检测服务器是否崩溃。
setTrafficClass(int trafficClass) //设置服务类型,业务流类型以0到255之间的int给出。不同的业务流可以适合不同的服务。
ServerSocket类
Socket类是不足以编写服务器的,因此JAVA提供一个ServerSocket类表示服务器Socket。每个ServerSocket监听服务器机器上一个特定端口,当有远程主机尝试连接该服务器的这个端口,服务器就被唤醒、协商建立客户端和服务器之间的连接,并返回一个Socket对象,表示两台主机之间的Socket。所以ServerSocket的作用就是绑定端口、监听入站数据、在绑定端口上监听来自远程机器的连接。而数据传输都是通过Socket类完成的。
??服务器工作流程就是使用ServerSocket构造函数创建一个ServerSocket对象,ServerSocket则使用其accept()方法监听这个端口入站连接。accept()方法会一直阻塞直至一个客户端尝试连接,此时accept()方法将返回连接客户端和服务器的Socket对象。之后便是利用Socket实例互相发送数据。
//构造方法
ServerSocket(int port) //创建绑定到特定端口的服务器套接字。
ServerSocket(int port, int queueLength) //创建指定端口的服务器套接字,并且指定入站连接请求所用的队列长度。
ServerSocket() //创建一个ServerSocket对象,但是不绑定端口因此不接收任何连接。之后可以使用bind(SocketAddress endpoint)或bind(SockerAddress endpoint, int queueLength)进行绑定。
//获取方法
InetAddress getInetAddress() //获取本地地址。
int getLocalPort() //返回所绑定的端口。
//设置方法
setSoTimeout(int timeout) //调用accept()时开始倒数、如果超时就抛异常。
setReuseAddress(boolean on) //确定了是否允许一个新的Socket绑定到之前使用过的一个端口。
setReceiveBufferSize(int size) //设置服务器Socket接收客户端Socket默认接收缓冲区大小。
setPerformancePreferences(int connectionTime, int latency, int bandwidth) //描述服务器所接受的Socket,为其连接时间、延迟和带宽给定的相应优先级。
DatagramPacket类
DatagramPacket表示的是UDP数据报包,此数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。
??这个类提供了一些方法来获取和设置IP首部的源或目标地址、获取和设置源或目标端口、获取和设置数据,以及获取和设置数据长度。其余首部字段则无法修改。
//接收数据报的构造函数
DatagramPacket(byte[] buffer, int length) //创建一个DatagramPacket对象接收数据,数据部分存储在buffer中,从buffer[0]开始存储直至存储完或者直至存储到了length字节。
DatagramPacket(byte[] buffer, int offset, int length) //创建DatagramPacket对象存储数据,但下标是从buffer[offset]开始存储。和上面一个方法一样数据长度不能超过buffer.length否则会抛异常。
//发送数据报的构造函数
DatagramPacket(byte[] data, int length, InetAddress destination, int port) //创建发往另一个主机的DatagramPacket对象,包的数据部分是用data从0到length的字节填充,length不能超过数组大小否则会抛异常。最后便是指定目标主机和端口号。
DatagramPacket(byte[] data, int offset, int length, SocketAddress destination) //同样创建发往另一个主机的DatagramPacket对象,数据部分数组的offset到lenght部分,目标地址由SocketAddress指定。
//获取方法
InetAddress getAddress() //返回远程主机地址,当数据报是发送报则返回目标地址,当数据报为接收报则返回发送方地址。
int getPort() //返回远程主机端口,指示的端口方和getAddress()方法一样
SocketAddress getSocketAddress() //返回一个SocketAddress对象,指示方和getAddress()方法一样。
byte[] getData() //获取数据报数据
//设置方法
setData(byte[] data) //改变UDP数据报的有效载荷,可以重复发送DatagramPacket对象而只改变数据。
setAddress(SocketAddress remote) //改变数据报要发往的端口和地址。
DatagramSocket类
要收发DatagramPacket就需要打开数据报Socket,JAVA中则使用DatagramSocket类创建和访问数据报Socket。服务器构造DatagramSocket时要指定它监听的本地端口。客户端和服务器使用的都是DatagramSocket对象,通常客户端使用匿名端口,而服务器使用的是已知端口。
//构造方法
DatagramSocket() //创建一个绑定到匿名端口的DatagramSocket对象,用于创建客户端Socket。
DatagramSocket(int port) //创建一个指定端口的DatagramSocket对象,用来创建已知端口监听的服务器Socket。
//常用方法
send(DatagramPacket dp) //使用该方法将数据包发送到远程主机
receive(DatagramPacket dp) //从网络接收一个数据报,并存储在指定的DatagramPacket中。该方法会阻塞当前线程,直到接收到数据报。
close() //释放该DatagramSocket占用的端口
connect(InetAddress host, int port)、disconnect() //指定远程收、发数据包的地址,对于其他主机将丢弃包。后面一个方法就是取消指定。
setSoTimeout(int timeout) //设置receive方法的超时时间。
以上是关于JAVA网络编程的主要内容,如果未能解决你的问题,请参考以下文章
VSCode自定义代码片段14——Vue的axios网络请求封装
VSCode自定义代码片段14——Vue的axios网络请求封装