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();