|
|
《JavaTM程序设计语言基 础》第
2 部分 《JavaTM 程序设计语言基础》第 1 部分 的结尾部分介绍了一个采用远程方法调用 (RMI)应用编程接口(API)的简单网络通信示例程序。RMI 示例程序允许多个 客户程序与同一个服务器程序通信,但这一点并没有清晰地在代码中表现出来, 因为 RMI API 是在网络接口(socket)和线程(thread)基础上生成的。 本课通过一个基于网络接口的简单程序来介绍网络接口和多线程程序设计的概 念。多线程程序可在同一时刻执行多项任务,如:监视多个客户程序的同时请求 等。
注意: 生成放映幻灯片的线程化 Applet 程序部分介绍了如何在同一个程序中使用 多个线程的另一个示例。 什么是网络接口和线程?网络接口是实现服务器程序与一个或多个客户程序之间双向通信的软件端点。 网络接口使服务器程序与运行该程序的计算机上特定的硬件端口关联起来,这样 在网络中的任何位置上,带有与该端口相关联的网络接口的客户程序都可以与这 个服务器程序实现通信。
处理一个以上客户请求的方法之一是使服务器程序多线程化。多线程的服务器 为每一个从客户机接收到的通信创建一个线程。线程是一个独立于运行程序和其 他线程的指令序列。 通过使用线程,多线程化的服务器程序就可以接受来自客户机的连接,启动该 通信的线程并继续监视来自其它客户机的请求。 关于示例程序的说明本课的示例程序涉及两个在 第 1 部分、 第 6 课:文件访问及许可 中介绍、由 FileIO.java 应用程序改编的客户/服务器程序对。 示例程序 1 设置了一个服务器程序和一个客户程序之间的客户/服务器通信过 程。服务器程序未多线程化,不能同时处理一个以上客户的请求。 示例程序 2 把服务器程序转化成多线程程序,从而可以处理来自一个以上客 户的请求。 示例程序 1:客户端工作情况
客户程序 会显示一个简单的用户界面,用于提示用户输入文本信息。在您 点击
示例程序 1:服务器端工作情况
服务器程序会显示一个简单的用户界面,当用户点击
示例程序 1:编译和运行若要运行示例程序,则必须首先启动服务器程序。否则,客户程序就无法创建 网络接口连接。以下是编译和运行示例程序的编译器和解释器命令。 javac SocketServer.java javac SocketClient.java java SocketServer java SocketClient 示例程序 1:服务器端程序
服务器程序 在其 listenSocket 方法
public void listenSocket(){
try{
server = new ServerSocket(4321);
} catch (IOException e) {
System.out.println("Could not listen on port 4321");
System.exit(-1);
}
然后,listenSocket 方法为发出请求的客户机生成一个 Socket 连接。本程序代码在客户机启动并请求连接到运行服务器程序的主机和端口。当成
功创建连接后,server.accept 方法就返回一个新的 Socket 对象。
try{
client = server.accept();
} catch (IOException e) {
System.out.println("Accept failed: 4321");
System.exit(-1);
}
之后,listenSocket 方法将生成一个 BufferedReader 对象来读取客户程序通过网络接口连接发送的数据。该方法还生成一个
PrintWriter 对象来把从客户机接收到的数据返回给服务器。
try{
in = new BufferedReader(new InputStreamReader(
client.getInputStream()));
out = new PrintWriter(client.getOutputStream(),
true);
} catch (IOException e) {
System.out.println("Read failed");
System.exit(-1);
}
}
最后,listenSocket 方法从输入流中循环读取客户机上输入的数 据,并把所读取的数据写入到输出流中返回。
while(true){
try{
line = in.readLine();
//Send data back to client
out.println(line);
} catch (IOException e) {
System.out.println("Read failed");
System.exit(-1);
}
}
actionPerformed 方法方法 public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if(source == button){
textArea.setText(line);
}
}
示例程序 1:客户端程序
客户程序在其 listenSocket 方法
public void listenSocket(){
//Create socket connection
try{
socket = new Socket("kq6py", 4321);
out = new PrintWriter(socket.getOutputStream(),
true);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
} catch (UnknownHostException e) {
System.out.println("Unknown host: kq6py");
System.exit(1);
} catch (IOException e) {
System.out.println("No I/O");
System.exit(1);
}
}
actionPerformed 方法
之后, public void actionPerformed(ActionEvent event){
Object source = event.getSource();
if(source == button){
//Send data over socket
String text = textField.getText();
out.println(text);
textField.setText(new String(""));
out.println(text);
}
//Receive text from server
try{
String line = in.readLine();
System.out.println("Text received: " + line);
} catch (IOException e){
System.out.println("Read failed");
System.exit(1);
}
}
示例程序 2:多线程服务程序示例程序现阶段只能在服务器程序和一个客户程序之间工作。若要实现多客户 连接,就必须把服务器程序转化为一个 多线程化服务器 程序。
本例中, public void listenSocket(){
try{
server = new ServerSocket(4444);
} catch (IOException e) {
System.out.println("Could not listen on port 4444");
System.exit(-1);
}
while(true){
ClientWorker w;
try{
//server.accept returns a client connection
w = new ClientWorker(server.accept(), textArea);
Thread t = new Thread(w);
t.start();
} catch (IOException e) {
System.out.println("Accept failed: 4444");
System.exit(-1);
}
}
}
这一服务器程序对非线程化服务器程序所做的重要修改是,
本例中, class ClientWorker implements Runnable {
private Socket client;
private JTextArea textArea;
//Constructor
ClientWorker(Socket client, JTextArea textArea) {
this.client = client;
this.textArea = textArea;
}
public void run(){
String line;
BufferedReader in = null;
PrintWriter out = null;
try{
in = new BufferedReader(new
InputStreamReader(client.getInputStream()));
out = new
PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.out.println("in or out failed");
System.exit(-1);
}
while(true){
try{
line = in.readLine();
//Send data back to client
out.println(line);
//Append data to text area
textArea.append(line);
}catch (IOException e) {
System.out.println("Read failed");
System.exit(-1);
}
}
}
}
JTextArea.append 方法是线程安全的,即它允许一个线程在其它线 程开始附加操作前完成自身的操作。这就可以避免线程全部或部分覆盖掉已插入文
本的信息串而破坏输出。如果 JTextArea.append 方法不具备线程 安全特征,您就需要把对 textArea.append(line)
的调用限制在 synchronized 方法之内,并把 run 方法对 textArea.append(line)
的调用更换为对 appendText(line) 的调用。
public synchronized void appendText(line){
textArea.append(line);
}
关键字 synchronized 的含义是,当本线程锁定 textArea 时,其它线程不能修改该
textArea,直到本线程完成操作为止。
在程序为获得清除并释放资源的机会而退出前,方法 protected void finalize(){
//Objects created in run method are finalized when
//program terminates and thread exits
try{
server.close();
} catch (IOException e) {
System.out.println("Could not close socket");
System.exit(-1);
}
}
更多信息有关网络接口的详细介绍见J ava 教程 的 网络接口详细说明 一节。 [TOP] |
|
||||||||||||||||||||||||||||||||||||||