网络编程可以让多台电脑实现串联,网络编程就是服务器端与客户端编程的开发。主流网络编程有两种形式:
第一种是C/S结构,也就是客户、服务器结构,缺点是麻烦,维护不便,因为要开发两套代码,也要维护两套代码。但是好处是安全,因为使用自己的连接端口,使用自己的通讯协议。
第二种是B/S结构,只开发一套服务器端代码,客户端使用浏览器访问。好处是利于开发和维护,坏处是使用公共的HTTP协议和公共的80端口,造成我们的程序安全性不高。
在C/S结构中,程序又可以分为两类,TCP程序与UDP程序。TCP使用可靠的连接方式进行传输。UDP使用不可靠的协议,属于数据包协议。
在Java中为我们提供的类就是ServerSocket类(服务器类)和Socket类(客户端类)。服务器类主要在服务器端工作,用于接收用户请求。客户端类表示每一个连接到服务器的用户。
我们使用一个经典的程序开发模型——Echo程序来做一个简单的演示。
要求:我们要实现客户端发出一个数据,服务器端接收到之后 加上一个"Echo"进行返回。允许多次输入,不能在每次连接之后都关闭服务器端。如果输入"exit"则退出程序。
我们来看一段代码。
package com.chuchu.server ; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class EchoServer { public static void main(String[] args) throws Exception { ServerSocket server = new ServerSocket(9999) ; System.out.println("等待客户端连接。。。"); Socket client = server.accept() ; // 客户端连接 Scanner scan = new Scanner(client.getInputStream()) ; PrintStream out = new PrintStream(client.getOutputStream()) ; boolean flag = true ; while(flag) { if(scan.hasNext()) { String value = scan.next() ; if("Exit".equalsIgnoreCase(value)) flag = false ; else out.println("【ECHO】" + value); } } server.close(); client.close(); scan.close(); } } package com.chuchu.client ; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; public class EchoClient { private static final BufferedReader INPUT = new BufferedReader(new InputStreamReader(System.in)) ; public static void main(String[] args) throws Exception { Socket client = new Socket("localhost", 9999) ; Scanner scan = new Scanner(client.getInputStream()) ; scan.useDelimiter("\n") ; PrintStream out = new PrintStream(client.getOutputStream()) ; boolean flag = true ; while(flag) { String input = getString("请输入要发送的内容") ; out.println(input); if(scan.hasNext()) { System.out.println(scan.next()); } if("Exit".equalsIgnoreCase(input)) { flag = false ; } } client.close(); scan.close(); INPUT.close(); } public static String getString(String promot) throws Exception { System.out.println(promot); String value = INPUT.readLine() ; return value ; } }在上面的代码中我们首先使用了Java提供的两个类实例化了服务器端和客户端,在服务器端我们使用ServerSocket对象中的accept()方法连接到客户端,获取Socket对象,然后再利用Socket对象中的getInputStream()方法和getOutputStream()方法获取输入输出流。在这里我们可以使用PrintStream类帮助我们在每次获取完成数据之后进行刷新操作。
客户端类大同小异,只不过我们可以将输入数据的代码封装成一个方法,并将键盘输入流封装成一个常量。
我们来看一下运行结果: 通过运行结果我们看到,服务器端成功接收到数据并将其处理后返回给了客户端。一个简单的Echo模型就算是完成了。但是当我们尝试再次启动一个客户端的时候,问题就出现了。
我们发现报错,Exception in thread “main” java.net.ConnectException: Connection refused: connect 这是由于我们的这个程序是单线程的,要向被多个用户访问我们需要将其改造成多线程。服务器端的每一个线程都为一个客户端实现Echo服务支持。
在上面代码中我们实现了一个内部类,这个类实现Runnable接口,构造方法里面为客户对象赋值,获取输入输出流。在run()方法中我们执行所有业务操作。
而在main()方法中我们写一个while循化接收客户端对象,没接收到一个就为此对象单开一个线程,为其服务。
最后不要忘记关闭资源。