使用 Java 进行网络编程时,由虚拟机实现了底层复杂的网络协议,Java 程序只需要调用 Java 标准库提供的接口,就可以简单高效地编写网络程序。Java 提供的这些标准库存在于 java.net 包下。
Socket 是一个抽象概念,一个应用程序通过一个 Socket 来建立一个远程连接,而 Socket 内部通过 TCP/IP 协议把数据传输到网络。Socket、TCP 和部分 IP 的功能都是由操作系统提供的,不同的编程语言只是提供了对操作系统调用的简单的封装。
public class Server { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; OutputStream output = null; try { serverSocket = new ServerSocket(8888); System.out.println("Server is running..."); // 没连接就会阻塞 socket = serverSocket.accept(); output = socket.getOutputStream(); output.write("您好,欢迎访问服务端!".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { //由下向上,依次关闭 if(output != null ){ try { output.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null ){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(serverSocket != null ){ try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
结果:Server is running...
public class Client { public static void main(String[] args) { Socket socket = null; InputStream input = null; try { InetAddress addressIP = InetAddress.getByName("127.0.0.1"); int port = 8888; socket = new Socket(addressIP, port); //尝试建立连接 input = socket.getInputStream(); byte[] bytes = new byte[1024]; int len; StringBuilder sb = new StringBuilder(); while ((len = input.read(bytes)) != -1) { sb.append(new String(bytes, 0, len, "UTF-8")); } System.out.println(sb); } catch (Exception e) { e.printStackTrace(); } finally { if(input != null ){ try { input.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null ){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
结果:您好,欢迎访问服务端!
UDP编程就简单得多,因为UDP没有创建连接,数据包也是一次收发一个,所以没有流的概念。
public class Server { public static void main(String[] args) { DatagramSocket ds = null; try { ds = new DatagramSocket(6666); // 监听指定端口 for (;;) { // 数据缓冲区: byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); ds.receive(packet); // 收取一个UDP数据包 String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8); System.out.println(s); // 发送数据: byte[] data = "请求已收到!".getBytes(StandardCharsets.UTF_8); packet.setData(data); ds.send(packet); } } catch (IOException e) { e.printStackTrace(); } finally { ds.close(); } } }
结果:你好,我发了个包!
public class Client { public static void main(String[] args) { DatagramSocket ds = null; try { ds = new DatagramSocket(); ds.setSoTimeout(1000); ds.connect(InetAddress.getByName("localhost"), 6666); // 发送: byte[] data = "你好,我发了个包!".getBytes(); DatagramPacket packet = new DatagramPacket(data, data.length); ds.send(packet); // 接收: byte[] buffer = new byte[1024]; packet = new DatagramPacket(buffer, buffer.length); ds.receive(packet); String resp = new String(packet.getData(), packet.getOffset(), packet.getLength()); System.out.println(resp); ds.disconnect(); } catch (Exception e) { e.printStackTrace(); } finally { ds.close(); } } }
结果:请求已收到!
HTTP 是 HyperText Transfer Protocol 的缩写,翻译为超文本传输协议,它是基于 TCP 协议之上的一种请求-响应协议。HTTP 请求的格式是固定的,它由 HTTP Header 和 HTTP Body 两部分构成。
服务器端的 HTTP 编程本质上就是编写 Web 服务器,这是一个非常复杂的体系,也是 JavaEE 开发的核心内容,本次就不实现了。
浏览器也是一种 HTTP 客户端,客户端的 HTTP 编程,它的行为本质上和浏览器是一样的,即发送一个 HTTP 请求,接收服务器响应后,获得响应内容。
public class Client { public static void main(String[] args) throws IOException { URL url = new URL("https://www.baidu.com/"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setUseCaches(false); conn.setConnectTimeout(5000); // 请求超时5秒 // 设置HTTP头: conn.setRequestProperty("Accept", "*/*"); conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 11; Windows NT 5.1)"); // 连接并发送HTTP请求: conn.connect(); // 判断HTTP响应是否200: if (conn.getResponseCode() != 200) { throw new RuntimeException("bad response"); } // 获取所有响应Header: Map<String, List<String>> map = conn.getHeaderFields(); for (String key : map.keySet()) { System.out.println(key + ": " + map.get(key)); } // 获取响应内容: InputStream input = conn.getInputStream(); } }
从 Java 11 开始,引入了新的 HttpClient,它使用链式调用的 API,能大大简化 HTTP 的处理。