03、ServerSocket 实例
接下来,我们模拟一个远程服务,通过 java.net.ServerSocket 实现。代码示例如下。
try (ServerSocket server = new ServerSocket(8888); Socket socket = server.accept(); InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); Scanner scanner = new Scanner(is)) { PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true); pw.println("你好啊,欢迎关注「沉默王二」 公众号,回复关键字「2048」 领取程序员进阶必读资料包"); boolean done = false; while (!done && scanner.hasNextLine()) { String line = scanner.nextLine(); System.out.println(line); if ("2048".equals(line)) { done = true; } } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
1)建立服务器端的套接字也比较简单,只需要指定一个能够独占的端口号就可以了(0~1023 这些端口都已经被系统预留了)。
ServerSocket server = new ServerSocket(8888);
2)调用 ServerSocket 对象的 accept() 等待客户端套接字的连接请求。一旦监听到客户端的套接字请求,就会返回一个表示连接已建立的 Socket 对象,可以从中获取到输入流和输出流。
Socket socket = server.accept();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
客户端套接字发送的所有信息都会包裹在服务器端套接字的输入流中;而服务器端套接字发送的所有信息都会包裹在客户端套接字的输出流中。
3)服务器端可以通过以下代码向客户端发送消息。
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true);
pw.println("你好啊,欢迎关注「沉默王二」 公众号,回复关键字「2048」 领取程序员进阶必读资料包");
4)服务器端可以通过以下代码读取客户端发送过来的消息。
Scanner scanner = new Scanner(is); boolean done = false; while (!done && scanner.hasNextLine()) { String line = scanner.nextLine(); System.out.println(line); if ("2048".equals(line)) { done = true; } }
运行该服务后,可以通过 telnet localhost 8888 命令连接该远程服务,不出所料,你将会看到以下信息。
PS:可以在当前命令窗口中输入 2048,服务端收到该消息后会中断该套接字连接(当前窗口会显示“遗失对主机的连接”)。
04、为多个客户端服务
非常遗憾的是,上面的例子中,服务器端只能为一个客户端服务——这不符合服务器端一对多的要求。
优化方案也非常简单(你应该也能想得到):服务器端接收到客户端的套接字请求时,可以启动一个线程来处理,而主程序继续等待下一个连接。代码示例如下。
try (ServerSocket server = new ServerSocket(8888)) { while (true) { Socket socket = server.accept(); Thread thread = new Thread(new Runnable() { @Override public void run() { // 套接字处理程序 } }); thread.start(); } } catch (IOException e) { e.printStackTrace(); }
线程内部(run(){} 方法里)用来处理套接字,代码示例如下:
try { InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); Scanner scanner = new Scanner(is); // 其他代码省略 // 向客户端发送消息 // 读取客户端发送过来的消息 } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } }
服务器端代码优化后重新运行,你就可以通过 telnet 命令测试了。打开一个命令行窗口输入 telnet localhost 8888,再打开一个新的命令行窗口输入 telnet localhost 8888,多个窗口都可以和服务器端进行通信,除非服务器端代码中断运行。
05、最后
如今大多数基于网络的软件,如浏览器、即时通讯工具甚至是 P2P 下载都是基于 Socket 实现的,所以掌握 Java Socket 编程还是蛮有必要的。Socket 编程也比较有趣,很多初学者都会编写一两个基于“客户端-服务器端”的小程序来提高自己的编程水平,建议你也试一试。
