博客
关于我
Java Socket网络编程-总结
阅读量:361 次
发布时间:2019-03-04

本文共 13070 字,大约阅读时间需要 43 分钟。

Java Socket网络编程


如想了解更多更全面的Java必备内容可以阅读:所有JAVA必备知识点面试题文章目录:


1、什么是网络编程?

网络编程: 主要是在发送端将信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。

在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称作客户端(Client),等待其他程序连接的程序被称作服务器(Server)。客户端程序可以在需要的时候启动,而服务器为了能够时刻相应连接,则需要一直启动。

2、IP地址与域名?

是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。

由于IP地址不方便记忆,所以有专门创造了域名(Domain Name)的概念,来将域名和IP地址相互映射,例如163.com、sina.com等。
其实在网络中只能使用IP地址进行数据传输,所以在传输以前,需要把域名转换为IP,这个由称作DNS的服务器专门来完成。
所以在网络编程中,可以使用IP或域名来标识网络上的一台设备。

3、端口号?

为了在一台设备上可以运行多个程序,人为的设计了端口(Port)的概念,端口号只有整数,范围是从0 到65535(2^16-1),每个端口对应一个唯一的程序。

每个网络程序,无论是客户端还是服务器端,都对应一个或多个特定的端口号。由于0-1023一般被用作知名服务器的端口如FTP、HTTP、SMTP等,所以实际编程时一般采用大于 1024 以上的端口号。
使用端口号,可以找到一台设备上唯一的一个程序。所以如果需要和某台计算机建立连接的话,只需要知道IP地址或域名即可,但是如果想和该台计算机上的某个程序交换数据的话,还必须知道该程序使用的端口号。

4、DNS详细解析过程?

域名系统DNS (Domain Name System)是因特网使用的命名系统,是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工作。

DNS解析过程:

在这里插入图片描述

  1. 检查浏览器缓存中是否缓存过该域名对应的IP地址。
    用户通过浏览器浏览过某网站之后,浏览器就会自动缓存该网站域名对应的IP地址,当用户再次访问的时候,浏览器就会从缓存中查找该域名对应的IP地址,因为缓存不仅是有大小限制,而且还有时间限制(域名被缓存的时间通过TTL属性来设置)所以存在域名对应的IP找不到的情况。当浏览器从缓存中找到了该网站域名对应的IP地址,那么整个DNS解析过程结束,如果没有找到,将进行下一步骤。
  2. 如果浏览器缓存中没有,如果在浏览器缓存中没有找到IP,那么将继续查找本机系统是否缓存过IP。
    如果第一个步骤没有完成对域名的解析过程,那么浏览器会去系统缓存中查找系统是否缓存过这个域名对应的IP地址,也可以理解为系统自己也具备域名解析的基本能力。在Windows系统中,可以通过设置hosts文件来将域名手动绑定到某IP上,hosts文件位置在C:/Windows/System32/drivers/etc/hosts。对于普通用户,并不推荐自己手动绑定域名和IP,对于开发者来说,通过绑定域名和IP,可以轻松切换环境,可以从测试环境切换到开发环境,方便开发和测试。在XP系统中,黑客常常修改他的电脑的hosts文件,将用户常常访问的域名绑定到他指定的IP上,从而实现了本地DNS解析,导致这些域名被劫持。在Linux或者Mac系统中,hosts文件在/etc/hosts,修改该文件也可以实现同样的目的。
  3. 如果至此还没有命中域名,才会真正的请求本地域名服务器(LDNS)来解析这个域名。大约80%的域名解析到这里就完成了。
    如果在本机上无法完成域名的解析,那么系统只能请求本地域名解析服务系统进行解析,本地域名系统LDNS一般都是本地区的域名服务器,比如你连接的校园网,那么域名解析系统就在你的校园机房里,如果你连接的是电信、移动或者联通的网络,那么本地域名解析服务器就在本地区,由各自的运营商来提供服务。对于本地DNS服务器地址,Windows系统使用命令ipconfig就可以查看,在Linux和Mac系统下,直接使用命令cat /etc/resolv.conf来查看LDNS服务地址。LDNS一般都缓存了大部分的域名解析的结果,当然缓存时间也受域名失效时间控制,大部分的解析工作到这里就差不多已经结束了,LDNS负责了大部分的解析工作。
  4. 如果LDNS仍然没有命中,向根域名解析服务器(Root Server DNS)发起域名解析请求。
    LDNS域名解析器还没有完成解析的话,那么本地域名解析服务器(LDNS)将向根域名服务器(Root Server DNS)发起解析请求。
  5. 根域名服务器返回顶级域名服务器(gTLD Server)地址。
    根域名服务器返回的是所查域的通用顶级域(Generic top-level domain,gTLD)地址,常见的通用顶级域有.com、.cn、.org、.edu等。
  6. LDNS向顶级域名服务器(gTLD Server)发起解析请求。
    本地域名解析服务器(LDNS)向顶级域名服务器(gTLD Server)发起请求。
  7. gTLD服务器接收请求并返回Name Server服务器,这个Name Server就是网站注册的域名服务器。
    gTLD服务器接收本地域名服务器发起的请求,并根据需要解析的域名,找到该域名对应的Name Server域名服务器,通常情况下,这个Name Server服务器就是你注册的域名服务器,那么你注册的域名的服务商的服务器将承担起域名解析的任务。
  8. 向Name Server服务器发送请求,Name Server根据映射关系表找到目标IP,并返回给LDNS。
    Name Server服务器查找域名对应的IP地址,将IP地址连同TTL值返回给本地域名服务器。
  9. LDNS缓存这个域名和对应的IP地址。
    本地域名服务器缓存解析后的结果,缓存时间由TTL时间来控制。
  10. LDNS把解析的结果返回给用户,用户根据TTL值缓存到本地系统缓存中,域名解析过程至此结束。
    解析结果将直接返回给用户,用户系统将缓存该IP地址,缓存时间由TTL来控制,至此,解析过程结束。

5、网络的分层结构和一些基本协议?

结构及协议 详细版图一:

在这里插入图片描述
结构及协议 简化版图二:
在这里插入图片描述
结构及协议 简化版图三:
在这里插入图片描述
例:HTTP请求 对应网络模型结构及协议 简化版图四:
在这里插入图片描述

6、什么是Socket?

Socket 通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket,一个Socket由一个IP地址和一个端口号唯一确定。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。 Socket是TCP/IP协议的一个十分流行的编程界面,但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。

Socket的基本工作过程包含以下四个步骤:

  • 创建Socket;
  • 打开连接到Socket的输入输出流;
  • 按照一定的协议对Socket进行读写操作;
  • 关闭Socket。

7、什么是TCP/IP协议?

既然是网络编程,涉及几个系统之间的交互,那么首先要考虑的是如何准确的定位到网络上的一台或几台主机,另一个是如何进行可靠高效的数据传输。这里就要使用到TCP/IP协议。

TCP/IP协议:由网络层的IP协议和传输层的TCP协议组成。 由IP地址可以唯一的确定Internet上的一台主机,IP层主要负责网络主机的定位,数据传输的路由。TCP层负责面向应用的可靠的或非可靠的数据传输机制。

8、什么是TCP协议?

8.1. TCP(Transmission Control Protocol 传输控制协议) 是一种面向连接(连接导向)的、可靠的、 基于IP的传输层协议。

TCP的工作是将消息或文件分解成更小的片段(称为数据包),在通过Internet发送。然后,这些数据包由另一个TCP层接收,然后将该数据重组为完整的文件或消息。TCP还负责对数据流进行错误检查,以确保数据的传递;如果发现错误,则TCP重新传输数据包。

8.2. 对TCP协议中首部每个字段的含义是什么?

  • 源端口号和目的端口号: 分别占用16位,用于区别主机中的不同进程。而IP地址是用来区分不同的主机的,源端口号和目的端口号配合上IP首部中的源IP地址和目的IP地址就能唯一 的确定一个TCP连接。
  • 序号: 占用32位,它表示在这个报文段中的的第一个数据字节在数据流中的序号。主要用来解决网络报文乱序的问题。
  • 确认序号: 占用32位,它包含发送确认的一端所期望收到的下一个序号,因此,确认序号应当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK=1的时候表示应答域有效时,该确认序列号的字段才有效。主要用来解决不丢包的问题。
  • 数据偏移(首部长度): 占用4位,需要这个值是因为任选字段的长度是可变的。这个字段占4bit(最多能 表示15个32bit的的字,即4*15=60个字节的首部长度),因此TCP最多有60字节的首部。然而,没有任选字段, 正常的长度是20字节。
  • URG: 紧急指针域(后面马上就要说到)有效,用来保证TCP连接不被中断,并且督促中间层设备要尽快处理这些数据,当URG=1,表明紧急指针字段有效。。
  • ACK: 确认序号是否有效,有两个取值:0和1, 为1的时候表示应答域有效,反之0为无效。
  • PSH: 表示Push操作。PSH=1就是指在数据包到达接收端以后,立即传送给应用程序, 而不是在缓冲区中排队。
  • RST: 当RST=1时,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立运输连接。
  • SYN: 表示同步序号,用来建立连接。SYN标志位和ACK标志位搭配使用,当连接请求的时候,SYN=1, ACK=0;连接被响应的时候,SYN=1,ACK=1;这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有SYN的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口;但是由于这种扫描方式只是进行TCP三次握手的第一次握手,因此这种扫描的成功表示被扫描的机器不很安全,一台安全的主机将会强制要求一个连接严格的进行TCP的三次握手。
  • FIN: 用来释放连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
  • 窗口: 占用16位,TCP的流量控制由连接的每一端通过声明的窗口大小来提供。窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端正期望接收的字节。
  • 检验和: 接收端根据此检验和再计算判断该TCP包是否正确。
  • 紧急指针: 占用16位。紧急指针仅在URG=1时才有意义。从数据部分的首位到紧急指针所指示的位置为止为紧急数据。因此也可以说紧急指针指出了紧急数据的末尾在报文段中的位置。如何处理紧急数据属于应用的问题。一般在暂时中断通信,或中断通信的情况下使用。
  • 选项: 选项字段用于提高TCP的传输性能。因为根据数据偏移(首部长度)进行控制,该字段尽量调整其为32位的整数倍。

在这里插入图片描述

8.3. 对TCP三次握手的了解?

各个状态名称与含义:

  • CLOSED: 表示关闭状态。
  • LISTEN: 表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
  • SYN_SENT:表示客户端同步已发送。
  • SYN_RECV: 表示服务器端同步已收到。
  • ESTABLISHED:表示已经建立连接。

在这里插入图片描述

TCP协议中首部主要一些值的含义【这里在上部分:TCP协议中首部每个字段的含义中已经描述过】:

  • SYN:表示同步序号,用来建立连接。
  • seq:占用32位,主要用来解决网络报文乱序的问题。
  • ACK:确认序号是否有效,1-确认序号有效,0-确认序号无效。
  • ack:占用32位,它包含发送确认的一端所期望收到的下一个序号,确认序号应当是上次已成功收到数据字节序号加1。主要用来解决不丢包的问题。

三次握手的过程:

  1. 建立连接,客户端发送SYN包到服务器,并进入SYN_SENT(同步已发送)状态,等待服务器确认。【其中报文段的头部:SYN=1(同步序号)ACK=0(确认序号无效)seq=x(客户端发送的序号)
  2. 服务器收到请求,服务器必须确认客户的数据包。同时自己也发送一个SYN包给客户端,即SYN+ACK包,此时服务器进入SYN_RECV(同步已收到)状态。【其中报文段的头部:SYN=1(同步序号)ACK=1(确认序号有效)ack=seq+1(ack确认序号=客户端发送的序号x+1)seq=y(服务器端发送的序号)
  3. 客户端收到服务器的SYN+ACK包,向服务器发送一个序列号(seq=客户端发送的序号x+1)ack=y+1(确认序号=服务器端发送的序号y+1)ACK=1(确认序号有效),此包发送完毕,客户端和服务器进入ESTAB_LISHED(已经建立连接)状态。

为什么连接建立需要三次握手,而不是两次或者四次握手?

结论:

  • 为什么不是两次握手:是为了防止失效的连接请求报文段被服务端接收,可能会导致服务器端一直等待下去,浪费服务端连接资源。
  • 为什么不是四次握手:在三次握手已经是一个可靠的通讯基础上再次握手没什么意义,浪费服务端连接资源。

为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。

三次握手理解(A-客户端 B-服务器端):传输开始前,A知道自己的序列号,B知道自己的序列号。

  • 第一次握手,B知道了A的序列号;
  • 第二次握手,A知道了B知道它(A)的序列号;
  • 第三次握手,B知道了A知道它(B)的序列号。

这样,对于A和B而言,都知道“对方已经知道自己的序列号”这一现实,所以TCP连接可以建立。

如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。

再如果只是两次握手,客户端仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。

此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。

8.4. 对TCP四次挥手的了解?

四次挥手(Four-Way Wavehand) 即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。

由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,如下图:

各个状态名称与含义:

  • ESTABLISHED:表示已经建立连接。
  • FIN_WAIT_1:其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即 进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态。
  • FIN_WAIT_2:表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
  • TIME_WAIT:表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED状态了。如果FIN_WAIT_1状态下,收到了对方同时带 FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态。
  • CLOSE_WAIT:表示在等待关闭。
  • LAST_ACK:被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED状态了。
  • CLOSED: 表示关闭状态。

在这里插入图片描述

TCP协议中首部主要一些值的含义【这里在上部分:TCP协议中首部每个字段的含义中已经描述过】:

  • FIN:用来释放连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
  • SYN:表示同步序号,用来建立连接。
  • seq:占用32位,主要用来解决网络报文乱序的问题。
  • ACK:确认序号是否有效,1-确认序号有效,0-确认序号无效。
  • ack:占用32位,它包含发送确认的一端所期望收到的下一个序号,确认序号应当是上次已成功收到数据字节序号加1。主要用来解决不丢包的问题。

四次握手的过程:

  • 客户端发送一个FIN,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。
  • 服务器端收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),服务器端进入CLOSE_WAIT状态。
  • 服务器端再次发送一个FIN,用来关闭服务器端到客户端的数据传送,服务器端进入LAST_ACK状态。
  • 客户端收到FIN后,客户端进入TIME_WAIT状态,接着发送一个ACK给服务器端,确认序号为收到序号+1,服务器端进入CLOSED状态。

为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

MSL(Maximum Segment Lifetime) 译为“报文最大生存时间”,指报文在网络上存在的最长时间,超过这个时间报文将被丢弃。2MSL即两倍的MSL(2MSL一般会在1~4分钟)。【RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等】
为什么要等待2MSL后CLOSED: 因为虽然双方都同意关闭连接了,而且握手的4个报文也都发送完毕,按理可以直接回到CLOSED 状态,但是我们必须假想网络是不可靠的,你无法保证你(客户端)最后发送的ACK报文一定会被对方收到,就是说对方处于LAST_ACK 状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT 状态的作用就是用来重发可能丢失的ACK报文。

8.5. 通过TCP协议- 实现客户端与服务器端进行传输的简单小案例:

//TCP服务器端public class TcpServer {   	public static void main(String[] args) throws Exception {   		try {   			System.out.println("Socket TCP服务器端启动....");		 	//启动Socket服务器端			ServerSocket serverSocket = new ServerSocket(8080);			//等待客户端请求			Socket accept = serverSocket.accept();			//获取请求流			InputStream inputStream = accept.getInputStream();			//转换成string类型			byte[] buf = new byte[1024];			int len = inputStream.read(buf);			String str = new String(buf, 0, len);			System.out.println("服务器接受客户端内容:" + str);		}catch (Exception e){   	  		throw e;	    }finally {   	       serverSocket.close();         	    }	}}
//TCP客户端public class TcpClient {   	public static void main(String[] args) throws Exception {   		try {   			System.out.println("Socket TCP客户端启动....");			Socket socket = new Socket("127.0.0.1", 8080);			OutputStream outputStream = socket.getOutputStream();			outputStream.write("我是客户端,我宣你".getBytes());		}catch (Exception e){   	  		throw e;	    }finally {   	       socket.close();        	    }	}}

9、什么是UDP协议?

UDP(User Datagram Protocol 用户数据报协议): 是传输层的协议,面向无连接的;使用尽最大努力交付但不保证可靠交付;是面向报文的,数据报必须限定在64KB之内;支持一对一、一对多、多对一和多对多的交互通信;无法保证传输数据的顺序性;不提供差错纠正、队列管理、重复消除、流量控制和拥塞控制,但提供差错检测。

对UDP的首部格式的了解?

  • 源端口: 源端口号,范围(0~65535),需要对方回信时选用,不需要时全部置0.
  • 目的端口:目的端口号,范围(0~65535),在终点交付报文的时候需要用到。
  • 长度:UDP的数据报的长度(包括首部和数据)其最小值为8(只有首部)
  • 校验和:检测UDP数据报在传输中是否有错,有错则丢弃。该字段是可选的,当源主机不想计算校验和,则直接令该字段全为0。
    当传输层从IP层收到UDP数据报时,就根据首部中的目的端口,把UDP数据报通过相应的端口,上交给应用进程。
    如果接收方UDP发现收到的报文中的目的端口号不正确(不存在对应端口号的应用进程0,),就丢弃该报文,并由ICMP发送“端口不可达”差错报文给对方。
  • 伪首部:在UDP伪首部中,包含32位源IP地址,32位目的IP地址,8位填充0,8位协议,16位UDP长度。伪首部是一个虚拟的数据结构,其中的信息是从数据报所在IP分组头的分组头中提取的,既不向下传送也不向上递交,而仅仅是为计算校验和,目的是让UDP层验证数据是否已经到达正确的目的地。

在这里插入图片描述

通过UDP协议- 实现客户端与服务器端进行传输的简单小案例:

//UDP服务器端public class UdpSocketServer {   	public static void main(String[] args) throws Exception {   		try {   			System.out.println("Socket UDP服务器端启动连接....");			DatagramSocket ds = new DatagramSocket(8080);			byte[] bytes = new byte[1024];			DatagramPacket dp = new DatagramPacket(bytes, bytes.length);			// 阻塞,等待接受客户端发送请求			ds.receive(dp);			// 获取客户端请求内容			String str=new String(dp.getData(),0,dp.getLength());			System.out.println("str:"+str);		}catch (Exception e){   	  		throw e;	    }finally {   	       	ds.close();      	    }	}}
//UDP客户端代码public class UdpClient {   	 public static void main(String[] args) throws IOException {   		try {   			System.out.println("Socket UDP客户端启动连接....");		 	DatagramSocket ds = new DatagramSocket();		 	String str="学习Java Socket UDP网络编程";		 	byte[] bytes= str.getBytes();		 	DatagramPacket dp= new DatagramPacket(bytes, bytes.length,InetAddress.getByName("127.0.0.1"),8080);		 	ds.send(dp);		}catch (Exception e){   		  	throw e;	    }finally {   	       	ds.close();      	    }	}}

UDP应用场景:

  1. 面向数据报方式
  2. 网络数据大多为短消息
  3. 拥有大量Client
  4. 对数据安全性无特殊要求
  5. 网络负担非常重,但对响应速度要求高
  6. ……等等

10、TCP协议与UDP协议的区别?

TCP特点:

  • TCP是面向连接的协议,通过三次握手建立连接,通讯完成时要拆除连接,由于TCP是面向连接协议,所以只能用于点对点的通讯。而且建立连接也需要消耗时间和开销,效率也会稍低。
  • TCP传输数据无大小限制,以字节流方式进行大数据传输,存在粘包和拆包的问题。
  • TCP是一个可靠的协议,它能保证接收方能够完整正确地接收到发送方发送的全部数据。

UDP特点:

  • UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送,速度快。
  • UDP传输数据时有大小限制,每个被传输的数据报必须限定在64KB之内。
  • UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。

TCP与UDP应用:

  • TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。
  • UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统等,并不要求数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。

11、TCP 短连接和长连接的区别?

长连接和短连接的产生在于 Client 和 Server 采取的关闭策略,具体的应用场景采用具体的策略。

短连接: Client 向 Server 发送消息,Server 回应 Client,然后一次读写就完成了,这时候双方任何一个都可以发起 close 操作,不过一般都是 Client 先发起 close 操作。短连接一般只会在 Client/Server 间传递一次读写操作。

优点: 管理起来比较简单,建立存在的连接都是有用的连接,不需要额外的控制手段。像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源。
缺点: 请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。

长连接: Client 与 Server 完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。

优点: 可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。所以长连接适合频繁建立连接的场景。
缺点: 连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致server端服务受损;如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个蛋疼的客户端连累后端服务。

12、TCP粘包、拆包及解决办法?

TCP 是基于字节流的,虽然应用层和 TCP 传输层之间的数据交互是大小不等的数据块,但是 TCP 并没有把这些数据块区分边界,仅仅是一连串没有结构的字节流传输的。

什么是粘包、拆包?
第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象。
在这里插入图片描述
第二种情况,接收端只收到一个数据包,但是这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。
在这里插入图片描述
第三种情况,这种情况有两种表现形式,如下图。接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。
在这里插入图片描述在这里插入图片描述
为什么会发生 TCP 粘包、拆包?

  • 要发送的数据大于 TCP 发送缓冲区剩余空间大小,将会发生拆包。
  • 待发送数据大于 MSS(最大报文长度),TCP 在传输前将进行拆包。
  • 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次发送出去,将会发生粘包。
  • 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

粘包、拆包解决办法?

  • 消息定长,报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即使粘包了通过接收方编程实现获取定长报文也能区分。
    sc.pipeline().addLast(new FixedLengthFrameDecoder(10));
  • 包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分。
    //比如以"|"分隔ByteBuf buf = Unpooled.copiedBuffer("发送的内容20200813|".getBytes());sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
  • 更复杂的应用层协议比如 Netty 中实现的一些协议都对粘包、拆包做了很好的处理。

13、TCP 流量控制?

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

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

实际上,为了避免此问题的产生,发送端主机会时不时的发送一个叫做窗口探测的数据段,此数据段仅包含一个字节来获取最新的窗口大小信息。

13、TCP 拥塞控制?

如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。

在这里插入图片描述
TCP 主要通过四个算法来进行拥塞控制:

  • 慢开始
  • 拥塞避免
  • 快重传
  • 快恢复

后续更多关于网络编程相关内容会不断更新中……


······

帮助他人,快乐自己,最后,感谢您的阅读!
所以如有纰漏或者建议,还请读者朋友们在评论区不吝指出!

…知识是一种宝贵的资源和财富,益发掘,更益分享…

转载地址:http://kkdg.baihongyu.com/

你可能感兴趣的文章
面试问道nginx优化怎么做的
查看>>
自学linux毕业shell面试题
查看>>
4 Java 访问控制符号的范围
查看>>
第9章 - 有没有替代原因(检验证据)
查看>>
VUE3(八)setup与ref函数
查看>>
Vue之Element标签页保留用户操作缓存。
查看>>
智能合约开发实践(1)
查看>>
2. Spring Boot学习——Yaml等配置文件教程
查看>>
MATLAB——操作矩阵的常用函数
查看>>
CMake自学记录,看完保证你知道CMake怎么玩!!!
查看>>
Eigen库中vector.transpose()函数什么意思
查看>>
ORB-SLAM2:LocalMapping线程学习随笔【李哈哈:看看总有收获篇】
查看>>
ORB-SLAM2:LoopClosing线程学习随笔【李哈哈:看看总有收获篇】
查看>>
牛客练习赛56 D 小翔和泰拉瑞亚(线段树)
查看>>
Codeforces Round #614 (Div. 2) B - JOE is on TV! (简单贪心)
查看>>
Codeforces Round #611 (Div. 3) E. New Year Parties (贪心)
查看>>
Codeforces Round #553 (Div. 2) B. Dima and a Bad XOR(异或+思维)
查看>>
Codeforces Round #305 (Div. 1) B. Mike and Feet(单调栈)
查看>>
hdu6434 Problem I. Count(数论)(好题)
查看>>
NC15553 数学考试(线性DP)
查看>>