68.JAVA编程思想——服务多个客户

68.JAVA编程思想——服务多个客户

JabberServer 可以正常工作,但每次只能为一个客户程序提供服务。在典型的服务器中,我们希望同时能处理多个客户的请求。解决这个问题的关键就是多线程处理机制。而对于那些本身不支持多线程的语言,达到这个要求无疑是异常困难的。Java 已对多线程的处理进行了尽可能的简化。由于Java 的线程处理方式非常直接,所以让服务器控制多名客户并不是件难事。最基本的方法是在服务器(程序)里创建单个ServerSocket,并调用accept()来等候一个新连接。一旦accept()返回,我们就取得结果获得的Socket,并用它新建一个线程,令其只为那个特定的客户服务。然后再调用accept()
,等候下一次新的连接请求。

对于下面这段服务器代码,大家可发现它与JabberServer.java例子非常相似,只是为一个特定的客户提供服务的所有操作都已移入一个独立的线程类中:

1     服务端代码

import java.io.*;

import java.net.*;

class ServeOneJabber
extends Thread {

private Socket
socket;

private BufferedReader
in;

private PrintWriter
out;

public ServeOneJabber(Socket
s) throwsIOException {

socket =
s;

in =
new
BufferedReader(new InputStreamReader(socket.getInputStream()));

// Enable auto-flush:

out= newPrintWriter(newBufferedWriter(newOutputStreamWriter(socket.getOutputStream())),true);

// If any of the above calls throw an

// exception, the caller is responsible for

// closing the socket. Otherwise the thread

// will close it.

start(); // Calls run()

}

public
void
run() {

try {

while (true) {

String
str = in.readLine();

if (str.equals("END"))

break;

System.out.println("Echoing: "+
str);

out.println(str);

}

System.out.println("closing...");

} catch (IOException
e) {

} finally {

try {

socket.close();

} catch (IOException
e) {

}

}

}

}

public
class
MultiJabberServer {

static
finalintPORT
= 8080;

public
staticvoid
main(String[]
args)throwsIOException {

ServerSocket
s = new ServerSocket(PORT);

System.out.println("Server Started");

try {

while (true) {

// Blocks until a connection occurs:

Socket
socket = s.accept();

try {

new ServeOneJabber(socket);

} catch (IOException
e) {

// If it fails, close the socket,

// otherwise the thread will close it:

socket.close();

}

}

} finally {

s.close();

}

}

} /// :~

每次有新客户请求建立一个连接时,ServeOneJabber 线程都会取得由accept()在main() 中生成的Socket 对象。然后和往常一样,它创建一个BufferedReader,并用Socket 自动刷新PrintWriter 对象。最后,它调用Thread 的特殊方法start(),令其进行线程的初始化,然后调用run()。这里采取的操作与前例是一样的:从套扫字读入某些东西,然后把它原样反馈回去,直到遇到一个特殊的"END"结束标志为止。

同样地,套接字的清除必须进行谨慎的设计。就目前这种情况来说,套接字是在ServeOneJabber外部创建的,所以清除工作可以“共享”。若ServeOneJabber 构建器失败,那么只需向调用者“掷”出一个违例即可,然后由调用者负责线程的清除。但假如构建器成功,那么必须由ServeOneJabber 对象负责线程的清除,这是在它的run()里进行的。

请注意MultiJabberServer 有多么简单。和以前一样,我们创建一个ServerSocket,并调用accept()允许一个新连接的建立。但这一次,accept() 的返回值(一个套接字)将传递给用于ServeOneJabber 的构建器,由它创建一个新线程,并对那个连接进行控制。连接中断后,线程便可简单地消失。

如果ServerSocket 创建失败,则再一次通过main()掷出违例。如果成功,则位于外层的try-finally 代码块可以担保正确的清除。位于内层的try-catch 块只负责防范ServeOneJabber 构建器的失败;若构建器成功,则ServeOneJabber 线程会将对应的套接字关掉。

为了证实服务器代码确实能为多名客户提供服务,下面这个程序将创建许多客户(使用线程),并同相同的服务器建立连接。每个线程的“存在时间”都是有限的。一旦到期,就留出空间以便创建一个新线程。允许创建的线程的最大数量是由final int maxthreads 决定的。大家会注意到这个值非常关键,因为假如把它设得很大,线程便有可能耗尽资源,并产生不可预知的程序错误。

2     客户端代码

import java.net.*;

import java.io.*;

class JabberClientThread
extends Thread {

private Socket
socket;

private BufferedReader
in;

private PrintWriter
out;

private
staticint
counter= 0;

private
int
id=
counter++;

private
staticint
threadcount= 0;

public
staticint
threadCount() {

return
threadcount;

}

public JabberClientThread(InetAddress
addr) {

System.out.println("Making client " +
id);

threadcount++;

try {

socket =
new Socket(addr, MultiJabberServer.PORT);

} catch (IOException
e) {

// If the creation of the socket fails,

// nothing needs to be cleaned up.

}

try {

in =
new BufferedReader(new InputStreamReader(socket.getInputStream()));

// Enable auto-flush:

out =
new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),
true);

start();

} catch (IOException
e) {

// The socket should be closed on any

// failures other than the socket

// constructor:

try {

socket.close();

} catch (IOException
e2) {

}

}

// Otherwise the socket will be closed by

// the run() method of the thread.

}

public
void
run() {

try {

for (int
i = 0; i < 25;
i++) {

out.println("Client " +
id + ": " +
i);

String
str = in.readLine();

System.out.println(str);

}

out.println("END");

} catch (IOException
e) {

} finally {

// Always close it:

try {

socket.close();

} catch (IOException
e) {

}

threadcount--;
// Ending this thread

}

}

}

public
class
MultiJabberClient {

static
finalintMAX_THREADS
= 40;

public
staticvoid
main(String[]
args)throwsIOException, InterruptedException {

InetAddress addr = InetAddress.getByName(null);

while (true) {

if (JabberClientThread.threadCount()<
MAX_THREADS)

new JabberClientThread(addr);

Thread.currentThread().sleep(100);

}

}

} /// :~

JabberClientThread 构建器获取一个InetAddress,并用它打开一个套接字。大家可能已看出了这样的一个套路:Socket 肯定用于创建某种Reader 以及/或者Writer(或者InputStream 和/或OutputStream)对象,这是运用Socket 的唯一方式(当然,我们可考虑编写一、两个类,令其自动完成这些操作,避免大量重复的代码编写工作)。同样地,start()执行线程的初始化,并调用run()。在这里,消息发送给服务器,而来自服务器的信息则在屏幕上回显出来。然而,线程的“存在时间”是有限的,最终都会结束。注意在套接字创建好以后,但在构建器完成之前,假若构建器失败,套接字会被清除。否则,为套接字调用close()的责任便落到了run()方法的头上。

threadcount 跟踪计算目前存在的JabberClientThread对象的数量。它将作为构建器的一部分增值,并在run()退出时减值(run()退出意味着线程中止)。在MultiJabberClient.main()中,大家可以看到线程的数量会得到检查。若数量太多,则多余的暂时不创建。方法随后进入“休眠”状态。这样一来,一旦部分线程最后被中止,多作的那些线程就可以创建了。大家可试验一下逐渐增大MAX_THREADS,看看对于你使用的系统来说,建立多少线程(连接)才会使您的系统资源降低到危险程度。

时间: 2024-08-25 12:05:29

68.JAVA编程思想——服务多个客户的相关文章

1.JAVA 编程思想——对象入门

对象入门 欢迎转载,转载请标明出处:    http://blog.csdn.net/notbaron/article/details/51040219 如果学JAVA,没有读透<JAVA 编程思想>这本书,实在不好意思和别人说自己学过JAVA.鉴于此,蛤蟆忙里偷闲,偷偷翻看这本传说中的牛书. 面向对象编程OOP具有多方面吸引力.实现了更快和更廉价的开发与维护过程.对分析与设计人员,建模处理变得更加简单,能生成清晰.已于维护的设计方案. 这些描述看上去非常吸引人的,不过蛤蟆还是没啥印象(至少到

66.JAVA编程思想——网络编程

66.JAVA编程思想--网络编程 历史上的网络编程都倾向于困难.复杂,而且极易出错. 程序员必须掌握与网络有关的大量细节,有时甚至要对硬件有深刻的认识.一般地,我们需要理解连网协议中不同的"层"(Layer).而且对于每个连网库,一般都包含了数量众多的函数,分别涉及信息块的连接.打包和拆包:这些块的来回运输:以及握手等等.这是一项令人痛苦的工作.但是,连网本身的概念并不是很难.我们想获得位于其他地方某台机器上的信息,并把它们移到这儿:或者相反.这与读写文件非常相似,只是文件存在于远程

java编程思想总结(三)

java编程思想总结(三) java编程思想总结是一个持续更新的系列,是本人对自己多年工作中使用到java的一个经验性总结,也是温故而知新吧,因为很多基础的东西过了这么多年,平时工作中用不到也会遗忘掉,所以看看书,上上网,查查资料,也算是记录下自己的笔记吧,过一段时间之后再来看看也是蛮不错的,也希望能帮助到正在学习的人们,本系列将要总结一下几点: 面向对象的编程思想 java的基本语法 一些有趣的框架解析 实战项目的整体思路 代码的优化以及性能调优的几种方案 整体项目的规划和视角 其它遗漏的东西

67.JAVA编程思想——套接字

67.JAVA编程思想--套接字 "套接字"或者"插座"(Socket)也是一种软件形式的抽象,用于表达两台机器间一个连接的"终端".针对一个特定的连接,每台机器上都有一个"套接字",可以想象它们之间有一条虚拟的"线缆".线缆的每一端都插入一个"套接字"或者"插座"里.当然,机器之间的物理性硬件以及电缆连接都是完全未知的.抽象的基本宗旨是让我们尽可能不必知道那些细节.

【Java编程思想】一、对象导论

作为一个电子专业的人,在学习了将近3年的嵌入式技术后,决定投奔移动互联网,在互联网大潮中急流勇进! 为了学习OOP(Object-oriented Programming),为了转向移动互联网,我决定开始学习android开发,那么就从Java开始吧! Java的学习资料很多,在研究几天之后,决定从<Java编程思想>这本书开始. 而在这本书之前,我已经看完了一个培训导师的Java4Android的Java教学视频,看的很快,因为我学过C和C++. 但我的Java水平依旧很差,主要在于面向对象

74.JAVA编程思想——远程方法

74.JAVA编程思想--远程方法 为通过网络执行其他机器上的代码,传统的方法不仅难以学习和掌握,也极易出错.思考这个问题最佳的方式是:某些对象正好位于另一台机器,我们可向它们发送一条消息,并获得返回结果,就象那些对象位于自己的本地机器一样.Java 1.1 的"远程方法调用"(RMI)采用的正是这种抽象.将引导大家经历一些必要的步骤,创建自己的RMI 对象. 1     远程接口概念 RMI 对接口有着强烈的依赖.在需要创建一个远程对象的时候,我们通过传递一个接口来隐藏基层的实施细节

Java编程思想总结(一)对象导论

Java编程思想总结(一)对象导论 1.1 抽象过程 万物皆对象. 程序是对象的集合(即:类),他们通过发送消息(调用方法)来告知彼此要做的. 每个对象都有自己的由其他对象所构成的存储(引用其他对象或基本类型,即组合). 每个对象都拥有其类型.每个类最重要的区别于其他类的特征就是“可以发送什么样的消息给它”(即调用方法). 某一特定类型的所有对象都可以接受同样的消息. 对象具有状态(成员属性).行为(成员方法)和标识(引用的名称).每个对象在内存中都有唯一的一个地址. 1.2 每个对象都有一个接

【java编程思想--学习笔记(四)】对象导论

写这篇博客的前言: 长话短说,我希望通过阅读<java编程思想>来使我的代码 简洁可用 . 目的的层次不同,首先具体的目标是,了解Java的特性和巩固Java的基础. 更抽象的目的如下: 1.期待以巩固基础的方式,使代码优美,简洁,高效. 2.使自己写的模块能够开放适度,好用. 3.形成一种对代码是否优美的审美观. 于是<Java编程思想>第一章 对象导论 由此开始. 1.1 抽象过程 java 相对于命令式语言的优势在于只针对于待解问题建模.后者所做的主要抽象要求所做问题基于计算

71.JAVA编程思想——JAVA与CGI

71.JAVA编程思想--JAVA与CGI Java 程序可向一个服务器发出一个CGI 请求,这与HTML 表单页没什么两样.而且和HTML 页一样,这个请求既可以设为GET(下载),亦可设为POST(上传).除此以外,Java 程序还可拦截CGI 程序的输出,所以不必依赖程序来格式化一个新页,也不必在出错的时候强迫用户从一个页回转到另一个页.事实上,程序的外观可以做得跟以前的版本别无二致. 代码也要简单一些,毕竟用CGI 也不是很难就能写出来(前提是真正地理解它).所以我们准备办个CGI 编程