前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >计算机网络基础知识点快速复习手册

计算机网络基础知识点快速复习手册

作者头像
蛮三刀酱
发布2019-03-26 11:43:39
5560
发布2019-03-26 11:43:39
举报
在这里插入图片描述
在这里插入图片描述

前言

本文快速回顾了计算机网络书本中常考的的知识点,用作面试复习,事半功倍。

主要内容有:计算机网络体系结构,TCP与UDP,UDP/TCP实现DEMO代码

-----正文开始-----

基础

计算机网络体系结构

在这里插入图片描述
在这里插入图片描述

1. 五层协议

  • 应用层为特定应用程序提供数据传输服务,例如 HTTP、DNS 等。数据单位为报文
  • 运输层 :提供的是进程间的通用数据传输服务。由于应用层协议很多,定义通用的运输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:
    • 传输控制协议TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;TCP 主要提供完整性服务.
    • 用户数据报协议UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。UDP 主要提供及时性服务。
  • 网络层为主机间提供数据传输服务,而运输层协议是为主机中的进程提供服务。网络层把运输层传递下来的报文段或者用户数据报封装成分组
  • 数据链路层:网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的节点提供服务。数据链路层把网络层传来的分组封装成帧
  • 物理层 :考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异

数据报->分组->帧->比特流

2. 七层协议

其中表示层和会话层用途如下:

  • 表示层 :数据压缩、加密以及数据描述。这使得应用程序不必担心在各台主机中表示/存储的内部格式不同的问题。
  • 会话层 :建立及管理会话。

五层协议没有表示层和会话层,而是将这些功能留给应用程序开发者处理。

3. 数据在各层之间的传递过程

在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。

路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要运输层和应用层。

4. TCP/IP

它只有四层,相当于五层协议中数据链路层和物理层合并为网络接口层。

现在的 TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层

TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中占用举足轻重的地位。

在这里插入图片描述
在这里插入图片描述

物理层/数据链路层/网络层

知识点偏通信理论的多一些,可以放在后面复习

传输层

TCP与UDP的特点

用户数据报协议 UDP(User Datagram Protocol):无连接的,尽最大可能交付,没有拥塞控制,面向报文

对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部,支持一对一、一对多、多对一和多对多的交互通信。

传输控制协议 TCP(Transmission Control Protocol)是有连接的,提供可靠交付,有流量控制,拥塞控制,面向字节流

把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块,每一条 TCP连接只能是点对点的(一对一)。

总结(TCP和UDP的区别):

1)TCP提供面向连接的传输;UDP提供无连接的传输

2)TCP提供可靠的传输(有序,无差错,不丢失,不重复);UDP提供不可靠的传输

3)TCP面向字节流的传输,因此它能将信息分割成组,并在接收端将其重组;UDP是面向数据报的传输,没有分组开销。

4)TCP提供拥塞控制和流量控制机制;UDP不提供拥塞控制和流量控制机制。

5)TCP只能是点对点的(一对一)。UDP支持一对一、一对多、多对一和多对多的交互通信。

首部格式

UDP

在这里插入图片描述
在这里插入图片描述

首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。

TCP

在这里插入图片描述
在这里插入图片描述
  • 序号 :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。
  • 确认号 :期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。
  • 数据偏移 :指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。
  • 确认 ACK :当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。
  • 同步 SYN :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。
  • 终止 FIN :用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。
  • 窗口 :窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。

TCP拆包粘包

如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况。

分包机制一般有两个通用的解决方法:

1,特殊字符控制

2,在包头首都添加数据包的长度

如果使用netty的话,就有专门的编码器和解码器解决拆包和粘包问题了。

tips:

UDP没有粘包问题,但是有丢包和乱序。不完整的包是不会有的,收到的都是完全正确的包。传送的数据单位协议是UDP报文或用户数据报,发送的时候既不合并,也不拆分。

三次握手四次挥手:

https://blog.csdn.net/qzcsu/article/details/72861891

三次握手

代码语言:javascript
复制
(1)第一步:源主机A的TCP向主机B发出连接请求报文段,其首部中的SYN(同步)标志位应置为1,表示想与目标主机B进行通信,**并发送一个同步序列号X(例:SEQ=100)进行同步,表明在后面传送数据时的第一个数据字节的序号是X+1(即101)**。SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号。

(2)第二步:目标主机B的TCP收到连接请求报文段后,如同意,则发回确认。在确认报中应将ACK位和SYN位置1,表示客户端的请求被接受。确认号应为X+1(图中为101),同时也为自己选择一个序号Y。

(3)第三步:源主机A的TCP收到目标主机B的确认后要向目标主机B给出确认,其ACK置1,确认号为Y+1,而自己的序号为X+1。**TCP的标准规定,SYN置1的报文段要消耗掉一个序号。**
  

运行客户进程的源主机A的TCP通知上层应用进程,连接已经建立。当源主机A向目标主机B发送第一个数据报文段时,**其序号仍为X+1,因为前一个确认报文段并不消耗序号。**
  

当运行服务进程的目标主机B的TCP收到源主机A的确认后,也通知其上层应用进程,连接已经建立。至此建立了一个全双工的连接。
在这里插入图片描述
在这里插入图片描述
三次握手的原因

如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

四次挥手

  1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
  2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
  3. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
  4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
  5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
  6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
在这里插入图片描述
在这里插入图片描述
等待 2MSL(最长报文段寿命) 的原因

书中解释:

在这里插入图片描述
在这里插入图片描述

TCP采用四次挥手关闭连接如图所示为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。

而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

Time-wait状态好处和坏处

好处

上方所述两点

坏处

高并发下,端口都处在timewait很快就用完端口。

解决方法:

  • (阿里手册)调小 TCP 协议的 time_ wait 超时时间。net . ipv 4. tcp _ fin _ timeout = 30
  • tcp_tw_reuse 这个参数作用是当新的连接进来的时候,可以复用处于TIME_WAIT的socket。默认值是0。
  • tcp_tw_recycle和tcp_timestamps 默认TIME_WAIT的超时时间是2倍的MSL,但是MSL一般会设置的非常长。如果tcp_timestamps是关闭的,开启tcp_tw_recycle是没用的。但是一般情况下tcp_timestamps是默认开启的,所以直接开启就有用了。

http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=28541347&id=5748888

  • 对于客户端 作为客户端因为有端口65535问题,TIME_OUT过多直接影响处理能力,打开tw_reuse 即可解决,不建议同时打开tw_recycle,帮助不大。
  • 对于服务端
    1. 打开tw_reuse无效
    2. 线上环境 tw_recycle 最好不要打开 服务器处于NAT 负载后,或者客户端处于NAT后(这是一定的事情,基本公司家庭网络都走NAT);公网服务打开就可能造成部分连接失败,内网的话到时可以视情况打开;像我所在公司对外服务都放在负载后面,负载会把timestamp 选项都给关闭,所以就算打开也不起作用。
    3. 服务器TIME_WAIT 高怎么办 不像客户端有端口限制,处理大量TIME_WAIT Linux已经优化很好了,每个处于TIME_WAIT 状态下连接内存消耗很少,而且也能通过tcp_max_tw_buckets = 262144 配置最大上限,现代机器一般也不缺这点内存。
    4. 高并发服务器建议调小 TCP 协议的 time_wait 超时时间。240s调整至30s(阿里Java规约)

什么情况下会出现RST包

看这篇就够了:https://blog.csdn.net/eric0318/article/details/51113018

TCP 滑动窗口

在这里插入图片描述
在这里插入图片描述

窗口是缓存的一部分,用来暂时存放字节流

发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。

发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。

  • 如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;
  • 接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。

接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {32, 33} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。

TCP 可靠传输(超时重传)

TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段。

一个报文段从发送再到接收到确认所经过的时间称为往返时间 RTT,加权平均往返时间 RTTs 计算如下:

image
image

超时时间 RTO 应该略大于 RTTs,TCP 使用的超时时间计算如下:

image
image

其中 RTTd 为偏差。

TCP 流量控制

流量控制是为了控制发送方发送速率,保证接收方来得及接收。

接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。

TCP 拥塞控制

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  • 拥塞控制主要包含以下2个内容: (1)慢开始,拥塞避免 (2)快重传,快恢复

发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。

为了便于讨论,做如下假设:

  • 接收方有足够大的接收缓存,因此不会发生流量控制;
  • 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。

1. 慢开始与拥塞避免

发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 …

注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。

如果出现了超时,则令 ssthresh = cwnd/2,然后重新执行慢开始。

2. 快重传与快恢复

在接收方,要求每次接收到报文段都应该发送对已收到有序报文段的确认,例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。

快重传

在发送方,如果收到三个重复确认,那么可以确认下一个报文段丢失,例如收到三个 M2 ,则 M3 丢失。此时执行快重传,立即重传下一个报文段。

快恢复

在快重传情况下,只是丢失个别报文段,而不是网络拥塞,因此执行快恢复,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。

慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。

UDP/TCP代码DEMO

经典:https://blog.csdn.net/column/details/socket.html

TCP:https://blog.csdn.net/ns_code/article/details/14105457

UDP:https://blog.csdn.net/ns_code/article/details/14128987

TCP

TCP连接的建立步骤

客户端向服务器端发送连接请求后,就被动地等待服务器的响应。典型的TCP客户端要经过下面三步操作:

  • 1、创建一个Socket实例:构造函数向指定的远程主机和端口建立一个TCP连接;
  • 2.通过套接字的I/O流与服务端通信;
  • 3、使用Socket类的close方法关闭连接。

服务端的工作是建立一个通信终端,并被动地等待客户端的连接。典型的TCP服务端执行如下两步操作:

  • 1、创建一个ServerSocket实例并指定本地端口,用来监听客户端在该端口发送的TCP连接请求;
  • 2、重复执行:
    • 1)调用ServerSocket的accept()方法以获取客户端连接,并通过其返回值创建一个Socket实例;
    • 2)为返回的Socket实例开启新的线程,并使用返回的Socket实例的I/O流与客户端通信;
    • 3)通信完成后,使用Socket类的close()方法关闭该客户端的套接字连接。
Demo

ServerDemo.java

代码语言:javascript
复制
/**
 * sinture.com Inc.
 * Copyright (c) 2016-2018 All Rights Reserved.
 */
package test.socketDemo.TCP;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author yzd
 * @version Id: ServerDemo.java, v 0.1 2018年07月10日 12:36 yzd Exp $
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 服务端在20006端口监听客户端请求的TCP连接
        ServerSocket server = new ServerSocket(20000);
        Socket client = null;
        boolean f = true;
        while(f){
            // 等待客户端的连接,如果没有获取连接
            client = server.accept();
            System.out.println("与客户端连接成功!");
            // 为每个客户端连接开启一个线程
            new Thread(new ServerThread(client)).start();

        }
    }
}

ServerThread.java

代码语言:javascript
复制
/**
 * sinture.com Inc.
 * Copyright (c) 2016-2018 All Rights Reserved.
 */
package test.socketDemo.TCP;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

/**
 * @author yzd
 * @version Id: ServerThread.java, v 0.1 2018年07月10日 13:41 yzd Exp $
 */
public class ServerThread implements Runnable {

    private Socket client = null;
    public ServerThread(Socket client){
        this.client = client;
    }

    @Override
    public void run() {
        try{
            //获取Socket的输出流,用来向客户端发送数据
            PrintStream out = new PrintStream(client.getOutputStream());
            //获取Socket的输入流,用来接收从客户端发送过来的数据
            BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
            boolean flag = true;
            while (flag){
                //接收从客户端发送过来的数据
                String str = buf.readLine();
                if(str == null || "".equals(str)){
                    flag = false;
                }else {
                    if("bye".equals(str)){
                        flag = false;
                    }else{
                        //将接收到的字符串前面加上echo,发送到对应的客户端
                        out.println("echo:" + str);
                    }
                }
            }
            out.close();
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ClientDemo.java

代码语言:javascript
复制
/**
 * sinture.com Inc.
 * Copyright (c) 2016-2018 All Rights Reserved.
 */
package test.socketDemo.TCP;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.SocketTimeoutException;

/**
 * @author yzd
 * @version Id: ClientDemo.java, v 0.1 2018年07月10日 14:05 yzd Exp $
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException {
        //客户端请求与本机在20006端口建立TCP连接
        Socket client = new Socket("127.0.0.1", 20000);
        client.setSoTimeout(10000);
        //获取键盘输入
        BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
        //获取Socket的输出流,用来发送数据到服务端
        PrintStream out = new PrintStream(client.getOutputStream());
        //获取Socket的输入流,用来接收从服务端发送过来的数据
        BufferedReader buf =  new BufferedReader(new InputStreamReader(client.getInputStream()));
        boolean flag = true;
        while(flag){
            System.out.print("输入信息:");
            String str = input.readLine();
            //发送数据到服务端
            out.println(str);
            if("bye".equals(str)){
                flag = false;
            }else{
                try{
                    //从服务器端接收数据有个时间限制(系统自设,也可以自己设置),超过了这个时间,便会抛出该异常
                    String echo = buf.readLine();
                    System.out.println(echo);
                }catch(SocketTimeoutException e){
                    System.out.println("Time out, No response");
                }
            }
        }
        input.close();
        if(client != null){
            //如果构造函数建立起了连接,则关闭套接字,如果没有建立起连接,自然不用关闭
            client.close();	//只关闭socket,其关联的输入输出流也会被关闭
        }

    }
}

UDP

UDP的通信建立的步骤

客户端要经过下面三步操作:

  • 1、创建一个DatagramSocket实例,可以有选择地对本地地址和端口号进行设置,如果设置了端口号,则客户端会在该端口号上监听从服务器端发送来的数据
  • 2、使用DatagramSocket实例的send()和receive()方法来发送和接收DatagramPacket实例,进行通信;
  • 3、通信完成后,调用DatagramSocket实例的close()方法来关闭该套接字。

UDP服务端要经过下面三步操作:

  • 1、创建一个DatagramSocket实例,指定本地端口号,并可以有选择地指定本地地址,此时,服务器已经准备好从任何客户端接收数据报文
  • 2、使用DatagramSocket实例的receive()方法接收一个DatagramPacket实例,当receive()方法返回时,数据报文就包含了客户端的地址,这样就知道了回复信息应该发送到什么地方;
  • 3、使用DatagramSocket实例的send()方法向服务器端返回DatagramPacket实例。
Demo

这里有一点需要注意:

UDP程序在receive()方法处阻塞,直到收到一个数据报文或等待超时。由于UDP协议是不可靠协议,如果数据报在传输过程中发生丢失,那么程序将会一直阻塞在receive()方法处,这样客户端将永远都接收不到服务器端发送回来的数据,但是又没有任何提示。为了避免这个问题,我们在客户端使用DatagramSocket类的setSoTimeout()方法来制定receive()方法的最长阻塞时间,并指定重发数据报的次数,如果每次阻塞都超时,并且重发次数达到了设置的上限,则关闭客户端。

ClientDemo.java

代码语言:javascript
复制
/**
 * sinture.com Inc.
 * Copyright (c) 2016-2018 All Rights Reserved.
 */
package test.socketDemo.UDP;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author yzd
 * @version Id: ClientDemo.java, v 0.1 2018年07月10日 14:46 yzd Exp $
 */
public class ClientDemo {
    public static final int TIMEOUT = 5000;
    public static final int MAXNUM = 5;

    public static void main(String[] args) throws IOException {
        String str_send = "Hello UDPServer";
        byte[] buf = new byte[1024];
        //客户端在9000端口监听接收到的数据
        DatagramSocket ds = new DatagramSocket(9000);
        InetAddress loc = InetAddress.getLocalHost();
        //定义用来发送数据的DatagramPacket实例
        DatagramPacket dp_send= new DatagramPacket(str_send.getBytes(),str_send.length(),loc,3000);
        //定义用来接收数据的DatagramPacket实例
        DatagramPacket dp_receive = new DatagramPacket(buf, 1024);
        //数据发向本地3000端口
        ds.setSoTimeout(TIMEOUT);               //设置接收数据时阻塞的最长时间
        int tries = 0;                         //重发数据的次数
        boolean receivedResponse = false;     //是否接收到数据的标志位
        //直到接收到数据,或者重发次数达到预定值,则退出循环
        while(!receivedResponse && tries<MAXNUM){
            //发送数据
            ds.send(dp_send);
            System.out.println("Client send message succeed.");
            try{
                //接收从服务端发送回来的数据
                ds.receive(dp_receive);
                //如果接收到的数据不是来自目标地址,则抛出异常
                if(!dp_receive.getAddress().equals(loc)){
                    throw new IOException("Received packet from an unknown source");
                }
                //如果接收到数据。则将receivedResponse标志位改为true,从而退出循环
                receivedResponse = true;
            }catch(InterruptedIOException e){
                //如果接收数据时阻塞超时,重发并减少一次重发的次数
                tries += 1;
                System.out.println("Time out," + (MAXNUM - tries) + " more tries..." );
            }
        }
        if(receivedResponse){
            System.out.println("client received data from server:");
            String str_receive = new String(dp_receive.getData(),0,dp_receive.getLength()) +
                    " from " + dp_receive.getAddress().getHostAddress() + ":" + dp_receive.getPort();
            System.out.println(str_receive);
            //由于dp_receive在接收了数据之后,其内部消息长度值会变为实际接收的消息的字节数,
            //所以这里要将dp_receive的内部消息长度重新置为1024
            dp_receive.setLength(1024);
        }else{
            //如果重发MAXNUM次数据后,仍未获得服务器发送回来的数据,则打印如下信息
            System.out.println("No response -- give up.");
        }
        ds.close();
    }
}

ServerDemo.java

代码语言:javascript
复制
/**
 * sinture.com Inc.
 * Copyright (c) 2016-2018 All Rights Reserved.
 */
package test.socketDemo.UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * @author yzd
 * @version Id: ServerDemo.java, v 0.1 2018年07月10日 15:12 yzd Exp $
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        String str_send = "Hello UDPclient";
        byte[] buf = new byte[1024];
        //服务端在3000端口监听接收到的数据
        DatagramSocket ds = new DatagramSocket(3000);
        //接收从客户端发送过来的数据
        DatagramPacket dp_receive = new DatagramPacket(buf, 1024);
        System.out.println("Server is on,Waiting for client to send data......");
        boolean f = true;
        while(f){
            //服务器端接收来自客户端的数据
            ds.receive(dp_receive);
            System.out.println("Server received data from client:");
            String str_receive = new String(dp_receive.getData(),0,dp_receive.getLength()) +
                    " from " + dp_receive.getAddress().getHostAddress() + ":" + dp_receive.getPort();
            System.out.println(str_receive);
            //数据发动到客户端的3000端口
            DatagramPacket dp_send= new DatagramPacket(str_send.getBytes(),str_send.length(),dp_receive.getAddress(),9000);
            ds.send(dp_send);
            System.out.println("Server send message succeed.");
            //由于dp_receive在接收了数据之后,其内部消息长度值会变为实际接收的消息的字节数,
            //所以这里要将dp_receive的内部消息长度重新置为1024
            dp_receive.setLength(1024);
        }
        ds.close();
    }
}

应用层

浏览器从输入URL地址到最终显示内容的过程

DNS查找对应ip过程

首先查找浏览器自身的DNS缓存,如果有这个域名映射且没过期(TTL)则直接向该IP发送HTTP请求,否则下一步

查找本地操作系统hosts缓存,如果有且没过期,拿出来使用完成DNS解析,否则下一步

查找本地DNS域名服务器

如果不可以由该服务器解析,则把请求发至根域名服务器,解析该域名是由谁来授权管理,返回顶级域名服务器的IP地址

本地DNS服务器联系顶级域名服务器。

顶级域名服务器如果无法解析,则找下一级DNS服务器,并把IP发给本地DNS服务器。

以此类推,在DNS域名解析的过程中,使用UDP协议进行不可靠传输,不需要三次握手,传输需要的内容较少,使用UDP更快。

在网页开发过程中尽量减少对DNS域名的解析,天猫,淘宝等使用进行dns延迟缓存

https://www.cnblogs.com/sjm19910902/p/6423181.html

HTTP请求过程

  1. 建立TCP连接
  2. 发送请求 一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET/sample/hello.jsp HTTP/1.1。
  3. 发送请求头信息 浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送
  4. 服务器应答 客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。
  5. 服务器发送应答头信息 正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。
  6. 服务器向浏览器发送数据 Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。

*7. Web服务器关闭TCP连接

一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

如果是第一次访问请求该网址

浏览器发送HTTP请求,请求头包括:

  • 请求方法(Request Method)
  • 协议版本
  • 客户端信息(User-Agent)
  • connect
  • 请求内容等
  • host

如果顺利访问:客户端返回200状态码

返回信息包括:

  • 返回内容
  • expires设置缓存过期时间
  • contentType返回内容类型
  • contentLength
  • status
  • Etage该缓存的版本号
  • contentEncoding
  • Date
  • cache-control
  • set-cookie设置本域名下浏览器的cookie
  • lastModified

如果是第二次

浏览器则发出http请求时

  • 带上cookie发送
  • if-Modified-since(匹配前一次请求时返回的last-modified)
  • if-None-match(匹配前一次请求时返回的Etag)
  • 如果资源没有被修改则返回304状态码。

但是如果前一次请求浏览器设置expires,则浏览器首先会检查缓存中的资源,如果在设置的expires时间之内则不会再次发送请求。

lastModified代表服务器最后修改时间,精确到秒。expires资源过期时间,精确到秒。Etag则代表资源的版本号,每次修改资源Etag就会变。不同资源的Etag不同。

如果正确访问

浏览器根据返回content-type,解析服务器返回的数据

  • 浏览器解析html文件时,每次遇到frame、img、link、javascript都会重新发送一个http请求
  • javascript下载完后就会立即执行阻塞浏览器的渲染以及绘制
  • 所以一般js链接放在最后,但是很多浏览器都会优先下载js文件和css文件,所以如果js没有对dom操作,尽量defer延迟加载js文件
  • css在文档头,防止因为css样式改变导致浏览器多次重绘或者回流,是页面闪动卡顿。

js和css尽量使用外链形式,减少DOM结构的长度和复杂度,减少浏览器解析html文件的时间。

dom节点尽量别深度嵌套,css少使用多层选择器。

页面减少http请求的个数,多个图片使用图片dataURI编码或则图片精灵进行合并、css文件压缩合并、js文件压缩合并。配置localhost之后就不会走dns了

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年02月18日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • -----正文开始-----
  • 基础
    • 计算机网络体系结构
      • 1. 五层协议
      • 2. 七层协议
      • 3. 数据在各层之间的传递过程
      • 4. TCP/IP
  • 物理层/数据链路层/网络层
  • 传输层
    • TCP与UDP的特点
      • 总结(TCP和UDP的区别):
    • 首部格式
      • UDP
      • TCP
    • TCP拆包粘包
      • 三次握手四次挥手:
        • 三次握手
        • 四次挥手
        • TCP采用四次挥手关闭连接如图所示为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
        • 如果已经建立了连接,但是客户端突然出现故障了怎么办?
        • Time-wait状态好处和坏处
        • 什么情况下会出现RST包
      • TCP 滑动窗口
        • TCP 可靠传输(超时重传)
          • TCP 流量控制
            • TCP 拥塞控制
              • 1. 慢开始与拥塞避免
              • 2. 快重传与快恢复
            • UDP/TCP代码DEMO
              • TCP
              • UDP
          • 应用层
            • 浏览器从输入URL地址到最终显示内容的过程
              • DNS查找对应ip过程
              • HTTP请求过程
              • 如果是第一次访问请求该网址
              • 如果是第二次
              • 如果正确访问
          相关产品与服务
          数据传输服务
          腾讯云数据传输服务(Data Transfer Service,DTS)可帮助用户在业务不停服的前提下轻松完成数据库迁移上云,利用实时同步通道轻松构建高可用的数据库多活架构,通过数据订阅来满足商业数据挖掘、业务异步解耦等场景需求。同时,DTS 还提供私有化独立输出版本 DTS-DBbridge,支持异构数据库和同构数据库之间迁移和同步,可以帮助企业实现完整数据库迁移(如 Oracle)。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档