目录
前言
在当今这个高度互联的世界中,网络编程已成为软件开发不可或缺的一部分。无论是构建Web服务、移动应用还是物联网解决方案,都需要通过网络进行数据交换和资源共享。本篇博客旨在为读者提供一个关于网络编程基础的介绍,特别是针对使用Java语言进行网络编程的情况。我们将从网络编程的核心概念入手,探讨IP地址、端口号以及通信协议的重要性。随后,将介绍如何利用Java的标准库来实现基于TCP和UDP协议的客户端-服务器应用程序。
一、概述
网络编程是指利用计算机网络实现程序之间通信的一种编程方式。在网络编程中,程序需要通过网络协议(如 TCP/IP)来进行通信,以实现不同计算机之间的数据传输和共享。在网络编程中,通常有三个基本要素:
| 要素 | 描述 |
|---|---|
| IP 地址 | 定位网络中某台计算机 |
| 端口号port | 定位计算机上的某个进程(某个应用) |
| 通信协议 | 通过IP地址和端口号定位后,如何保证数据可靠高效的传输,这就需要依靠通信协议了 |
二、网络编程三要素
1. IP地址
IP 地址用于唯一标识网络中的每一台计算机。在 Internet 上,使用 IPv4 或 IPv6 地址来表示 IP 地址。通常 IPv4 地址格式为 xxx.xxx.xxx.xxx,其中每个 xxx 都表示一个 8 位的二进制数(每一个xxx的取值范围是0-255),组合起来可以表示 2^32 个不同的 IP 地址。 IPv4 地址的总数量是4294967296 个,但并不是所有的 IPv4 地址都可以使用。IPv4 地址被分为网络地址和主机地址两部分,前3个字节用于表示网络(省市区),最后1个字节用于表示主机(家门牌)。而一些 IP 地址被保留或者被私有机构使用,不能用于公网的地址分配。另外,一些 IP 地址被用作多播地址,仅用于特定的应用场景。因此实际上可供使用的 IPv4 地址数量要少于总数量,而且随着 IPv4 地址的逐渐枯竭,IPv6 地址已经开始逐渐普及,IPv6 地址数量更是相当巨大。 IPv6使用16个字节表示IP地址(128位),这样就解决了网络地址资源数量不够的问题。IPv6 地址由 8 组 16 位十六进制数表示,每组之间用冒号分隔,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984。
补充: 本机地址为127.0.0.1,主机名为localhost;192.168.0.0-192.168.255.255为私有地址,属于非注册地址,专门为组织机构内部使用。
IP地址毕竟是数字标识,使用时不好记忆和书写,因此在IP地址的基础上又发展出一种符号化的地址方案,来代替数字型的IP地址。每一个符号化的地址都与特定的IP地址对应。这个与网络上的数字型IP地址相对应的字符型地址,就被称为域名。 目前域名已经成为互联网品牌、网上商标保护必备的要素之一,除了识别功能外,还有引导、宣传等作用。如:www.baidu.com。在Internet上域名与IP地址之间是一对一(或者多对一)的,域名虽然便于人们记忆,但机器之间只能互相认识IP地址,它们之间的转换工作称为域名解析,域名解析需要由专门的域名解析服务器来完成,DNS(Domain Name System域名系统)就是进行域名解析的服务器,域名的最终指向是IP。
2. 端口号
在计算机中,不同的应用程序是通过端口号区分的。 端口号是用两个字节(无符号)表示的,它的取值范围是0~65535,而这些计算机端口可分为3大类:
- 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23等)
- 注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。
- 动态/私有端口:49152~65535。 通常情况下,服务器程序使用固定的端口号来监听客户端的请求,而客户端则使用随机端口连接服务器。
IP地址好比每个人的地址(门牌号),端口好比是房间号。必须同时指定IP地址和端口号才能够正确的发送数据。接下来通过一个图例来描述IP地址和端口号的作用,如图所示:

3. 通信协议
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则。就像两个人想要顺利沟通就必须使用同一种语言一样,如果一个人只懂英语而另外一个人只懂中文,这样就会造成没有共同语言而无法沟通。 在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。 在计算机网络中,常用的协议有 TCP、UDP、HTTP、FTP 等。这些协议规定了数据传输的格式、传输方式和传输顺序等细节。其中,TCP(传输控制协议)是一种可靠的面向连接的协议,它提供数据传输的完整性保证;而 UDP(用户数据报协议)则是一种无连接的协议,传输效率高。在网络编程中,需要选取合适的协议类型来实现数据传输。
世界上第一个网络体系结构由IBM公司提出(1974年,SNA),以后其他公司也相继提出自己的网络体系结构如:Digital公司的DNA,美国国防部的TCP/IP等,多种网络体系结构并存,其结果是若采用IBM的结构,只能选用IBM的产品,只能与同种结构的网络互联。 为了促进计算机网络的发展,国际标准化组织ISO(International Organization for Standardization)于1977年成立了一个委员会,在现有网络的基础上,提出了不基于具体机型、操作系统或公司的网络体系结构,称为开放系统互连参考模型,即OSI/RM (Open System Interconnection Reference Model)。OSI模型把网络通信的工作分为7层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。OSI七层协议模型如图所示:

OSI参考模型的初衷是提供全世界范围的计算机网络都要遵循的统一标准,但是由于存在模型和协议自身的缺陷,迟迟没有成熟的产品推出。TCP/IP协议在实践中不断完善和发展取得成功,作为网络的基础,Internet的语言,可以说没有TCP/IP参考模型就没有互联网的今天。 TCP/IP,即Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,是Internet最基本的协议、Internet国际互联网络的基础。 TCP/IP协议是一个开放的网络协议簇,它的名字主要取自最重要的网络层IP协议和传输层TCP协议。TCP/IP协议定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。TCP/IP参考模型采用4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求,这4个层次分别是:网络接口层、互联网层(IP层)、传输层(TCP层)、应用层。 OSI模型与TCP/IP模型的对应关系如图所示:

OSI 参考模型是理论上的,而 TCP/IP 参考模型是实践上的。TCP/IP 参考模型被许多实际的协议(如 IP、TCP、HTTP 等)所支持和实现,而 OSI 参考模型则主要是作为理论框架和标准进行研究和讨论。 OSI 参考模型是由国际标准化组织提出的网络通信协议框架,其中分为 7 层,各层之间明确了功能的划分,从物理层到应用层,逐层向上升,每层只对自己下一层提供服务,并依次封装和解封数据。OSI 参考模型是一种理论上的协议框架,用于描述计算机系统间的通信原理和规范。 TCP/IP 参考模型(也称互联网参考模型)是实际应用中最广泛的协议框架。它将网络协议划分为 4 层:网络接口层、网络层、传输层和应用层。TCP/IP 参考模型与 OSI 参考模型之间有着相对应的层次结构,但是其中的每一层都是实际存在的协议,而不是纯粹的框架。TCP/IP 参考模型被广泛应用于互联网上,是计算机系统间进行通信的重要基础。
三、网络基础类
1. InetAddress类
java.net.IntAddress类用来封装计算机的IP地址和DNS(没有端口信息),它包括一个主机名和一个IP地址,是java对IP地址的高层表示。大多数其它网络类都要用到这个类,包括Socket、ServerSocket、URL、DatagramSocket、DatagramPacket等。IntAddress类常用静态方法和实例方法如下表所示:
| 方法 | 描述 |
|---|---|
| static InetAddress getLocalHost() | 得到本机的InetAddress对象,其中封装了IP地址和主机名 |
| static InetAddress getByName(String host) | 传入目标主机的名字或IP地址得到对应的InetAddress对象,其中封装了IP地址和主机名(底层会自动连接DNS服务器进行域名解析) |
| public String getHostAddress() | 获取IP地址 |
| public String getHostName() | 获取主机名/域名 |
下面是一段示例代码:
// 本机
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
System.out.println("本机主机名或域名为:" + localHost.getHostName());
System.out.println("本机IP地址为:" + localHost.getHostAddress());
// 百度
InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress);
System.out.println("远程主机名或域名为:" + inetAddress.getHostName());
System.out.println("远程IP地址为:" + inetAddress.getHostAddress());
运行结果如下图所示:

2. URL类
URL是统一资源定位符,对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。URL标准格式为:<协议>://<域名或IP>:<端口>/<路径>。其中,<协议>://<域名或IP>是必需的,<端口>/<路径>有时可省略。如:https://www.baidu.com。为了方便程序员编程,JDK中提供了URL类,该类的全名是java.net.URL,该类封装了大量复杂的涉及从远程站点获取信息的细节,可以使用它的各种方法来对URL对象进行分割、合并等处理。下面是URL类的一个使用示例:
URL url = new URL("http://127.0.0.1:8080/oa/index.html?name=zhangsan#tip");
String protocol = url.getProtocol();
System.out.println("协议:" + protocol);
String host = url.getHost();
System.out.println("主机地址:" + host);
int port = url.getPort();
System.out.println("端口:" + port);
int defaultPort = url.getDefaultPort();
System.out.println("默认端口:" + defaultPort);
String ref = url.getRef();
System.out.println("锚点:" + ref);
String path = url.getPath();
System.out.println("资源路径:" + path);
String query = url.getQuery();
System.out.println("查询参数:" + query);
String file = url.getFile();
System.out.println("资源路径 + 查询参数:" + file);
运行结果如下图所示:

补充:
锚点(Anchor Point 或 Fragment Identifier)是一种HTML元素,常用于创建网页内部的链接,允许用户快速跳转到页面的特定部分。锚点通常由HTML中的
<a>标签与一个特定的ID属性结合使用来定义。当点击一个链接至特定ID的链接时,浏览器会滚动到拥有该ID的元素所在的位置。使用URL类的openStream()方法可以打开到此URL的连接并返回一个用于从该连接读入的InputStream,实现最简单的网络爬虫。
四、TCP与UDP协议
1. Socket套接字概述
我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接Socket来进行分离。 套接字就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据。而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心它如何传输,这属于网络其它层次工作。 Socket实际是传输层供给应用层的编程接口。Socket就是应用层与传输层之间的桥梁。使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。 TCP协议和UDP协议是传输层的两种协议。Socket是传输层供给应用层的编程接口,所以Socket编程就分为TCP编程和UDP编程两类。

2. TCP与UDP概述
TCP须先建立TCP连接,形成传输数据通道,似于拨打电话 传输前,采用“三次握手”方式,属于点对点通信,是面向连接的,效率低。 仅支持单播传输,每条TCP传输连接只能有两个端点(客户端、服务端)。 两个端点的数据传输,采用的是“字节流”来传输,属于可靠的数据传输。 传输完毕,需释放已建立的连接,开销大,速度慢,适用于文件传输、邮件等。UDP采用数据报(数据、源、目的)的方式来传输,无需建立连接,类似于发短信。 每个数据报的大小限制在64K内,超出64k可以分为多个数据报来发送。 发送不管对方是否准备好,接收方即使收到也不确认,因此属于不可靠的。 可以广播发送,也就是属于一对一、一对多和多对一连接的通信协议。 发送数据结束时无需释放资源,开销小,速度快,适用于视频会议、直播等。下面是TCP与UDP的比较:
3. TCP的三次握手
TCP(传输控制协议)是一种面向连接的、可靠的传输层协议。它使用三次握手来建立连接,以确保数据在两个设备之间可靠地传输。三次握手完成后,客户端和服务器就可以开始交换数据了。三次握手的过程如下:
- 客户端发送 SYN(同步)数据包。这个数据包包含客户端的初始序列号(ISN)。
- 服务器收到 SYN 数据包后,发送 SYN-ACK(同步确认)数据包。这个数据包包含服务器的初始序列号(ISN)和对客户端 ISN 的确认号(ACK)。
- 客户端收到 SYN-ACK 数据包后,发送 ACK(确认)数据包。这个数据包包含对服务器 ISN 的确认号(ACK)。

三次握手可以确保数据在两个设备之间可靠地传输。它可以防止以下情况的发生:
- 不会丢失:如果没有三次握手,客户端和服务器可能会同时发送数据,导致数据丢失。
- 不会重复:如果没有三次握手,客户端和服务器可能会重复发送数据,导致数据重复。
- 不会乱序:如果没有三次握手,客户端和服务器可能会乱序发送数据,导致数据乱序。
4. TCP的四次挥手
TCP使用四次挥手来关闭连接,以确保数据在两个设备之间可靠地传输。四次挥手完成后,客户端和服务器之间的连接就关闭了。四次挥手的过程如下:
- 客户端发送 FIN(结束)数据包。这个数据包表示客户端已经完成数据传输,并希望关闭连接。
- 服务器收到 FIN 数据包后,发送 ACK(确认)数据包。这个数据包表示服务器已经收到客户端的 FIN 数据包,并同意关闭连接。
- 服务器发送 FIN 数据包。这个数据包表示服务器已经完成数据传输,并希望关闭连接。
- 客户端收到 FIN 数据包后,发送 ACK(确认)数据包。这个数据包表示客户端已经收到服务器的 FIN 数据包,并同意关闭连接。

四次挥手可以确保数据在两个设备之间可靠地传输。它可以防止以下情况的发生:
- 不会丢失:如果没有四次挥手,客户端和服务器可能会同时关闭连接,导致数据丢失。
- 不会重复:如果没有四次挥手,客户端和服务器可能会重复发送数据,导致数据重复。
- 不会乱序:如果没有四次挥手,客户端和服务器可能会乱序发送数据,导致数据乱序。
五、基于TCP协议的编程
1. TCP协议编程概述
套接字是一种进程间的数据交换机制,利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实上的标准。 在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client),而在第一次通讯中等待连接的程序被称作服务端(Server)。一旦通讯建立,则客户端和服务器端完全一样,没有本质的区别。 套接字与主机地址和端口号相关联,主机地址就是客户端或服务器程序所在的主机的IP地址,端口地址是指客户端或服务器程序使用的主机的通信端口。在客户端和服务器中,分别创建独立的Socket,并通过Socket的属性,将两个Socket进行连接,这样客户端和服务器通过套接字所建立连接并使用IO流进行通信。

2. Socket类概述
Socket类实现客户端套接字(Client),套接字是两台机器间通信的端点。Socket常用的一些方法如下所示:
| 方法 | 描述 |
|---|---|
| public Socket(InetAddress a, int p) | 构造方法,创建套接字并连接到指定IP地址的指定端口号 |
| public InetAddress getInetAddress() | 返回此套接字连接到的远程 IP 地址 |
| public InputStream getInputStream() | 返回此套接字的输入流(接收网络消息) |
| public OutputStream getOutputStream() | 返回此套接字的输出流(发送网络消息) |
| public void shutdownInput() | 禁用此套接字的输入流 |
| public void shutdownOutput() | 禁用此套接字的输出流 |
| public synchronized void close() | 关闭此套接字(默认会关闭IO流) |
3. ServerSocket类概述
ServerSocket类用于实现服务器套接字(Server服务端)。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。ServerSocket常用的一些方法如下所示:
| 方法 | 描述 |
|---|---|
| public ServerSocket(int port) | ServerSocket构造方法,指定占用的端口号 |
| public Socket accept() | 侦听要连接到此套接字并接受它 |
| public InetAddress getInetAddress() | 返回此服务器套接字的本地地址 |
| public void close() | 关闭此套接字 |
4. 示例
下面是基于TCP协议的客户端与服务端之间双向通信的例子,服务端代码如下:
public static void main(String[] args) throws IOException {
try(ServerSocket server = new ServerSocket(9999);
Socket socket = server.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))
) {
String msg;
while((msg = in.readLine()) != null) {
System.out.println("服务端接受到的消息:" + msg);
out.write("服务端接受到消息:" + msg + "\n");
out.flush();
}
}
}
客户端代码如下:
public static void main(String[] args) throws IOException {
try(Socket client = new Socket(InetAddress.getLocalHost(), 9999);
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
Scanner sc = new Scanner(System.in)
) {
String msg;
while (!"exit".equals(msg = sc.nextLine())) {
out.write(msg + "\n");
out.flush();
System.out.println(in.readLine());
}
}
}
运行结果如下图所示:

六、基于UDP协议的编程
1. UDP协议编程概述
在UDP通信协议下,两台计算机之间进行数据交互,并不需要先建立连接,发送端直接往指定的IP和端口号上发送数据即可,但是它并不能保证数据一定能让对方收到,也不能确定什么时候可以送达。 java.net.DatagramSocket类和java.net.DatagramPacket类是使用UDP编程中需要使用的两个类,并且发送端和接收端都需要使用这个俩类,并且发送端与接收端是两个独立的运行程序。
- DatagramSocket:负责接收和发送数据,创建接收端时需要指定端口号。
- DatagramPacket:负责把数据打包,创建发送端时需指定接收端的IP地址和端口。
2. DatagramSocket类概述
DatagramSocket类作为基于UDP协议的Socket,使用DatagramSocket类可以用于接收和发送数据,同时创建接收端时还需指定端口号。下面是DatagramSocket常用的一些方法:
| 方法 | 描述 |
|---|---|
| public DatagramSocket(); | 创建发送端的数据报套接字 |
| public DatagramSocket(int port); | 创建接收端的数据报套接字,并指定端口号 |
| public void send(DatagramPacket p); | 发送数据报 |
| public void receive(DatagramPacket p); | 接收数据报 |
| public void close(); | 关闭数据报套接字 |
3. DatagramPacket类概述
DatagramPacket类负责把发送的数据打包(打包的数据为byte类型的数组),并且创建发送端时需指定接收端的IP地址和端口,下面是DatagramPacket常用的一些方法:
| 方法 | 描述 |
|---|---|
| public DatagramPacket(byte buf[], int offset, int length) | 创建接收端的数据报 |
| public DatagramPacket(byte buf[], int offset, int length, InetAddress address, int port) | 创建发送端的数据报,并指定接收端的IP地址和端口号 |
| public synchronized byte[] getData() | 返回数据报中存储的数据 |
| public synchronized int getLength() | 获得发送或接收数据报中的长度 |
4. 示例
下面是一个UDP发送和接受的示例代码,发送端代码如下:
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
byte[] bytes = "你好你好".getBytes();
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getLocalHost(), 8090);
socket.send(packet);
socket.close();
}
接受端代码如下:
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(8090);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, 0, buf.length);
socket.receive(packet);
String data = new String(buf, 0, packet.getLength());
System.out.println("接受到数据:" + new String(data));
socket.close();
}
运行结果如下图所示:

总结
本文介绍了网络编程的基础知识,从IP地址、端口号到通信协议,这些是构成网络通信不可或缺的三个要素。我们了解到,IP地址用于定位网络中的计算机,端口号则用于区分同一台计算机上的不同服务,而通信协议则是确保数据能够高效可靠传输的关键。接着,我们探讨了两种重要的传输层协议——TCP和UDP,它们各自的特点及适用场景。为了更好地理解和掌握这些理论知识,我们还提供了具体的Java代码示例,展示了如何基于这两种协议实现简单的客户端-服务器架构下的消息传递功能。希望本篇博客能帮助到大家,让我们共同进步吧!

7万+

被折叠的 条评论
为什么被折叠?



