Java 网络编程(或 Java 网络编程)指的是编写跨多个设备(计算机)运行的程序,这些设备都通过网络相互连接。
Java 网络编程的优势
Java 网络编程使用的包
J2SE API 中的 java.net
包包含了提供底层通信细节的一组类和接口,允许您编写专注于解决手头问题的程序。
java.net
包提供了对两种常见的网络协议的支持:
-
TCP - 传输控制协议允许两个应用程序之间进行可靠的通信。通常与互联网协议(Internet Protocol)一起使用,称为 TCP/IP。
-
UDP - 用户数据报协议是一种无连接的协议,允许数据包在应用程序之间传输。
本章将详细介绍以下两个主题:
-
套接字编程 - 这是在网络中最广泛使用的概念,并在此详细解释。
-
URL 处理 - 这将在单独的部分中介绍。点击这里了解 Java 语言中的 URL 处理。
Java 网络编程中的套接字编程
套接字提供了使用 TCP 在两台计算机之间进行通信的机制。客户端程序在其通信端创建一个套接字并尝试连接到服务器。
当连接建立后,服务器在其通信端创建一个套接字对象。此时,客户端和服务器可以通过向套接字写入和从中读取来进行通信。
java.net.Socket
类表示一个套接字,而 java.net.ServerSocket
类则提供了服务器程序监听客户端并与其建立连接的机制。
使用套接字在两台计算机之间建立 TCP 连接时,会发生以下步骤:
-
服务器实例化一个
ServerSocket
对象,指定通信所使用的端口号。
-
服务器调用
ServerSocket
类的 accept()
方法。这个方法会一直等待直到一个客户端连接到指定端口的服务器。
-
当服务器正在等待时,客户端实例化一个
Socket
对象,指定要连接的服务器名称和端口号。
-
Socket
类的构造器尝试让客户端连接到指定的服务器和端口号。如果建立了通信,则客户端现在有了一个可以与服务器通信的 Socket
对象。
-
在服务器端,
accept()
方法返回一个与客户端套接字相连的新套接字的引用。
-
建立连接之后,可以通过输入/输出流进行通信。每个套接字都有一个
OutputStream
和一个 InputStream
。客户端的 OutputStream
与服务器的 InputStream
相连,而客户端的 InputStream
则与服务器的 OutputStream
相连。
TCP 是一种双向通信协议,因此可以在两个流上同时发送数据。以下是实现套接字所需的一些有用类及其完整的方法集。
ServerSocket 类的构造器
java.net.ServerSocket
类由服务器应用程序用来获取端口并监听客户端请求。
ServerSocket
类有四个构造器:
-
public ServerSocket(int port) throws IOException
-
尝试创建绑定到指定端口的服务器套接字。如果端口已被其他应用程序绑定,则会抛出异常。
-
public ServerSocket(int port, int backlog) throws IOException
-
与第一个构造器类似,backlog 参数指定了可以存储在等待队列中的传入客户端数量。
-
public ServerSocket(int port, int backlog, InetAddress address) throws IOException
-
与前一个构造器类似,
InetAddress
参数指定了要绑定的本地 IP 地址。这对于可能具有多个 IP 地址的服务器有用,可以让服务器指定接收客户端请求的 IP 地址。
-
public ServerSocket() throws IOException
-
创建一个未绑定的服务器套接字。使用此构造器时,在准备绑定服务器套接字时使用
bind()
方法。
如果 ServerSocket
构造器没有抛出异常,这意味着应用程序成功绑定到了指定的端口,并准备好接受客户端请求。
ServerSocket 类的方法
以下是 ServerSocket
类的一些常用方法:
-
public int getLocalPort()
-
返回服务器套接字正在监听的端口。如果您在构造器中传入了 0 作为端口号,并让服务器为您找到一个端口,此方法很有用。
-
public Socket accept() throws IOException
-
等待传入的客户端。此方法会阻塞直到客户端在指定端口连接到服务器或套接字超时,前提是使用
setSoTimeout()
方法设置了超时值。否则,此方法无限期阻塞。
-
public void setSoTimeout(int timeout)
-
设置服务器套接字在
accept()
期间等待客户端的时间。
-
public void bind(SocketAddress host, int backlog)
-
将套接字绑定到
SocketAddress
对象中指定的服务器和端口。如果使用无参数构造器实例化了 ServerSocket
,则需要使用此方法。
当 ServerSocket
调用 accept()
时,此方法不会返回直到有一个客户端连接。客户端连接后,ServerSocket
在一个未指定的端口上创建一个新的 Socket
并返回对该新 Socket
的引用。现在在客户端和服务器之间存在一个 TCP 连接,并可以开始通信。
Socket 类的构造器
java.net.Socket
类代表客户端和服务器用来相互通信的套接字。客户端通过实例化一个对象来获得 Socket
对象,而服务器则从 accept()
方法的返回值中获得 Socket
对象。
Socket
类有五个客户端用于连接服务器的构造器:
-
public Socket(String host, int port) throws UnknownHostException, IOException
-
尝试连接到指定端口的指定服务器。如果此构造器没有抛出异常,则连接成功并且客户端已连接到服务器。
-
public Socket(InetAddress host, int port) throws IOException
-
此构造器与前一个相同,不同之处在于主机由
InetAddress
对象表示。
-
public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException
-
连接到指定主机和端口,在指定地址和端口的本地主机上创建一个套接字。
-
public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException
-
此构造器与前一个相同,不同之处在于主机由
InetAddress
对象而不是字符串表示。
-
public Socket()
-
创建一个未连接的套接字。使用
connect()
方法将此套接字连接到服务器。
当 Socket
构造器返回时,它不仅实例化了一个 Socket
对象,实际上还尝试连接到指定的服务器和端口。
Socket 类的方法
以下是 Socket
类中一些感兴趣的方法。注意客户端和服务器都有 Socket
对象,因此这些方法可以由客户端和服务器调用。
-
public void connect(SocketAddress host, int timeout) throws IOException
-
此方法连接套接字到指定主机。仅当使用无参数构造器实例化
Socket
时需要此方法。
-
public InetAddress getInetAddress()
-
-
public int getLocalPort()
-
public SocketAddress getRemoteSocketAddress()
-
public InputStream getInputStream() throws IOException
-
返回套接字的输入流。输入流连接到远程套接字的输出流。
-
public OutputStream getOutputStream() throws IOException
-
返回套接字的输出流。输出流连接到远程套接字的输入流。
-
public void close() throws IOException
-
关闭套接字,使得此
Socket
对象不再能够再次连接到任何服务器。
InetAddress 类的方法
此类表示 Internet 协议(IP)地址。以下是您在网络编程中需要的一些有用方法:
-
static InetAddress getByAddress(byte[] addr)
-
给定原始 IP 地址返回
InetAddress
对象。
-
static InetAddress getByAddress(String host, byte[] addr)
-
根据提供的主机名和 IP 地址创建
InetAddress
。
-
static InetAddress getByName(String host)
-
-
-
static InetAddress getLocalHost()
-
Java 网络编程示例
Java 中实现 Socket 客户端
下面的 GreetingClient
是一个使用套接字连接到服务器并发送问候消息,然后等待响应的客户端程序。
示例:Socket 客户端
import java.net.*;
import java.io.*;
public class GreetingClient {
public static void main(String [] args) {
String serverName = args[0];
int port = Integer.parseInt(args[1]);
try {
System.out.println("Connecting to " + serverName + " on port " + port);
Socket client = new Socket(serverName, port);
System.out.println("Just connected to " + client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hello from " + client.getLocalSocketAddress());
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("Server says " + in.readUTF());
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Java 中实现 Socket 服务器
下面的 GreetingServer
程序是一个服务器应用的例子,它使用 Socket
类在命令行参数指定的端口上监听客户端连接。
示例:Socket 服务器
import java.net.*;
import java.io.*;
public class GreetingServer extends Thread {
private ServerSocket serverSocket;
public GreetingServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(10000);
}
public void run() {
while(true) {
try {
System.out.println("Waiting for client on port " +
serverSocket.getLocalPort() + "...");
Socket server = serverSocket.accept();
System.out.println("Just connected to " + server.getRemoteSocketAddress());
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println(in.readUTF());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("Thank you for connecting to " + server.getLocalSocketAddress()
+ "\nGoodbye!");
server.close();
} catch (SocketTimeoutException s) {
System.out.println("Socket timed out!");
break;
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
public static void main(String [] args) {
int port = Integer.parseInt(args[0]);
try {
Thread t = new GreetingServer(port);
t.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
编译客户端和服务器并启动服务器
$ java GreetingServer 6066
Waiting for client on port 6066...
检查客户端程序如下:
输出
$ java GreetingClient localhost 6066
Connecting to localhost on port 6066
Just connected to localhost/127.0.0.1:6066
Server says Thank you for connecting to /127.0.0.1:6066
Goodbye!