一、网络编程概述
网络编程是通过计算机网络进行数据传输和通信的编程技术,在 Java 中,我们可以使用丰富的网络编程功能和 API 来实现不同计算机之间的数据交互。
Java 网络编程是指在 Java 语言中使用网络协议和 API 进行网络通信的编程技术。Java 网络编程可以实现多种应用场景,包括客户端 / 服务器通信、网站开发、分布式系统等。
网络编程的核心概念包括网络通信协议、Socket 编程、服务器和客户端等。网络通信协议是指在网络中进行通信时所使用的规则和标准。常见的网络通信协议包括 TCP/IP 协议、UDP 协议、HTTP 协议等。Java 中使用 Socket 编程来实现网络通信,可以支持多种网络通信协议。Socket 编程是指使用 Socket API 进行网络通信的编程技术。Java 中的 Socket 编程可以实现客户端和服务器之间的通信,包括 TCP 连接和 UDP 连接。Socket 编程需要处理网络连接、数据传输和错误处理等问题。服务器和客户端是指在网络通信中扮演不同角色的程序。服务器可以提供服务,等待客户端请求并响应请求;客户端可以请求服务并接收响应。Java 中可以通过 Socket 编程来实现客户端和服务器之间的通信。
二、计算机网络
1. 网络协议
计算机网络中的网络协议是为计算机网络中进行数据交换而建立的规则、标准或约定的集合。它就像是计算机之间交流的语言,规定了通信实体之间所交换的消息的格式、意义、顺序以及针对收到信息或发生的事件所采取的动作。网络协议的三要素包括语法、语义和时序。语法即数据与控制信息的结构和格式;语义指需要发出何种控制信息,完成何种动作以及做出何种响应;时序则是事件顺序和速度匹配。
例如,在不同的计算机系统中,可能会使用不同的字符集。为了实现通信,就需要规定每个终端都要将各自字符集中的字符先变换为标准字符集的字符后,才进入网络传送,到达目的终端之后,再变换为该终端字符集的字符。
2. 网络体系结构
计算机网络体系结构把计算机网络实现的功能分到不同层次,层与层之间用接口连接,通过协议数据单元进行通信。计算机网络体系结构的分层通常指的是 ISO/OSI(开放系统互连)模型或 TCP/IP 模型。
ISO/OSI 模型将计算机网络通信协议分为七层,这些层分别是:物理层,负责将数据转换成比特流并通过适当的介质进行传输;数据链路层,处理数据帧的封装和解封,确保数据在网络中的正确传输;网络层,负责选择最优路径进行数据包的传输;传输层,提供端到端的信息传输服务,确保信息的可靠性和顺序性;会话层,管理和维护在不同主机之间建立的通信会话;表示层,负责数据的表示、加密和压缩等操作;应用层,为用户提供具体的网络服务和应用程序。
TCP/IP 非标准但广泛使用的四层体系结构则由低到高依次为:网络接口层,也称为网络访问层,负责网络设备间的通信;网际互联层,处理数据包在不同的网络之间的路由和转发;传输层,包括传输控制协议(TCP)和用户数据协议(UDP),负责数据段的可靠传输;应用层,直接面向用户的应用层,包括常见的协议如 HTTP、FTP、SMTP 等。
无论是 OSI 还是 TCP/IP 模型,每层都有其特定的职责,并且依赖于下一层提供的服务来实现自己的功能。例如,在发送文件时,需要经过多个层次的处理。首先,应用层的程序如 Outlook 会将邮件交给按 POP3 或 SMTP 协议编写的程序,该程序按协议整理数据格式后发给下面层的程序。每个层都会对数据格式做一些加工,还会用报头的形式增加一些信息。经过加工后的数据以帧的形式交给物理层,物理层的电路再以位流的形式发送数据到网络中。接收方的过程是相反的,物理层接收到数据后,以相反的顺序遍历 OSI 的所有层,使接收方收到这个电子邮件。接收方的主机,每一层都会阅读本层对应的报头,拆除自己层的报头把数据传送给上一层。
三、OSI 参考模型
1. 简介
世界上第一个网络体系结构,由 ISO 提出,把网络通信的工作分为 7 层。OSI 参考模型是开放式系统互联参考模型,它是一个试图使各种计算机在世界范围内互连为网络的标准框架。
2. 7 个层次功能
- 物理层:为数据端设备提供传数据的通路。物理层作为 OSI 的最底层,主要是用来传输物理信号,比如电信号、光号。传输介质有光纤、同轴电缆、双绞线等。物理层在局部局域网上传送数据帧,负责管理计算机通信设备和网络媒体之间的互通,包括针脚、电压、线缆规范、集线器、中继器、网卡、主机适配器等。
- 数据链路层:实现相邻节点之间的可靠传输。数据链路层在物理层提供比特流服务的基础上,将比特信息封装成数据帧,起到在物理层上建立、撤销、标识逻辑链接和链路复用以及差错校验等功能。通过使用接收系统的硬件地址或物理地址来寻址,建立相邻节点之间的数据链路,通过差错控制保证数据帧在信道上的无差错传输,同时为其上面的网络层提供有效的服务。
- 网络层:完成源主机节点到目的主机节点之间的网络传输。网络层也称通信子网层,是高层协议之间的界面层,用于控制通信子网的操作,是通信子网与资源子网的接口。在计算机网络中进行通信的两个计算机之间可能会经过很多个数据链路,也可能还要经过很多通信子网,网络层的任务就是选择合适的网间路由和交换节点,确保数据及时传送。
- 传输层:进行源端节点到目的端节点之间可靠的信息传输。传输层建立了主机端到端的链接,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括处理差错控制和流量控制等问题。该层向高层屏蔽了下层数据通信的细节,使高层用户看到的只是在两个传输实体间的一条主机到主机的、可由用户控制和设定的、可靠的数据通路。我们通常说的,TCP、UDP 就是在这一层。
- 会话层和表示层:负责应用程序之间建立、维持和中断会话,以及数据格式的转换。会话层就是负责建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。会话层的同事拿到表示层的同事转换后资料,会话层的同事那里可能会掌握本公司与其他好多公司的联系方式,这里公司就是实际传递过程中的实体。他们要管理本公司与外界好多公司的联系会话。当接收到表示层的数据后,会话层将会建立并记录本次会话,他首先要找到公司 B 的地址信息,然后将整份资料放进信封,并写上地址和联系方式。准备将资料寄出。等到确定公司 B 接收到此份报价单后,此次会话就算结束了,外联部的同事就会终止此次会话。表示层提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。如果必要,该层可提供一种标准表示形式,用于将计算机内部的多种数据格式转换成通信中采用的标准表示形式。数据压缩和加密也是表示层可提供的转换功能之一。
- 应用层:为应用进程提供服务。应用层是计算机网络体系结构中的最高层,它直接为用户和应用程序提供服务。在 OSI 模型中,应用层对应于第 7 层,而在 TCP/IP 模型中,它包括了 OSI 模型中的第 5、6、7 层。应用层的作用是在网络中实现不同应用程序之间的通信和数据交换,它定义了数据格式和传输协议,使得不同类型的应用程序能够相互通信。应用层是网络通信的最前沿,它的效率和安全性直接影响着用户体验和数据的安全。常见应用层的网络服务协议有:HTTP,HTTPS,FTP,POP3、SMTP 等。
四、TCP/IP 参考模型
1. 简介
Internet 最基本的协议,由传输控制协议(TCP)和网络互联协议(IP)得名。TCP/IP 协议是应用在 INTERNET 上的非国际标准体系结构,但目前已经成为互联网通信的核心协议。它采用分层体系结构,与开放系统互连(OSI)模型的层次结构相似,可分为四层,由低到高依次为:网络接口层、网络层(IP 层)、传输层(TCP 层)和应用层。
2. 4 个层次
- 网络接口层:也称为网络访问层,负责网络设备间的通信,对应了 OSI 参考模型的数据链路层和物理层。主要功能是连接上一层的 IP 数据报,通过网络向外发送,或者接收和处理来自网络上的物理帧,并抽取 IP 数据传送到上一层 ------ 网络层。常见的接口层协议有:Ethernet 802.3、Token Ring 802.5、X.25、Frame relay、HDLC、PPP ATM 等。物理层定义物理介质的各种特性,包括机械特性、电子特性、功能特性和规程特性。数据链路层负责接收 IP 数据报并通过网络发送之,或者从网络上接收物理帧,抽出 IP 数据报,交给 IP 层。
- 互联网层(IP 层):网络层主要解决计算机之间的通信问题,它负责管理不同设备之间的数据交换,是 INTERNET 通信子网的最高层。它所提供的是不可靠的无连接数据报机制,无论传输是否正确,不做验证,不发确认,也不保证分组的正确顺序。网络层包括:IP(Internet Protocol)协议、ICMP(Internet Control Message Protocol)控制报文协议、ARP(Address Resolution Protocol)地址转换协议、RARP(Reverse ARP)反向地址转换协议。IP 是网络层的核心,通过路由选择将下一跳 IP 封装后交给接口层。ICMP 是网络层的补充,可以回送报文,用来检测网络是否通畅。ARP 是正向地址解析协议,通过已知的 IP,寻找对应主机的 MAC 地址。RARP 是反向地址解析协议,通过 MAC 地址确定 IP 地址。
- 传输层(TCP 层):传输层主要是确保所有传送到某个系统数据正确无误地到达该系统,提供端到端的可靠性传输。该层主要协议有:TCP 协议和 UDP 协议。TCP 协议是面向连接的通信协议,提供可靠的面向连接的数据传输服务。在进行网络通信时,需要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过 "三次握手"。UDP 协议是无连接通信协议,在数据传输时,数据的发送端和接收端不建立逻辑连接。UDP 协议特点是消耗资源小,通信效率高,通常用于音频、视频和普通数据的传输。
- 应用层:直接面向用户的应用层,包括常见的协议如 HTTP、FTP、SMTP 等。应用层是将应用程序的数据传送给传输层,以便进行信息交换。它主要为各种应用程序提供了使用的协议,标准的应用层协议主要有:FTP 文件传输协议,为文件传输提供途径;HTTP 超文本传输协议,用来访问在 WWW 服务器上的各种页面;DNS 域名服务系统,用于实现从主机域名到 IP 地址之间的转换;TELNET 虚拟终端服务,实现互联网中的工作站登陆到远程服务器的能力;SMTP 简单邮件传输协议,实现互联网中电子邮件的传输功能;NFS 网络文件系统,用于实现网络中不同主机之间的文件共享;RIP 路由信息协议,用于网络设备之间交换路由信息。
五、常见网络协议
1. TCP 协议
TCP 协议是面向连接的通信协议,提供可靠的数据传输,建立连接需要三次握手。
TCP 具有以下特点:
- 面向连接:在传输数据前先在发送端和接收端建立逻辑连接。
- 可靠性强:通过各种校验机制保证数据传输的可靠性和稳定性。
- 基于流的协议:数据以流的形式传输。
- TCP 处于传输层通信协议。
TCP 连接建立时需要进行三次握手,四次挥手操作,这样就保证了数据传输的安全性和可靠性。在数据传输过程中,客户端向服务器端发送数据时会进行各种校验,服务器端向客户端反馈消息时也同样如此。
传输稳定性方面,数据传输速率会随机调整。使用 TCP 发送数据时,会受到带宽硬件等限制。如果发送过程中发现数据接收端接受数据缓慢,接收端会将该状态定期发送给客户端,发送端会根据该状态调整速度,以保证数据传输的稳定性。
TCP 的应用场景包括聊天信息传送、单人语音视频聊天等,但只能进行点对点传输,无法进行广播、多播操作。
2. UDP 协议
UDP 协议是无连接通信协议,消耗资源小,通信效率高,但不能保证数据完整性。
UDP 是面向数据报的传输层协议,不是面向连接的协议,发送和接收数据不需要建立连接。UDP 协议特点是消耗资源小,通信效率高,通常用于音频、视频和普通数据的传输。例如视频会议都使用 UDP 协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用 UDP 协议传送数据时,由于 UDP 的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用 UDP 协议。
UDP 报文头长度 64 位,8 字节。0 - 15 位存储源端口号,16 - 31 位存储目标端口号,32 - 47 位是数据长度,48 - 63 存储报文头和数据的校验和。UDP 数据包的最大长度是 65535 - 8 = 65507 字节,大约 64KB。
3. HTTP 协议
HTTP 协议用于客户端和服务器之间的通信。
HTTP(Hypertext Transfer Protocol,超文本传输协议)是互联网应用层的主要协议,广泛用于在客户端和服务器之间传输数据。它是 Web 的基础,使得不同类型的资源(如文本、图像、视频等)可以在客户端(通常是浏览器)和服务器之间进行交换。
HTTP 基于请求和响应的模型,客户端发送请求,服务器返回响应。每个 HTTP 请求都是独立的,服务器不需要记住之前的请求状态。HTTP 可以支持多种数据格式,通过不同的 MIME 类型来处理不同类型的数据。HTTP 协议支持缓存,可以提高性能,减少服务器负载。
HTTP 请求包括请求行、请求头、请求体。请求行是 HTTP 请求的第一行,包含方法、请求 URI 和 HTTP 版本。请求头包含了请求的附加信息,可以影响请求的处理或提供客户端的环境信息。请求体用于携带客户端要发送到服务器的数据,通常在 POST 或 PUT 请求中使用。
HTTP 响应包括响应行、响应头、响应实体。响应行以响应码开头,接着是包含元数据的首部。响应头客户端使用的附加信息。响应实体是服务器返回给浏览器的信息。
六、计网常见问题
域名系统(DNS)的工作流程,以及如何提高 DNS 查询效率。
一、DNS 的工作流程
当用户在浏览器中输入一个网址(如 www.example.com)时,DNS 的工作流程如下:
- 用户发起 DNS 请求:浏览器首先检查本地缓存是否有该域名的记录。如果没有,操作系统会向配置的 DNS 服务器发送一个 DNS 查询请求。
- 递归查询:
- 如果本地 DNS 解析器(通常是用户 ISP 的 DNS 服务器)没有缓存该域名的 IP 地址,它会作为 DNS 客户端,向根 DNS 服务器发起查询。
- 根 DNS 服务器是顶级的 DNS 服务器,它不直接解析域名,而是告诉下一级的顶级域(TLD,例如.com、.net 等)DNS 服务器的地址。
- 本地 DNS 服务器接着向 TLD DNS 服务器发起查询。TLD DNS 服务器管理着在其下注册的所有二级域名的记录,它会返回负责该域名的权威 DNS 服务器的地址。
- 最后,本地 DNS 服务器向权威 DNS 服务器发起查询。权威 DNS 服务器存储了域名对应的 IP 地址等记录,它会将这些信息返回给本地 DNS 服务器。
- 缓存结果:本地 DNS 服务器收到 IP 地址后,会缓存这个结果(以减少未来的查询时间),然后把 IP 地址返回给用户的计算机。
- 浏览器发起连接:用户的计算机使用得到的 IP 地址,通过互联网与目标服务器建立连接,从而用户可以访问目标网站。
二、提高 DNS 查询效率的方法
优化 DNS 解析的速度和效率可以通过以下几个方法实现:
- 选择合适的 DNS 服务器:选择速度较快、稳定性高的 DNS 服务器可以提高解析速度和效率。例如,可以尝试使用如谷歌 8.8.8.8 或 Cloudflare 1.1.1.1 这类公共 DNS 服务,它们的响应迅速、稳定可靠。
- 启用 DNS 缓存:在本地网络设备(如路由器、计算机)上启用 DNS 缓存可以减少对 DNS 服务器的查询请求,提高解析速度。包括浏览器缓存、系统缓存、路由器缓存等。
- 减少 DNS 查找次数:减少需要解析的域名数量,可以通过将多个域名解析到同一 IP 地址,或者使用子域名等方式实现。同时,避免不必要的重定向和跳转,减少 DNS 查找次数。
- 优化 DNS 记录:合理配置 DNS 记录,如 A 记录、MX 记录、CNAME 记录等,保证 DNS 解析的正确性和稳定性。避免配置错误或冗余的 DNS 记录。
- 使用 DNS 预解析(DNS Prefetching):通过在浏览器中预解析域名,提前获取 DNS 解析结果,可以加快后续网页内容的加载速度。DNS Prefetch 应该尽量放在网页的前面,具体使用方法如下:
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <meta http-equiv="x-dns-prefetch-control" content="on"> <link rel="dns-prefetch" href="//www.zhix.net"> <link rel="dns-prefetch" href="//api.share.zhix.net"> <link rel="dns-prefetch" href="//bdimg.share.zhix.net"> |
- 分布式部署 DNS 服务器(DNS 负载均衡):将 DNS 服务器部署在多个地理位置,可以提高用户访问速度和可用性。
- 使用 HTTPS DNS:通过加密 DNS 查询数据,可以保护用户隐私和防止中间人攻击。同时,一些 HTTPS DNS 服务提供商会提供更快的解析速度和更好的稳定性。
- 定期更新 DNS 记录:根据网站内容的变化情况,定期更新 DNS 记录,保证解析结果的实时性和准确性。
- 使用 DNSSEC:通过使用 DNSSEC(DNS 安全扩展)技术,可以保证 DNS 查询的安全性和完整性,防止域名劫持等攻击。
- 监控和日志分析:对 DNS 解析过程进行监控和日志分析,及时发现和解决性能瓶颈和问题。
七、Java 网络编程
1. 网络编程三要素
- IP 地址:唯一标识网络中的计算机设备。在 Java 网络编程中,IP 地址用于确定通信的目标计算机。例如,在建立 TCP 连接或发送 UDP 数据包时,需要指定目标计算机的 IP 地址。IP 地址通常由四个数字组成,每个数字的范围是 0-255,例如 192.168.1.1。
- 端口:唯一标识设备中的应用程序。端口号是一个 16 位的整数,范围从 0 到 65535。在 Java 中,不同的应用程序可以通过不同的端口号进行区分。例如,HTTP 协议通常使用端口 80,HTTPS 协议使用端口 443。
- 协议:计算机网络通信必须遵守的规则。Java 网络编程中常见的协议有 TCP 和 UDP。TCP 是一种面向连接的协议,提供可靠的数据传输;UDP 是一种无连接的协议,速度较快,但不保证数据的可靠性。
2. TCP 网络编程
- Socket 介绍:对 TCP/IP 协议进行封装的编程调用接口。Socket 是 Java 中实现网络编程的核心类之一。它提供了一种方便的方式来建立网络连接,并进行数据的传输。
- 客户端步骤:创建 Socket 对象,获取输入输出流,进行数据读写。
- 创建 Socket 对象:使用Socket(String host, int port)构造方法创建一个客户端 Socket 对象,指定服务器的 IP 地址和端口号。例如,Socket socket = new Socket("192.168.4.21", 123)。
- 获取输入输出流:通过getInputStream()和getOutputStream()方法获取输入输出流,用于读取服务器发送的数据和向服务器发送数据。
- 进行数据读写:使用输入输出流进行数据的读写操作。例如,可以使用OutputStream out = socket.getOutputStream()获取输出流,然后使用out.write(bytes)向服务器发送数据;使用InputStream in = socket.getInputStream()获取输入流,然后使用in.read(bytes)读取服务器发送的数据。
- 服务端步骤:创建 ServerSocket 对象,侦听客户端连接,获取输入输出流,进行数据读写。
- 创建 ServerSocket 对象:使用ServerSocket(int port)构造方法创建一个服务端 ServerSocket 对象,指定监听的端口号。例如,ServerSocket serverSocket = new ServerSocket(123)。
- 侦听客户端连接:使用accept()方法侦听客户端连接,该方法会阻塞直到有客户端连接到来。当有客户端连接时,返回一个 Socket 对象,代表与客户端的连接。例如,Socket socket = serverSocket.accept()。
- 获取输入输出流:通过与客户端连接的 Socket 对象获取输入输出流,用于读取客户端发送的数据和向客户端发送数据。
- 进行数据读写:使用输入输出流进行数据的读写操作,与客户端的读写操作类似。
3. UDP 网络编程
- 编程原理:通过传输层将数据转换为数据包发送。UDP 是一种无连接的协议,它将数据封装成数据包进行发送。每个数据包都包含了目标地址和端口号,以及数据内容。UDP 不保证数据的可靠性和顺序性,但速度较快,适用于一些对实时性要求较高的应用场景。
- 发送端步骤:创建 DatagramSocket 对象,封装数据报包,发送数据。
- 创建 DatagramSocket 对象:使用DatagramSocket()构造方法创建一个 UDP 发送端的 DatagramSocket 对象。例如,DatagramSocket ds = new DatagramSocket()。
- 封装数据报包:将数据封装成数据报包,使用DatagramPacket(byte[] buf, int length, InetAddress address, int port)构造方法创建一个数据报包对象。其中,buf是数据的字节数组,length是数据的长度,address是目标地址,port是目标端口号。例如,String str = "连接上了偶"; byte[] buf = str.getBytes(); DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("127.0.0.1"), 10000)。
- 发送数据:使用DatagramSocket对象的send()方法发送数据报包。例如,ds.send(dp)。
- 接收端步骤:创建 DatagramSocket 对象,创建空数据报包,接收数据,解析数据。
- 创建 DatagramSocket 对象:使用DatagramSocket(int port)构造方法创建一个 UDP 接收端的 DatagramSocket 对象,指定监听的端口号。例如,DatagramSocket ds = new DatagramSocket(10000)。
- 创建空数据报包:使用DatagramPacket(byte[] buf, int length)构造方法创建一个空的数据报包对象,用于接收数据。其中,buf是用于存储接收数据的字节数组,length是字节数组的长度。例如,byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf, buf.length)。
- 接收数据:使用DatagramSocket对象的receive()方法接收数据报包。例如,ds.receive(dp)。
- 解析数据:通过数据报包对象的方法解析接收到的数据,包括目标地址、端口号和数据内容。例如,String ip = dp.getAddress().getHostAddress(); int port = dp.getPort(); String text = new String(dp.getData(), 0, dp.getLength())。
4. URL 编程
- URL 类:处理 URL 地址,下载相关数据。URL 类位于java.net包中,用于表示统一资源定位符(Uniform Resource Locator)。它提供了与 Web 资源交互的功能,包括解析 URL 字符串、打开与 URL 相关联的连接、读取和写入数据等。
- 创建 URL 对象:可以使用URL(String spec)构造方法通过指定的 URL 字符串创建一个 URL 对象。例如,URL url = new URL("https://www.example.com:80/docs/resource1.html?name=example#section1")。
- 获取 URL 的各个部分:URL 类提供了一些方法用于获取 URL 的各个部分,如getProtocol()获取协议部分,getHost()获取主机名,getPort()获取端口号,getPath()获取路径部分,getQuery()获取查询字符串,getFile()获取文件名和查询字符串,getRef()获取片段标识符。
- 读取 URL 资源:可以使用openStream()方法打开一个输入流,以读取此 URL 表示的资源。例如,BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); String line; while ((line = reader.readLine())!= null) { System.out.println(line); }。
- URLConnection 类:针对 HTTP 协议,进行网络连接和数据传输。URLConnection 类用于表示与 URL 建立的通信连接,它是通过 URL 类的openConnection()方法获得。可以使用 URLConnection 类进行 HTTP 请求和响应的处理,包括设置请求头、获取响应头、读取响应内容等。
5. 多线程与网络编程
使用多线程处理多个客户端连接,提高服务器的并发处理能力。在服务器端编程中,当有多个客户端同时连接时,如果使用单线程处理,那么在处理一个客户端请求时,其他客户端的请求就会被阻塞,等待当前请求处理完成。而使用多线程,可以为每个客户端连接创建一个独立的线程进行处理,这样可以同时处理多个客户端请求,提高服务器的并发处理能力。例如,可以将与单个客户端交互的操作封装到线程中,每当与一个新的客户端建立连接,就开启一个异步线程处理与该客户端之间的交互。这样可以有效地提高服务器的并发性能,减少客户端的等待时间。