1. 基础

Java 提供了非常易用的网络 API,调用这些 API 我们可以很方便的通过建立 TCP/IP 或者 UDP 套接字,在网络之间进行相互同新,其中 TCP 要比 UDP 更加安全和常用。

尽管 Java 网络 API 允许我们通过套接字(socket)打开或关闭连接,但所有的网络同新均是基于 Java IO 类 InputStream 和 OutputStream 实现的。

Java TCP 网络编程

通常情况下,客户端打开一个连接到服务器端 TCP/IP 连接,然后客户端开始与服务器之间通信,当通信结束后客户端关闭连接。

客户端通过一个已经打开的连接可以发送不止一个请求。事实上在服务器处于接收状态下,客户端可以发送尽可能多的数据,服务器也可以主动关闭连接。

Java 中 Socket 和 ServerSocket 类

当客户端想要打开一个连接到服务器的 TCP/IP 连接时,就要使用到 Java Socket 类。Socket 类只需要被告知连接的 IP 地址和 TCP 端口,其余都是 Java 实现。

假如我们想要打开一个监听服务,来监听客户端某些指定 TCP 端口连接,那就需要使用 Java ServerSocket 类。当客户端通过 Socket 连接服务器端的 ServerSocket 监听时,服务器端会指定这个连接的一个 Socket,此时客户端与服务器间的通信就变成 Socket 与 Socket 之间的通信。

Java UDP 网络基础

UDP 的工作方式于 TCP 略有不同。通过 UDP 通信时,在客户端与服务器之间并没有建立连接的概念,客户端发送到服务器的数据,服务器可能(也并没有接收到这些数据)而且客户端也并不知道这些数据是否被服务器成功接收。当服务器向客户端发送数据时候也是如此。

正式因为不可靠的数据传输,UDP 相比 TCP 减少了很多协议开销。

2. Socket

当我们想要在 Java 中使用 TCP/IP 通过网络连接到服务器时,就需要创建 java.net.Socket 对象并连接到服务器。

创建 Socket

下面代码是连接到 IP 为 127.0.0.1 服务器上的 8080 端口,这台服务器是我们的 Web 服务器

Socket socket = new Socket("127.0.0.1", 8080);

当然了我们也可以使用域名代替 IP

Socket socket = new Socket("www.baidu.com", 80)

Socket 发送数据

要通过 Socket 发送数据,我们需要获取 Socket 的输出流(OutputStream):

Socket socket = new Socket("127.0.0.1", 8080);
OutputStream out = socket.getOutputStream();

out.write("some thing".getBytes());
out.flush();
out.close();

socket.close();

操作系统能底层的 TCP/IP 首先会将数据放入一个更大的缓存块中,而缓存块的大小是与 TCP/IP 的数据包大小相适应的。

Socket 读取数据

从 Socket 中读取数据,我们就需要获取 Socket 的输入流:

Socket socket = new Socket("127.0.0.1", 8080);
InputStream input = socket.getInputStream();

int data = input.read();
// read more data

input.close();
socket.close();

需要注意的是,从 Socket 的输入流中读取数据并不能像读取文件那样,一直调用 read() 方法知道返回 -1 为止,因为对 Socket 而言,只有当服务器关闭连接时,Socket 的输入流才会返回 -1,而事实上服务器并不会不停地关闭连接,假设我们想通过一个连接发送多个请求的时候,在这种情况下关闭连接就显得非常蠢。

因此,从 Socket 的输入流中读取数据时我们必须要知道需要填写读取的字节数,这可以通过让服务器在数据中告知发送了多少字节来实现,也可以采用在数据末尾设置特殊字符标记方式实现。

关闭 Socket

当使用完 Socket 我们必须将 Socket 关闭,断开与服务器之间的连接。关闭 Socket 只需要调用 Socket.close() 方法即可:

Socket socket = new Socket("127.0.0.1", 8080);
socket.close();


3. ServerSocket

用 java.net.ServerSocket 实现 java 服务器通过 TCP/IP 监听客户端连接。

创建一个 ServerSocket 连接

以下是一个创建 ServerSocket 类来监听 9000 端口的简单的代码。

ServerSocket serverSocket = new ServerSocket(9000)

监听请求连接

要获取请求的连接需要用 ServerSocket() 方法。该方法返回一个 Socket 类,该类具有普通 Java Socket 类的所有特性。

ServerSocket serverSocket = new ServerSocket(9000);
boolean isStopped = false;
while(!isStopped){
    Socket clientSocket = serverSocket.accept();
}

对每个调用了 accept() 方法的类都只获得一个请求连接。

另外,请求的连接也只能在线程运行的 Server 中调用 accept() 方法之后才能够接受请求。线程运行在 Server 中其它所有的方法上的时候都不能接受客户端的连接请求。所以“接受”请求的线程通常会把 Socket 的请求连接放入一个工作线程池中,然后在和客户端连接。

关闭客户端 Socket

客户端请求执行完毕,并且不会再有该客户端的其它请求发过来的手,就需要关闭 Socket 连接,这和关闭一个普通客户端的连接一样的:

socket.close();

关闭服务器 Socket

要关闭服务的时候需要关掉 ServerSocket 连接:

serverSocket.close();