《第一行代码:Java》第12章、Java网络编程 读书笔记。有关计算机网络的基础知识建议去看计算机网络相关的文章,本章中主要以“C/S”开发中的TCP程序实现为主。
文章目录
-
- Java网络编程
-
- 12.1 网络编程
- 12.2开发第一个网络程序
- 12.3 网络开发的经典模型——Echo程序
- 本章小结:
Java网络编程
12.1 网络编程
网络编程的核心意义在于不同的电脑主机之间的数据交互,但是在Java中这一概念会进一步简化。在Java中是以JVM进程划分网络的,即不同的JVM代表不同的主机。Java中同一台主机的不同JVM之间的数据访问也属于远程访问。
-
网络编程的是指意义在于数据的交互,而在交互的过程中一定会将交互的双方分为服务器端和客户端,而这两端的开发会存在以下两种模式:
-
模式一:C/S结构(Client/Server),此类模式的开发一般要编写两套程序,一套是客户端代码,另外一套属于服务器端代码。由于需要有编写程序,所以对于开发以及维护的成本较高。但是由于其使用的是自己的连接端口与交换协议,所以安全性比较高。而C/S结构程序的开发分为两种:
- TCP(传输控制协议,一种面向连接的通信协议,可靠的传输)
- UDP(数据报协议,一种面向无连接的通讯协议)。
-
形式二:B/S结构(Browser /Server ),不再单独开发客户端代码,只开发一套服务器端程序,客户端将利用浏览器进行访问,这种模式只需要开发一套程序,但是安全性不高,因为使用的是公共的HTTP 协议以及公共的80端口。
本章主要以“C/S”开发中的TCP程序实现为主。
-
12.2开发第一个网络程序
-
在java.net包中,提供了网络编程相关的开发工具类,在此包中有以下两个主要的核心操作类。
- ServerSocket类:是一个封装支持TCP协议的操作类,主要工作是在服务器端,用于接收客户端请求。
- Socket类:也是一个封装了TCP协议的操作类,每个Socket对象都是一个客户端
-
java.net.ServerSocket类常用方法:
public ServerSocket(int port) throws IOException // 构造,开辟一个指定的端口监听,一般使用5000以上的端口 public Socket accept() throws IOException // 普通,服务器端接收客户端请求,通过Socket返回 public void close() throws IOException // 普通,返回服务器端
-
java.net.Socket类常用方法:
public Socket(String host, int port) throws UnknowHostException, IOException // 构造,指定要连接的主机(IP地址)和端口 public OutputStream getOutputStream() throws IOException // 普通,取得指定客户端的输出对象,使用PrintStream操作 public InputStream getInputStream() throws IOException // 普通,从指定的客户端读取数据,使用Scanner操作
-
在客户端,程序可以通过Socket类的getInputStream()方法,取得服务器的输出信息,在服务器端可以通过getOutputStream()方法取得客户端的输出信息,具体如下图所示:
-
定义服务器端——主要使用ServerSocket类:
package com.yootk.demo; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; public class HelloServer { public static void main(String[] args) throws Exception { ServerSocket server = new ServerSocket(9999) ; // 所有的服务器必须有端口System.out.println("等待客户端连接....."); // 提示信息Socket client = server.accept() ; // 等待客户端连接// OutputStream并不方便进行内容的输出,所以利用打印流完成输出PrintStream out = new PrintStream(client.getOutputStream()) ;out.println("Hello World !"); // 输出数据out.println("你好世界 !"); // 输出数据out.close(); client.close();server.close();} } // 程序执行结果: 等待客户端连接.....
本程序在本机的9999端口上设置了一个服务器的监听操作(accept()方法表示打开服务器监听,并且会一直等待,直到有客户端连接后才会执行下一步),当有客户端通过TCP连接方式连接到服务器端后,服务器端将会利用PrintSream输出数据,当数据输出完毕后,该服务器将会关闭,也就是说本次定义的服务器只能处理一次客户端的请求。
-
编写客户端——主要使用Socket类:
package com.yootk.demo; import java.net.Socket; import java.util.Scanner; public class HelloClient { public static void main(String[] args) throws Exception { Socket client = new Socket("localhost",9999) ; // 连接服务器端// 取得客户端的输入数据流对象,表示接收服务器端的输出信息Scanner scan = new Scanner(client.getInputStream()) ; // 接收服务器端回应数据scan.useDelimiter("\n") ; // 设置分隔符while (scan.hasNext()) { // 是否有数据System.out.println("【回应数据】" + scan.next()); // 取出数据}scan.close();client.close();} } /* 程序执行结果:Hello World !你好世界 ! */
在TCP程序中,每一个Socket对象都表示一个客户端的信息,所以客户端程序要连接也必须依靠Socket对象操作。在实例化Socket对象时必须设置连接的主机名称(本机为“localhost“,或者填写IP地址)以及连接端口号,当连接成功后就可以利用Scanner进行输入流数据的读取,这样就可以接收服务器端的回应信息了。
12.3 网络开发的经典模型——Echo程序
在网络编程中 Echo是一个经典的程序开发模型,本程序的意义在于:客户端随意输入信息并且将信息发送给服务器端,服务器端接收后前面加上一个”ECHO:”的前缀标记后将数据返还给客户端。在本程序中服务器端既要接收客户端发送来的数据,又要向客户端输出数据,同时考虑到需要进行多次数据交换,所以每次连接后不应该立刻关闭服务器,而当用户输入了一些特定字符串(例如:“byebye”)后才表示可以结束本次的Echo操作。
-
服务器端实现:
package com.yootk.demo; 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) ; // 定义连接端口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 str = scan.next().trim() ; // 得到客户端发送的内容,并删除空格if (str.equalsIgnoreCase("byebye")) { // 程序结束标记out.println("拜拜,下次再会!"); // 输出结束信息flag = false ; // 退出循环} else { // 回应输入信息out.println("ECHO : " + str); // 加“ECHO :”前缀返回}}}scan.close();out.close();client.close(); server.close();} }
-
客户端实现:
package com.yootk.demo; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; public class EchoClient { public static void main(String[] args) throws Exception { Socket client = new Socket("localhost", 9999); // 服务器地址与端口Scanner input = new Scanner(System.in); // 键盘输入数据// 利用Scanner包装客户端输入数据(服务器端输出),PrintStream包装客户端输出数据;Scanner scan = new Scanner(client.getInputStream());PrintStream out = new PrintStream(client.getOutputStream());input.useDelimiter("\n"); // 设置键盘输入分隔符scan.useDelimiter("\n"); // 设置回应数据分隔符boolean flag = true; // 循环标志while (flag) { System.out.print("请输入要发送数据:");if (input.hasNext()) { // 键盘是否输入数据String str = input.next().trim(); // 取得键盘输入数据out.println(str); // 发送数据到服务器端if (str.equalsIgnoreCase("byebye")) { // 结束标记flag = false; // 结束循环}if (scan.hasNext()) { // 服务器端有回应System.out.println(scan.next()); // 输出回应数据}}}input.close();scan.close();out.close();client.close();} } /* 程序执行结果:请输入您需要发送的数据:aewcdsacsa5545Echo:aewcdsacsa5545请输入您需要发送的数据:54d5f4adEcho:54d5f4ad请输入您需要发送的数据:sdafsa4Echo:sdafsa4请输入您需要发送的数据:byebye拜拜,下次再见! */
-
上面的程序实现了一个最简单的服务器端与客户端通讯,但是该程序只能连接一个客户端,不能连接其他客户端,因为所有的操作都是在主线程上进行的开发,也就是说该程序属于单线程的网络应用。而在实际的开发中一个服务器需要同时处理多个客户端的请求操作,在这样的情况下就可以利用多线程来进行操作,把每一个连接到服务器端的客户都作为一个独立的线程对象保留,如图12-3所示:
-
多线程服务器端实现:
package com.yootk.demo; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; class EchoThread implements Runnable { // 建立线程类private Socket client; // 每个线程处理一个客户端public EchoThread(Socket client) { // 创建线程对象时传递Socketthis.client = client;}@Overridepublic void run() { try { // 每个线程对象取得各自Socket的输入流与输出流Scanner scan = new Scanner(client.getInputStream());PrintStream out = new PrintStream(client.getOutputStream());boolean flag = true; // 控制多次接收操作while (flag) { if (scan.hasNext()) { // 是否有内容String str = scan.next().trim(); // 得到客户端发送的内容if (str.equalsIgnoreCase("byebye")) { // 程序结束out.println("拜拜,下次再会!");flag = false; // 退出循环} else { // 应该回应输入信息out.println("ECHO : " + str); // 回应信息}}}scan.close();out.close();client.close();} catch (Exception e) { e.printStackTrace();}} } public class EchoServer { public static void main(String[] args) throws Exception { ServerSocket server = new ServerSocket(9999); // 在9999端口上监听boolean flag = true; // 循环标记while (flag) { // 接收多个客户端请求Socket client = server.accept(); // 客户端连接new Thread(new EchoThread(client)).start(); // 创建并启动新线程}server.close();} }
本章小结:
- ServerSocket主要用于TCP协议的服务器程序开发上,使用accept()方法等待客户端连接,每个连接的客户端都是用一个SOcket表示。
- 服务器端加入多线程机制后,就可以同时为多个用户提供服务。