最近阿粉的同事们在准备面试,其中也有收到offer的几个不错的人,毕竟疫情稳定了,而阿粉在电话面试的时候,被问到关于HTTP协议的内容的时候,却显得有点麻木了,为什么呢?因为套路太深了,让阿粉猝不及防呀。
面试官:你了解TCP/IP协议么?说实话在阿粉听到这个问题的时候,阿粉的第一想法就是,我回答了这个问题,接下来肯定还有一个三次握手和四次挥手等着我,但是还是得回答呀,于是阿粉就开始作答了。
阿粉开始作答:TCP/IP协议虽然会放在一起说,但是他们其实呢是属于两个不同的协议。
面试官:那你说说什么是三次握手,什么是四次挥手吧
1. 三次握手
大家看这个图,图是来自于百度搜索,而且百度上有各种各样的图,当你看到图的时候第一时间肯定是看不懂的,也就是只能通过这个画的标志的“线”来进行分析,其实这仅仅只是一个方面。
那么我们就来根据图来解析一下这个图中都代表了什么意思,图中存在着两个序号和三个不同的标志位其中有大小写容易混淆的呦。
序号:
那么关于这个图,我们怎么给面试官说呢?
(1) 第一次握手(SYN=1, seq=x):
客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。
发送完毕后,客户端进入 SYN_SEND 状态。
(2) 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。发送完毕后,服务器端进入 SYN_RCVD 状态。
(3) 第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1
发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束。
你如果这么说,面试官有可能还会问,你这也太官方了,能不能说说你的理解,那么你可以用一个实际上的例子来给他说一下,
阿粉:鸡丁,嘿,我是阿粉,你听的到我说话么?
鸡丁:吵吵啥,听到了,除了你我还能认识谁。
阿粉:你听的到你还不赶紧回复,怪不得你没有女朋友呢。那我们再继续交流一下吧。
而这三次的对话过程就是通俗的三次握手,期间对话三次,以此来确定两个方向上的数据传输通道是否正常。
2. 四次挥手
那么四次挥手怎么来回答呢?
(1)第一次挥手(FIN=1,seq=x)
假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。
发送完毕后,客户端进入 FIN_WAIT_1 状态。
(2) 第二次挥手(ACK=1,ACKnum=x+1)
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。
发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。
(3) 第三次挥手(FIN=1,seq=y)
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。
(4) 第四次挥手(ACK=1,ACKnum=y+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。两次后会重传直到超时。如果多了会有大量半链接阻塞队列。
那么怎么去通俗的给面试官说呢?
阿粉:鸡丁呀,我要说的都说完了,你还有啥事么?
鸡丁:你说的我都明白了,但是别断,我还有要嘱咐你的,给我找女朋友的事情。
鸡丁:xxxxx,我说完了。
阿粉,行啦,别BB了,记住了,挂了把。
如果面试官问你的时候,你这么回答的话,既有官方的解释,还有本身自己的理解,那么这个问题就已经算是差不多了,
而面试官显然不可能会这么放过你,肯定再给你来个雷,为啥是三次握手,而是四次挥手呢?为啥不是三次呢?
这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方ACK和FIN一般都会分开发送。所以这时候挥手的时候就是四次,而不再是三次了。
那么我们怎么去手写一个HTTP协议呢?代码送上:
- public class Server {
- public static void main(String[] args) throws Exception{
- ServerSocketChannel ssc = ServerSocketChannel.open();
- ssc.socket().bind(new InetSocketAddress(8080));
- ssc.configureBlocking(false);
- Selector selector = Selector.open();
- ssc.register(selector, SelectionKey.OP_ACCEPT);
- while (true){
- if (selector.select(3000)==0){
- continue;
- }
- Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
- while (keyIterator.hasNext()){
- SelectionKey key = keyIterator.next();
- new Thread(new HttpHandler(key)).run();
- keyIterator.remove();
- }
- }
- }
- private static class HttpHandler implements Runnable{
- private int bufferSize = 1024;
- private String localCharset = "UTF-8";
- private SelectionKey key;
- public HttpHandler(SelectionKey key){
- this.key=key;
- }
- public void handleAccept()throws IOException{
- SocketChannel clientChannel = ((ServerSocketChannel)key.channel()).accept();
- clientChannel.configureBlocking(false);
- clientChannel.register(key.selector(),SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
- }
- @Override
- public void run() {
- try {
- if (key.isAcceptable()){
- handleAccept();
- }
- if (key.isReadable()){
- handleRead();
- }
- }catch (IOException e){
- e.printStackTrace();
- }
- }
- public void handleRead() throws IOException{
- SocketChannel sc = (SocketChannel) key.channel();
- ByteBuffer buffer = (ByteBuffer) key.attachment();
- buffer.clear();
- if (sc.read(buffer)==-1){
- sc.close();
- }else {
- buffer.flip();
- String receiveString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();
- String[] requestMessage = receiveString.split("\r\n");
- for (String s:requestMessage) {
- System.out.println(s);
- if (s.isEmpty()){
- break;
- }
- String[] firstLine = requestMessage[0].split(" ");
- System.out.println();
- System.out.println("Method:\t"+firstLine[0]);
- System.out.println("url:\t"+firstLine[1]);
- System.out.println("HTTP Version:\t"+firstLine[2]);
- System.out.println();
- StringBuffer sendString = new StringBuffer();
- sendString.append("HTTP/1.1 200 OK\r\n");
- sendString.append("Content-Type:text/html;charset="+localCharset+"\r\n");
- sendString.append("\r\n");
- sendString.append("<html><head><title>显示报文</title></head><body>");
- sendString.append("接受到的请求报文是:<br/>");
- for (String s1:requestMessage) {
- sendString.append(s1+"<br/>");
- }
- sendString.append("</body></html>");
- buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset));
- sc.write(buffer);
- sc.close();
- }
- }
- }
- }
- }
这是一个简单的实现,只是实现思路,并不是真正的处理请求,而大家也要注意设置Content-Type的类型,不然容易出问题的,毕竟长度是有限制的。
今天,V2EX上一篇名为《QQ 正在尝试读取你的浏览记录》的文章突然爆发。 帖子内...
阿尔卡特朗讯企业(Alcatel-Lucent Enterprise)英国医疗保健部门负责人尼尔汉弗莱...
自动化决策工具在组织的应用中正变得越来越普遍。然而,其背后的一些机器学习(ML...
众所周知,从上半年开始,就一直传出5G基站太耗电,富可敌国的运营商也交不起电...
尽管经历了艰难的一年,但世界各地的许多公司已经开始加速使用人工智能(AI)来最...
中国已经开始逐步进入万物互联的社会。在2G时代我们通过电脑和世界相联,在3G和4...
手机QQ最近更新的比较频繁,这不新版又来了,这次上线了一个面对面加好友功能,...
本文转载自微信公众号「悲了伤的白犀牛」,作者悲了伤的白犀牛。转载本文请联系...
作为新一代通信技术及新型基础设施核心,5G的发展备受瞩目,受到了全球各国的一...
人工智能似乎正在取得巨大进步。它已经成为自动驾驶汽车、自动翻译系统、语音和...