
153 服务多个客户
JabberServer可以正常工作,但每次只能为一个客户程序提供服务。在典型的服务器中,我们希望同时能处理多个客户的请求。解决这个问题的关键就是多线程处理机制。而对于那些本身不支持多线程的语言,达到这个要求无疑是异常困难的。通过第14章的学习,大家已经知道Java已对多线程的处理进行了尽可能的简化。由于Java的线程处理方式非常直接,所以让服务器控制多名客户并不是件难事。
最基本的方法是在服务器(程序)里创建单个ServerSocket,并调用accept()来等候一个新连接。一旦accept()返回,我们就取得结果获得的Socket,并用它新建一个线程,令其只为那个特定的客户服务。然后再调用accept(),等候下一次新的连接请求。
对于下面这段服务器代码,大家可发现它与JabberServerjava例子非常相似,只是为一个特定的客户提供服务的所有 *** 作都已移入一个独立的线程类中:
//: MultiJabberServerjava
// A server that uses multithreading to handle
// any number of clients
import javaio;
import javanet;
class ServeOneJabber extends Thread {
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public ServeOneJabber(Socket s)
throws IOException {
socket = s;
in =
new BufferedReader(
new InputStreamReader(
socketgetInputStream()));
// Enable auto-flush:
out =
new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socketgetOutputStream())), 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 = inreadLine();
if (strequals("END")) break;
Systemoutprintln("Echoing: " + str);
outprintln(str);
}
Systemoutprintln("closing");
} catch (IOException e) {
} finally {
try {
socketclose();
} catch(IOException e) {}
}
}
}
public class MultiJabberServer {
static final int PORT = 8080;
public static void main(String[] args)
throws IOException {
ServerSocket s = new ServerSocket(PORT);
Systemoutprintln("Server Started");
try {
while(true) {
// Blocks until a connection occurs:
Socket socket = saccept();
try {
new ServeOneJabber(socket);
} catch(IOException e) {
// If it fails, close the socket,
// otherwise the thread will close it:
socketclose();
}
}
} finally {
sclose();
}
}
} ///:~
每次有新客户请求建立一个连接时,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决定的。大家会注意到这个值非常关键,因为假如把它设得很大,线程便有可能耗尽资源,并产生不可预知的程序错误。
//: MultiJabberClientjava
// Client that tests the MultiJabberServer
// by starting up multiple clients
import javanet;
import javaio;
class JabberClientThread extends Thread {
private Socket socket;
private BufferedReader in;
private PrintWriter out;
private static int counter = 0;
private int id = counter++;
private static int threadcount = 0;
public static int threadCount() {
return threadcount;
}
public JabberClientThread(InetAddress addr) {
Systemoutprintln("Making client " + id);
threadcount++;
try {
socket =
new Socket(addr, MultiJabberServerPORT);
} catch(IOException e) {
// If the creation of the socket fails,
// nothing needs to be cleaned up
}
try {
in =
new BufferedReader(
new InputStreamReader(
socketgetInputStream()));
// Enable auto-flush:
out =
new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socketgetOutputStream())), true);
start();
} catch(IOException e) {
// The socket should be closed on any
// failures other than the socket
// constructor:
try {
socketclose();
} 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++) {
outprintln("Client " + id + ": " + i);
String str = inreadLine();
Systemoutprintln(str);
}
outprintln("END");
} catch(IOException e) {
} finally {
// Always close it:
try {
socketclose();
} catch(IOException e) {}
threadcount--; // Ending this thread
}
}
}
public class MultiJabberClient {
static final int MAX_THREADS = 40;
public static void main(String[] args)
throws IOException, InterruptedException {
InetAddress addr =
InetAddressgetByName(null);
while(true) {
if(JabberClientThreadthreadCount()
< MAX_THREADS)
new JabberClientThread(addr);
ThreadcurrentThread()sleep(100);
}
}
} ///:~
JabberClientThread构建器获取一个InetAddress,并用它打开一个套接字。大家可能已看出了这样的一个套路:Socket肯定用于创建某种Reader以及/或者Writer(或者InputStream和/或OutputStream)对象,这是运用Socket的唯一方式(当然,我们可考虑编写一、两个类,令其自动完成这些 *** 作,避免大量重复的代码编写工作)。同样地,start()执行线程的初始化,并调用run()。在这里,消息发送给服务器,而来自服务器的信息则在屏幕上回显出来。然而,线程的“存在时间”是有限的,最终都会结束。注意在套接字创建好以后,但在构建器完成之前,假若构建器失败,套接字会被清除。否则,为套接字调用close()的责任便落到了run()方法的头上。
threadcount跟踪计算目前存在的JabberClientThread对象的数量。它将作为构建器的一部分增值,并在run()退出时减值(run()退出意味着线程中止)。在MultiJabberClientmain()中,大家可以看到线程的数量会得到检查。若数量太多,则多余的暂时不创建。方法随后进入“休眠”状态。这样一来,一旦部分线程最后被中止,多作的那些线程就可以创建了。大家可试验一下逐渐增大MAX_THREADS,看看对于你使用的系统来说,建立多少线程(连接)才会使您的系统资源降低到危险程度。
1、下载wrapper-windows-x86-32系列的工具包,解压缩为<wrapper-home>。
2、新建一个目录,例如:C:\MyServerApp,并在其下面创建五个文件夹
分别为:
bin 可执行程序文件夹
conf 配置文件夹
lib wrapper自身的jar包文件夹
logs 日志文件夹包
application 新建一个class用于存放应用程序的class文件, 此包下还新建一个lib 用于存放应用程序所需要的 jar包。
3、将<wrapper-home>\src\bin 中的Appbatin,InstallApp-NTbatin,UninstallApp-NTbatin等文件
与<wrapper-home>\bin 下的wrapperexe复制到bin 目录下,并且将Appbatin,InstallApp-NTbatin,
UninstallApp-NTbatin等的后缀in去掉。
将<wrapper-home>\src\conf 下的wrapperconf文件和<wrapper-home>\conf 下的wrapper-license复制到conf目录下,wrapper-license文件一定不能少
将<wrapper-home>\lib 下的wrapperjar,wrappertestjar和wrapperdll文件复制到lib目录下
最终的包结构可以是这样子的:
C:\MyServerApp
|-bin
|-Appbat
|-InstallApp-NTbat
|-UninstallApp-NTbat
|-wrapperexe
|-lib
|-wrapperjar 必须要的
|-wrappertestjar
|-wrapperdll
|-conf
|-wrapperconf
|-wrapper-license
|-logs
|-wrapperlog
|-application
|-lib 应用程序中如果用到了jar包,那么就复制到此文件夹里
|-class 用来存放应用程序(一般应用程序是有包结构的,就把工程里面的bin目录下的class文件连同包一起拷过来)
4、配置Wrapper工具的wrapperconf配置文件
配置Java 虚拟机的位置
wrapperjava command=C:\Program Files\Java \jdk160_07\bin\java
配置wrapper的主类,如果用他的代理方式,就填orgtanukisoftwarewrapperWrapperSimpleApp
wrapperjava mainclass=orgtanukisoftwarewrapperWrapperSimpleApp
工具寻找jar包以及应用程序的路径
wrapperjava classpath1=/lib/wrappertestjar
wrapperjava classpath2=/lib/wrapperjar
wrapperjava classpath3=/lib/wrapperdll
应用程序连同其文件夹一起拷到application文件夹下了,所以路径只需要指定到/application/
wrapperjava classpath4=/application/class
应用程序要用到的所有jar包
wrapperjava classpath5=/application/lib/jar
wrapperjava librarypath1=/lib
wrapperjava additional1=-Dprogramename=runbat
要将哪一个应用程序做成服务,就要把他的主函数所在的类名 写在这里
wrapperappparameter1=comlukeserviceserverTcpServer
wrapperconsoleformat=PM
wrapperconsoleloglevel=INFO
指定日志记录的地方
wrapperlogfile=/logs/wrapperlog
wrapperlogfileformat=LPTM
wrapperlogfileloglevel=INFO
wrapperlogfilemaxsize=0
wrapperlogfilemaxfiles=0
wrappersyslogloglevel=NONE
wrapperconsoletitle=Wrapper Application
wrapperntservicename=@wrapper @
wrapperntservicedisplayname=@WrapperApplication @
wrapperntservicedescription=@WrapperDescription @
wrapperntservicedependency1=
wrapperntservicestarttype=AUTO_START
wrapperntserviceinteractive=false
5、运行cmd
cd c:\MyServerApp\bin
Appbat测试不会将把你的应用(此处为server)安装到Win32系统服务中
InstallAppbat安装 后才将把你的应用(此处为server)安装到Win32系统服务中
第一处错误:
Systemoutprintln(“INFO:。。。。“ + thisport=" + ",operateStr + "" + thisoperateStr +""");// 错误,检查你的引号
改成
Systemoutprintln(“INFO:。。。。“ + thisport=" + ",operateStr + " + thisoperateStr + "");
第二处错误:
String[] dps = ipsplit("\"); // 需对\转义
改成
String[] dps = ipsplit("\\");
以上就是关于虽能告诉我服务器端最简单的java程序全部的内容,包括:虽能告诉我服务器端最简单的java程序、如何把一个Java程序做成Windows服务、java服务器端程序报错,怎样修改等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)