网络编程的核心意义在于不同的电脑主机之间进行的数据交互,但是在Java中将这一概念又进一步进行了简化,即:Java是以JVM进程划分网络的(可能一台或多台电脑上会同时运行多个JVM,那么这些不同的JVM彼此都是一台主机),不同的JVM进程表示不同的主机
网络编程分类
网络编程的实质意义在于数据的交互,而在这一交互过程之中一定就会分为服务器端与客户端,而这两端的开发就会存在有以下两种模式:
Ø 形式一:C/S结构(Client / Server),此类模式的开发一般要编写两套程序,一套是客户端代码,另外一套属于服务器端代码。由于需要有编写程序,所以对于开发以及维护的成本较高。但是由于其使用的是自己的连接端口与交换协议,所以安全性比较高。而C/S结构程序的开发分为两种:TCP(传输控制协议,可靠的传输)、UDP(数据报协议)。
Ø 形式二:B/S结构(Browser / Server),不再单独开发客户端代码,只开发一套服务器端程序,客户端将利用浏览器进行访问,这种模式只需要开发一套程序,但是安全性不高,因为使用的是公共的HTTP协议以及公共的80端口。
java.net包提供了网络编程有关的开发工具类,在此包中有两个主要的核心操作类:
ServerSocket类:是一个封装支持TCP协议的操作类,主要工作在服务器端,用于接收客户端请求;
Socket类:也是一个封装了TCP协议的操作类,每一个Socket对象都表示一个客户端。
ServerSocket类的常用方法
No. | 方法名称 | 类型 | 描述 |
1 | public ServerSocket(int port) throws IOException | 构造 | 开辟一个指定的端口监听,一般使用5000以上 |
2 | public Socket accept() throws IOException | 普通 | 服务器端接收客户端请求,通过Socket返回 |
3 | public void close() throws IOException | 普通 | 关闭服务器端 |
Socket类的常用方法
No. | 方法名称 | 类型 | 描述 |
1 | public Socket(String host, int port) throws UnknownHostException, IOException | 构造 | 指定要连接的主机(IP地址)和端口 |
2 | public OutputStream getOutputStream() throws IOException | 普通 | 取得指定客户端的输出对象,使用的时候肯定使用PrintStream装饰操作 |
3 | public InputStream getInputStream() throws IOException | 普通 | 从指定的客户端读取数据,使用Scanner操作 |
客户端与服务器端交互
在客户端,程序可以通过Socket类的getInputStream()方法,取得服务器的输出信息,在服务器端可以通过getOutputStream()方法取得客户端的输出信息
定义服务器端 —— 主要使用ServerSocket
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.close();
client.close();
server.close();
}
}
编写客户端 —— Socket
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") ; // 设置分隔符
if (scan.hasNext()) { // 是否有数据
System.out.println("【回应数据】" + scan.next());// 取出数据
}
scan.close();
client.close();
}
}
Echo程序
在网络编程之中ECHO是一个经典的程序开发模型,本程序的意义在于:客户端随意输入信息并且将信息发送给服务器端,服务器端接收后前面加上一个“ECHO:”的前缀标记后将数据返还给客户端。在本程序中服务器端即要接收客户端发送来的数据,也要向客户端输出数据,同时考虑到需要进行多次数据交换,所以每次连接之后不应该立刻关闭服务器,而当用户输入了一些特定字符串(例如:“byebye”)后才表示可以结束本次的Echo操作。
实现服务器端
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 : ”前缀返回
}
}
}
server.close();
}
}
定义客户端
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()); // 输出回应数据
}
}
}
}
}
建立线程处理
在实际的开发中一个服务器需要同时处理多个客户端的请求操作,在这样的情况下就可以利用多线程来进行操作。把每一个连接到服务器端的客户都作为一个独立的线程对象保留