1. URL编程
1.1 URL
- URL(Uniform Resource Locator):统一资源定位符,它表示 Internet 上某一资源的地址。
- 它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
- 通过 URL 我们可以访问 Internet 上的各种网络资源,比如最常见的 www, ftp站点。浏览器通过解析给定的 URL 可以在网络上查找相应的文件或其他资源。
- URL的基本结构由5部分组成:<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
- 例如: http://192.168.1.100:8080/helloworld/index.jsp#a?username=shkstart&password=123
- 为了表示URL, java.net 中实现了类 URL。我们可以通过下面的构造器来初始化一个 URL 对象:
public URL (String spec)
:通过一个表示URL地址的字符串可以构造一个URL对象。例如: URL url = new URL (“http://www. baidu.com/”);- public URL(URL context, String spec):通过基 URL 和相对 URL 构造一个 URL 对象。例如: URL downloadUrl = new URL(url, “download.html")
- public URL(String protocol, String host, String file); 例如: new URL(“http”,“www.baidu.com”, “download. html");
- public URL(String protocol, String host, int port, String file); 例如: URL gamelan = new URL(“http”, “www.baidu.com”, 80, “download.html");
- 一个URL对象生成后,其属性是不能被改变的,但可以通过它给定的方法来获取这些属性:
public String getProtocol( )
获取该URL的协议名public String getHost( )
获取该URL的主机名public String getPort( )
获取该URL的端口号public String getPath( )
获取该URL的文件路径public String getFile( )
获取该URL的文件名public String getQuery( )
获取该URL的查询名
1.2 URLConnection类
- URLConnection:表示到URL所引用的远程对象的连接。当与一个URL建立连接时,首先要在一个 URL 对象上通过方法 openConnection() 生成对应的 URLConnection对象。如果连接过程失败,将产生IOException.
- URL netchinaren = new URL (“http://www.baidu.com/index.shtml”);
- URLConnectonn u = netchinaren.openConnection( );
- 通过URLConnection对象获取的输入流和输出流,即可以与现有的CGI程序进行交互。
public Object getContent( ) throws IOException
- public int getContentLength( )
- public String getContentType( )
- public long getDate( )
- public long getLastModified( )
public InputStream getInputStream( )throws IOException
public OutputSteram getOutputStream( )throws IOException
1.3 URI、 URL和URN的区别
- URI是uniform resource identifier,统一资源标识符, 用来唯一的标识一个资源。
- URL是uniform resource locator,统一资源定位符,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
- URN, uniform resource name,统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com。也就是说, URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。 URL和URN都是一种URI。
- 在Java的URI中,一个URI实例可以代表绝对的,也可以是相对的,只要它符合URI的语法规则。而URL类则不仅符合语义,还包含了定位该资源的信息,因此它不能是相对的。
2. TCP协议的报文格式
-
源端⼝号
源端⼝号表示报⽂的发送端⼝,占16位。源端⼝和源IP地址组合起来,可以标识报⽂的发送地址 -
⽬的端⼝号
⽬的端⼝号表示报⽂的接收端⼝,占16位。⽬的端⼝和⽬的IP地址相结合,可以标识报⽂的接收地址。
TCP协议是基于IP协议的基础上传输的, TCP报⽂中的源端⼝号+源IP,与TCP报⽂中的⽬的端⼝号+⽬的IP⼀起,组合起来唯⼀性的确定⼀条TCP连接。 -
序号(Sequence Number)
TCP传输过程中,在发送端出的字节流中,传输报⽂中的数据部分的每⼀个字节都有它的编号。序号(Sequence Number)占32位,发起⽅发送数据时,都需要标记序号。
序号(Sequence Number)的语义与SYN控制标志(Control Bits)的值有关。根据控制标志
(Control Bits)中的SYN是否为1,序号(Sequence Number)表达不同的含义:- 当SYN = 1时,当前为连接建⽴阶段,此时的序号为初始序号ISN((Initial Sequence Number),通过算法来随机⽣成序号;
- 当SYN = 0时在数据传输正式开始时,第⼀个报⽂的序号为 ISN + 1,后⾯的报⽂的序号,为前⼀个报⽂的SN值+TCP报⽂的净荷字节数(不包含TCP头)。⽐如,如果发送端发送的⼀个TCP帧的净荷为12byte,序号为5,则发送端接着发送的下⼀个数据包的时候,序号的值应该设置为5+12=17。
- 在数据传输过程中, TCP协议通过序号(Sequence Number)对上层提供有序的数据流。发送端可以⽤序号来跟踪发送的数据量;接收端可以⽤序号识别出重复接收到的TCP包,从⽽丢弃重复包;对于乱序的数据包,接收端也可以依靠序号对其进⾏排序。
-
确认序号(Acknowledgment Number)
确认序号(Acknowledgment Number)标识了报⽂接收端期望接收的字节序列。如果设置了ACK
控制位,确认序号的值表示⼀个准备接收的包的序列码,注意,它所指向的是准备接收的包,也就是下⼀个期望接收的包的序列码。 -
头部⻓度
该字段占⽤4位,⽤来表示TCP报⽂⾸部的⻓度,单位是4bit位。其值所表示的并不是字节数,⽽是头部的所含有的32bit的数⽬(或者倍数),或者4个字节的倍数,所以TCP头部最多可以有60(4*15=60)。没有任何选项字段的TCP头部⻓度为20字节,所以其头部⻓度为5,可以通过20/4=5计算得到。 -
保留字段
头部⻓度后⾯预留的字段⻓度为6位,作为保留字段,暂时没有什么⽤处。 -
控制标志 (Control Bits)
共6个bit位,具体的标志位为: URG、 ACK、 PSH、 RST、SYN、 FIN。 6个标志位的说明,如
在连接建⽴的三次握⼿过程中,若只是单个SYN置位,表示的只是建⽴连接请求。如
果SYN和ACK同时置位为1,表示的建⽴连接之后的响应。 -
检验和
⻓度为16位,共2个字节。对整个TCP报⽂段,即TCP头部和TCP数据进⾏校验和计算,接收端⽤
于对收到的数据包进⾏验证。 -
紧急指针
⻓度为16位, 2个字节。它是⼀个偏移量,和SN序号值相加表示紧急数据最后⼀个字节的序号。
以上⼗项内容是TCP报⽂⾸部必须的字段,也称固有字段,⻓度为20个字节。接下来是TCP报⽂的可选项和填充部分。 -
选项和填充
可选项和填充部分的⻓度为4n字节(n是整数),该部分是根据需要⽽增加的选项。如果不⾜4n字节,要加填充位,使得选项⻓度为32位(4字节)的整数倍,
3. Tcp三次握手
TCP连接的建⽴时,双⽅需要经过三次握⼿,具体过程如下:
- 第⼀次握⼿:Client进⼊SYN_SENT状态,发送⼀个SYN帧来主动打开传输通道,该帧的SYN标志位被设置为1,同时会带上Client分配好的SN序列号,该SN是根据时间产⽣的⼀个随机值,通常情况下每间隔4ms会加1。除此之外,SYN帧还会带⼀个MSS(最⼤报⽂段⻓度)可选项的值,表示客户端发送出去的最⼤数据块的⻓度。
- 第⼆次握⼿:Server端在收到SYN帧之后,会进⼊SYN_RCVD状态,同时返回SYN+ACK帧给Client,主要⽬的在于通知Client,Server端已经收到SYN消息,现在需要进⾏确认。Server端发出的SYN+ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Client的SN+1;SYN+ACK帧的SYN标志位被设置为1,SN值为Server端⽣成的SN序号;SYN+ACK帧的MSS(最⼤报⽂段⻓度)表示的是Server端的最⼤数据块⻓度。
- 第三次握⼿:Client在收到Server的第⼆次握⼿SYN+ACK确认帧之后⾸先将⾃⼰的状态会从SYN_SENT变成ESTABLISHED,表示⾃⼰⽅向的连接通道已经建⽴成功,Client可以发送数据给Server端了。然后,Client发ACK帧给Server端,该ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Server端的SN序列号+1。还有⼀种情况,Client可能会将ACK帧和第⼀帧要发送的数据,合并到⼀起发送给Server端。
- Server端在收到Client的ACK帧之后,会从SYN_RCVD状态会进⼊ESTABLISHED状态,⾄此,Server⽅向的通道连接建⽴成功,Server可以发送数据给Client,TCP的全双⼯连接建⽴完成。
4. Tcp四次挥手
- 第⼀次挥⼿:主动断开⽅(可以是客户端,也可以是服务器端),向对⽅发送⼀个FIN结束请求报⽂,此报⽂的FIN位被设置为1,并且正确设置Sequence Number(序列号)和Acknowledgment Number(确认号)。发送完成后,主动断开⽅进⼊FIN_WAIT_1状态,这表示主动断开⽅没有业务数据要发送给对⽅,准备关闭SOCKET连接了。
- 第⼆次挥⼿:正常情况下,在收到了主动断开⽅发送的FIN断开请求报⽂后,被动断开⽅会发送⼀个ACK响应报⽂,报⽂的Acknowledgment Number(确认号)值为断开请求报⽂的Sequence Number (序列号)加1,该ACK确认报⽂的含义是:“我同意你的连接断开请求”。之后,被动断开⽅就进⼊了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知⾼层的应⽤进程,对⽅向本地⽅向的连接已经关闭,对⽅已经没有数据要发送了,若本地还要发送数据给对⽅,对⽅依然会接受。被动断开⽅的CLOSE-WAIT(关闭等待)还要持续⼀段时间,也就是整个CLOSE-WAIT状态持续的时间。主动断开⽅在收到了ACK报⽂后,由FIN_WAIT_1转换成FIN_WAIT_2状态。
- 第三次挥⼿:在发送完成ACK报⽂后,被动断开⽅还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截⽌后,被动断开⽅会向主动断开⽅发送⼀个FIN+ACK结束响应报⽂,表示被动断开⽅的数据都发送完了,然后,被动断开⽅进⼊LAST_ACK状态。
- 第四次挥⼿:主动断开⽅收在到FIN+ACK断开响应报⽂后,还需要进⾏最后的确认,向被动断开⽅发送⼀个ACK确认报⽂,然后,⾃⼰就进⼊TIME_WAIT状态,等待超时后最终关闭连接。处于TIME_WAIT状态的动断开⽅,**在等待完成2MSL的时间后,如果期间没有收到其他报⽂,则证明对⽅已正常关闭,主动断开⽅的连接最终关闭。被动断开⽅在收到主动断开⽅的最后的ACK报⽂以后,最终关闭了连接,⾃⼰啥也不管了。**四次挥⼿的全部交互过程,具体如下图所示:
5.面试题
- 为什么关闭连接需要四次挥手,而建立连接需要三次握手
- 关闭连接时,被动断开⽅在收到对⽅的FIN结束请求报⽂时,很可能业务数据没有发送完成,并不能⽴即关闭连接,被动⽅只能先回复⼀个ACK响应报⽂,当数据发送完时,被动方会发送FIN+ACK报⽂,被动断开⽅的确认报⽂,需要拆开成为两步,故总体就需要四步挥⼿。
- ⽽在建⽴连接中,当Server端收到Client端的SYN连接请求报⽂后,不需要传输数据,所以只需要三次握⼿即可。
- 为什么连接建⽴的时候是三次握⼿,可以改成两次握⼿吗?
- 不可以,如果把三次握⼿改成两次握⼿,可能发⽣死锁。
- 假设TCP建⽴的连接时⼆次握⼿,Server发送了SYN+ACK帧。按照两次握⼿的协定,Server认为连接已经成功地建⽴了,可以开始发送数据帧。
- 但是如果SYN+ACK帧在传输中被丢失,Client没有收到,会⼀直等待Server的SYN+ACK确认应答帧。⽽Server⼀直没有收到Client数据,这样就形成了死锁。
- 为什么主动断开⽅在TIME-WAIT状态必须等待2MSL的时间?
- 主动断开⽅等待2MSL的时间,是为了确保两端都能最终关闭。
- 假设⽹络是不可靠的,被动断开⽅发送FIN+ACK报⽂后,其主动⽅的ACK响应报⽂有可能丢失,这时候的被动断开收不到ACK,而不能进入CLOSED状态。
- 在这种场景下,被动断开⽅会超时重传FIN+ACK断开响应报⽂。
- 如果主动断开⽅在2MSL时间内,收到这个重传的FIN+ACK报⽂,会重传⼀次ACK报⽂,后再⼀次重新启动2MSL计时等待,这样,就能确保被动断开⽅能收到ACK报⽂,能确保被动⽅顺利进⼊到CLOSED状态。
- 如果主动断开⽅在发送完ACK响应报⽂后⽴即释放连接,则将⽆法收到被动⽅重传的FIN+ACK报⽂,不会发送ACK确认报⽂,被动断开⽅就⽆法进⼊CLOSED状态。
- 如果已经建⽴了连接,但是Client端突然出现故障了怎么办?
- TCP有⼀个保活计时器,Client端如果出现故障,Server端不能⼀直等下去,每收到⼀次Client客户端的数据帧后,Server端都的保活计时器会复位。
- 若2⼩时还没有收到Client端的任何数据帧,Server端就会发送⼀个探测报⽂段,以后每隔75秒钟发送⼀次。若⼀连发送10个探测报⽂仍然没反应,
- Server端就认为Client端出了故障,接着就关闭连接。