多任务处理:服务器协议
服务器协议
既然我们将要介绍的多任务服务器方法与特定的客户端-服务器协议相互独立,我们希望能够实现一个同时满足两者的协议。EchoProtocol中给出了回显协议的代码。这个类的静态方法handleEchoClient()中封装了对每个客户端的处理过程。除添加了写日志功能(马上会对其介绍)外,这段代码与TCPEchoServer.java中的连接处理部分几乎完全一致。该方法的参数是客户端Socket实例和Logger实例的引用。
EchoProtocol类实现了Runnable接口(run()方法只是根据该实例的Socket和Logger引用,简单地调用handleEchoClient()方法),因此我们可以创建一个独立执行run()方法的线程。另外,服务器端的协议执行过程可以通过直接调用这个静态方法实现(为其传入Socket和Logger实例的引用)。
EchoProtocol.java
0 import java.io.IOException;
1 import java.io.InputStream;
2 import java.io.OutputStream;
3 import java.net.Socket;
4 import java.util.logging.Level;
5 import java.util.logging.Logger;
6
7 public class EchoProtocol implements Runnable {
8 private static final int BUFSIZE = 32; // Size (in bytes) of I/O buffer
9 private Socket clntSock; // Socket connect to client
10 private Logger logger; // Server logger
11
12 public EchoProtocol(Socket clntSock, Logger logger) {
13 this.clntSock = clntSock;
14 this.logger = logger;
15 }
16
17 public static void handleEchoClient(Socket clntSock, Logger logger) {
18 try {
19 // Get the input and output I/O streams from socket
20 InputStream in = clntSock.getInputStream();
21 OutputStream out = clntSock.getOutputStream();
22
23 int recvMsgSize; // Size of received message
24 int totalBytesEchoed = 0; // Bytes received from client
25 byte[] echoBuffer = new byte[BUFSIZE]; // Receive Buffer
26 // Receive until client closes connection, indicated by -1
27 while ((recvMsgSize = in.read(echoBuffer)) != -1) {
28 out.write(echoBuffer, 0, recvMsgSize);
29 totalBytesEchoed += recvMsgSize;
30 }
31
32 logger.info("Client " + clntSock.getRemoteSocketAddress() + ", echoed "
33 + totalBytesEchoed + " bytes.");
34
35 } catch (IOException ex) {
36 logger.log(Level.WARNING, "Exception in echo protocol", ex);
37 } finally {
38 try {
39 clntSock.close();
40 } catch (IOException e) {
41 }
42 }
43 }
44
45 public void run() {
46 handleEchoClient(clntSock, logger);
47 }
48 }
EchoProtocol.java
1.声明实现Runnable接口:第7行
2.类成员变量和构造函数:第8-15行
每个EchoProtocol实例都包含了一个相应连接的套接字和对logger实例的引用。
3.handleEchoClient():第17-43行
实现回显协议:
从套接字中获取输入/输出流:第20-21行
接收和回显:第25-30行
循环执行直到连接关闭(由read()方法返回值为-1指示),每次循环中在接收到数据后
就立即写回。
在日志中记录连续的详细信息:第32-33行
同时记录远端的SocketAddress和回显的字节数。
异常处理:第36行
将异常写入日志。
你的服务器每分钟将执行上千次客户端请求。现在有用户报告了一个问题。那么如何才能确定到底发生了什么呢?是不是服务器的问题呢?也许是客户端在破坏这个协议。为了处理这种情况,大部分的服务器都将它们的活动记录写入日志。这里我们只对写日志作非常基础的介绍,不过,你得知道还存在更多的日志记录功能以满足企业级需求。
首先我们介绍Logger类,它代表了本地或远端的一个日志记录工具。通过该类的一个实例,我们就可以记录服务器的各种活动信息,就像在EchoProtocol中演示的那样。或许你会在服务器上使用多个日志记录器(logger),每个记录器以不同的方式负责不同的需求。例如,你可以有不同的日志记录器分别负责记录操作、安全和出错消息。在Java中,每个日志记录器由一个全局唯一的名字识别。按照如下代码调用Logger.getLogger()静态工厂方法即可获取一个Logger实例:
Logger logger = Logger.getLogger("practical");
这行代码用于获取名字为"practical"的记录器。如果这个名字的记录器不存在,则用这个名字创建一个新的记录器,否则,返回已存在的记录器实例。无论在程序中获取名字为"practical"的记录器多少次,都将返回同一个实例。
有了日志记录器,需要记录什么内容呢?这取决于你要做什么。如果服务器在正常地运行,你可能不想将服务器执行的每一步都记录到日志中,因为记录日志是要消耗系统资源的,如为日志项分配存储空间,写日志需要占用服务器的处理时间等。另一方面,如果是为了调试,你可能就希望记录服务器执行的每一步。为了对不同情况进行处理,记录器中通常包含了日志项的等级或严格度的概念。Level类中就封装了消息的重要程度信息。每个Logger实例都有一个当前等级,每条日志消息也有对应的等级,低于Logger实例的当前等级的消息将被抛弃(即不写入日志)。每个等级都有一个对应的整数值,因此等级之间可以相互比较和排序。系统识别的Level实例共有7种,同时还能创建用户定义的等级,不过通常没有必要这样做。内置的等级(定义为Level类的静态字段)包括:severe,warning,info,config,fine,finer,和finest。
当你写日志的时候,消息又去哪儿呢?记录器将消息发送到一个或多个Handler实例中,该实例用来发布这些消息。默认情况,每个logger有一个ConsoleHandler用来将消息打印到System.err中。你也可以为一个logger改变或添加handler(如FileHandler)。注意,与logger一样,每个handler也有自己的最小日志等级,因此要发布一条消息,它的等级必须同时高于logger和handler的等级阈值。logger和handler的可配置性很高,包括他们的最小等级。
Logger对我们来说一个重要的特征是它是线程安全的(thread-safe),即可以在并行运行的不同线程中调用它的方法,而不需要在调用者中添加额外的同步措施。如果没有这个特征,由不同线程记录的不同消息将错乱无章地写到日志中。
Logger: 查找/创建
static Logger getLogger(String name)
static Logger getLogger(String name, String
resourceBundleName)
这些静态工厂方法返回指定名字的Logger实例,必要时创建新的实例。
一旦有了logger,我就需要做的就是……写日志。Logger提供了从细粒度到粗粒度的日志记录工具,并能够区分消息的不同等级甚至消息的上下文。
Logger: 记录日志消息
void severe(String msg)
void warning(String msg)
void info(String msg)
void config(String msg)
void fine(String msg)
void finer(String msg)
void finest(String msg)
void entering(String sourceClass, String sourceMethod)
void entering(String sourceClass, String sourceMethod,
Object param)
void entering(String sourceClass, String sourceMethod,
Object[] params)
void exiting(String sourceClass, String sourceMethod)
void exiting(String sourceClass, String sourceMethod,
Object result)
void throwing(String sourceClass, String sourceMethod,
Throwable thrown)
void log(Level level, String msg)
void log(Level level, String msg, Throwable thrown)
severe(),warning()等方法根据其名字对应等级,将给定的符合等级的消息写入日志。entering()和exiting()方法在程序进入或退出给定类的指定方法时将记录写入日志。注意你也可以选择性地指定额外信息,如参数和返回值等。throwing()方法用于将指定方法所抛出的异常信息写入日志。log()方法提供了一种一般性的日志记录方式,该方法可以指定需要记录的消息等级和内容,并可以选择性地添加异常信息。当然还存在很多其他记录日志的方法,这里我们只提到了主要的几种。 我们可能希望能够通过设置最小记录等级或日志消息处理器的等级,来定制自己的记录器。
Logger: 设置/获取日志的等级和处理器
Handler[] getHandlers()
void addHandler(Handler handler)
void removeHandler(Handler handler)
Level getLevel()
void setLevel(Level newLevel)
boolean isLoggable(Level level)
getHandlers()方法返回一个包含了和该记录器关联的所有日志处理器数组。addHandler()和removeHandler()方法用于给该记录器添加或删除日志处理器。getLevel()和setLevel()方法用于获取和设置日志记录的最小等级。isLoggable()方法则在该记录器能够记录所给定的等级的日志时返回true。
现在我们准备介绍一些不同的方法来实现并行服务器。
相关下载:
Java_TCPIP_Socket编程(doc)
http://download.csdn.net/detail/undoner/4940239
文献来源:
UNDONER(小杰博客) :http://blog.csdn.net/undoner
LSOFT.CN(琅软中国) :http://www.lsoft.cn