本文共 13070 字,大约阅读时间需要 43 分钟。
如想了解更多更全面的Java必备内容可以阅读:所有JAVA必备知识点面试题文章目录:
网络编程: 主要是在发送端将信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。
在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称作客户端(Client),等待其他程序连接的程序被称作服务器(Server)。客户端程序可以在需要的时候启动,而服务器为了能够时刻相应连接,则需要一直启动。是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
由于IP地址不方便记忆,所以有专门创造了域名(Domain Name)的概念,来将域名和IP地址相互映射,例如163.com、sina.com等。 其实在网络中只能使用IP地址进行数据传输,所以在传输以前,需要把域名转换为IP,这个由称作DNS的服务器专门来完成。 所以在网络编程中,可以使用IP或域名来标识网络上的一台设备。为了在一台设备上可以运行多个程序,人为的设计了端口(Port)的概念,端口号只有整数,范围是从0 到65535(2^16-1),每个端口对应一个唯一的程序。
每个网络程序,无论是客户端还是服务器端,都对应一个或多个特定的端口号。由于0-1023一般被用作知名服务器的端口如FTP、HTTP、SMTP等,所以实际编程时一般采用大于 1024 以上的端口号。 使用端口号,可以找到一台设备上唯一的一个程序。所以如果需要和某台计算机建立连接的话,只需要知道IP地址或域名即可,但是如果想和该台计算机上的某个程序交换数据的话,还必须知道该程序使用的端口号。域名系统DNS (Domain Name System)是因特网使用的命名系统,是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工作。
DNS解析过程:
用户通过浏览器浏览过某网站之后,浏览器就会自动缓存该网站域名对应的IP地址,当用户再次访问的时候,浏览器就会从缓存中查找该域名对应的IP地址,因为缓存不仅是有大小限制,而且还有时间限制(域名被缓存的时间通过TTL属性来设置)所以存在域名对应的IP找不到的情况。当浏览器从缓存中找到了该网站域名对应的IP地址,那么整个DNS解析过程结束,如果没有找到,将进行下一步骤。
如果第一个步骤没有完成对域名的解析过程,那么浏览器会去系统缓存中查找系统是否缓存过这个域名对应的IP地址,也可以理解为系统自己也具备域名解析的基本能力。在Windows系统中,可以通过设置hosts文件来将域名手动绑定到某IP上,hosts文件位置在C:/Windows/System32/drivers/etc/hosts。对于普通用户,并不推荐自己手动绑定域名和IP,对于开发者来说,通过绑定域名和IP,可以轻松切换环境,可以从测试环境切换到开发环境,方便开发和测试。在XP系统中,黑客常常修改他的电脑的hosts文件,将用户常常访问的域名绑定到他指定的IP上,从而实现了本地DNS解析,导致这些域名被劫持。在Linux或者Mac系统中,hosts文件在/etc/hosts,修改该文件也可以实现同样的目的。
如果在本机上无法完成域名的解析,那么系统只能请求本地域名解析服务系统进行解析,本地域名系统LDNS一般都是本地区的域名服务器,比如你连接的校园网,那么域名解析系统就在你的校园机房里,如果你连接的是电信、移动或者联通的网络,那么本地域名解析服务器就在本地区,由各自的运营商来提供服务。对于本地DNS服务器地址,Windows系统使用命令ipconfig就可以查看,在Linux和Mac系统下,直接使用命令cat /etc/resolv.conf来查看LDNS服务地址。LDNS一般都缓存了大部分的域名解析的结果,当然缓存时间也受域名失效时间控制,大部分的解析工作到这里就差不多已经结束了,LDNS负责了大部分的解析工作。
LDNS域名解析器还没有完成解析的话,那么本地域名解析服务器(LDNS)将向根域名服务器(Root Server DNS)发起解析请求。
根域名服务器返回的是所查域的通用顶级域(Generic top-level domain,gTLD)地址,常见的通用顶级域有.com、.cn、.org、.edu等。
本地域名解析服务器(LDNS)向顶级域名服务器(gTLD Server)发起请求。
gTLD服务器接收本地域名服务器发起的请求,并根据需要解析的域名,找到该域名对应的Name Server域名服务器,通常情况下,这个Name Server服务器就是你注册的域名服务器,那么你注册的域名的服务商的服务器将承担起域名解析的任务。
Name Server服务器查找域名对应的IP地址,将IP地址连同TTL值返回给本地域名服务器。
本地域名服务器缓存解析后的结果,缓存时间由TTL时间来控制。
解析结果将直接返回给用户,用户系统将缓存该IP地址,缓存时间由TTL来控制,至此,解析过程结束。
结构及协议 详细版图一:
Socket 通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket,一个Socket由一个IP地址和一个端口号唯一确定。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。 Socket是TCP/IP协议的一个十分流行的编程界面,但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。
Socket的基本工作过程包含以下四个步骤:既然是网络编程,涉及几个系统之间的交互,那么首先要考虑的是如何准确的定位到网络上的一台或几台主机,另一个是如何进行可靠高效的数据传输。这里就要使用到TCP/IP协议。
TCP/IP协议:由网络层的IP协议和传输层的TCP协议组成。 由IP地址可以唯一的确定Internet上的一台主机,IP层主要负责网络主机的定位,数据传输的路由。TCP层负责面向应用的可靠的或非可靠的数据传输机制。8.1. TCP(Transmission Control Protocol 传输控制协议) 是一种面向连接(连接导向)的、可靠的、 基于IP的传输层协议。
TCP的工作是将消息或文件分解成更小的片段(称为数据包),在通过Internet发送。然后,这些数据包由另一个TCP层接收,然后将该数据重组为完整的文件或消息。TCP还负责对数据流进行错误检查,以确保数据的传递;如果发现错误,则TCP重新传输数据包。8.2. 对TCP协议中首部每个字段的含义是什么?
8.3. 对TCP三次握手的了解?
各个状态名称与含义:
TCP协议中首部主要一些值的含义【这里在上部分:TCP协议中首部每个字段的含义中已经描述过】:
三次握手的过程:
为什么连接建立需要三次握手,而不是两次或者四次握手?
结论:
为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。
三次握手理解(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。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,如下图:各个状态名称与含义:
四次握手的过程:
为什么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(); } }}
UDP(User Datagram Protocol 用户数据报协议): 是传输层的协议,面向无连接的;使用尽最大努力交付但不保证可靠交付;是面向报文的,数据报必须限定在64KB之内;支持一对一、一对多、多对一和多对多的交互通信;无法保证传输数据的顺序性;不提供差错纠正、队列管理、重复消除、流量控制和拥塞控制,但提供差错检测。
对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应用场景:
TCP特点:
UDP特点:
TCP与UDP应用:
长连接和短连接的产生在于 Client 和 Server 采取的关闭策略,具体的应用场景采用具体的策略。
短连接: Client 向 Server 发送消息,Server 回应 Client,然后一次读写就完成了,这时候双方任何一个都可以发起 close 操作,不过一般都是 Client 先发起 close 操作。短连接一般只会在 Client/Server 间传递一次读写操作。
优点: 管理起来比较简单,建立存在的连接都是有用的连接,不需要额外的控制手段。像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源。 缺点: 请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。长连接: Client 与 Server 完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。
优点: 可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。所以长连接适合频繁建立连接的场景。 缺点: 连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致server端服务受损;如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个蛋疼的客户端连累后端服务。TCP 是基于字节流的,虽然应用层和 TCP 传输层之间的数据交互是大小不等的数据块,但是 TCP 并没有把这些数据块区分边界,仅仅是一连串没有结构的字节流传输的。
什么是粘包、拆包? 第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象。粘包、拆包解决办法?
sc.pipeline().addLast(new FixedLengthFrameDecoder(10));
//比如以"|"分隔ByteBuf buf = Unpooled.copiedBuffer("发送的内容20200813|".getBytes());sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
流量控制是为了控制发送方发送速率,保证接收方来得及接收。
接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。
实际上,为了避免此问题的产生,发送端主机会时不时的发送一个叫做窗口探测的数据段,此数据段仅包含一个字节来获取最新的窗口大小信息。
如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。
后续更多关于网络编程相关内容会不断更新中……
······
帮助他人,快乐自己,最后,感谢您的阅读! 所以如有纰漏或者建议,还请读者朋友们在评论区不吝指出!…知识是一种宝贵的资源和财富,益发掘,更益分享…
转载地址:http://kkdg.baihongyu.com/