您的位置:首页 > 其它

项目day01--<客户上下线时间程序实现>(上)

2016-05-16 21:19 435 查看
1 这是项目

2 建立maven的工程,所以先建立Maven,里面的包需要自己导入一下

3 程序里面有详细的注释

一:

这里的wtmpx文件是服务器的一个日志文件,里面记录了用户的信息,自己看着下面的属性,也可以自己定义一个文件,在windows的txt文件就可以

**
* 该类用于描述wtmpx文件中的每一条日志
* 这里只记录该项目需要的5项信息:
* user,pid,type,time,host
* 在解析完事后,定义一个类进行存储
* @author Administrator
*
*/
public class LogData {
/*
*
*
*/
/**
* 日志的长度(字节量)
*/
public static final int LOG_LENGTH = 372;

/**
* user在一条日志中的起始位置
*/
public static final int USER_OFFSET = 0;

/**
* user在一条日志中的长度(字节量)
*/
public static final int USER_LENGTH = 32;

/**
* pid在一条日志中的起始位置
*/
public static final int PID_OFFSET = 68;

/**
* type在一条日志中的起始位置
*/
public static final int TYPE_OFFSET = 72;

/**
* time在一条日志中的起始位置
*/
public static final int TIME_OFFSET = 80;

/**
* host在一条日志中的起始位置
*/
public static final int HOST_OFFSET = 114;

/**
* host在一条日志中的长度(字节量)
*/
public static final int HOST_LENGTH = 258;

/**
* 日志类型:登入日志
*/
public static final short TYPE_LOGIN = 7;

/**
* 日志类型:登入日志
*/
public static final short TYPE_LOGOUT = 8;

/*
* 属性定义
*/
//用户名
private String user;
//进程ID
private int pid;
//日志类型
private short type;
//日志生成时间
private int time;
//用户地址
private String host;

public LogData(String user, int pid, short type, int time, String host) {
super();
this.user = user;
this.pid = pid;
this.type = type;
this.time = time;
this.host = host;
}
/**
* 将给定的字符串解析为一个LogData实例
* 该字符串格式必须是当前类toString方法
* 生成的格式
* lidz,441232,7,1375334515,192.168.1.61
* @param logData
*/
public LogData(String logData){
String[] data = logData.split(",");
this.user = data[0];
this.pid = Integer.parseInt(data[1]);
this.type = Short.parseShort(data[2]);
this.time = Integer.parseInt(data[3]);
this.host = data[4];
}

public LogData(){

}

public String getUser() {
return user;
}

public void setUser(String user) {
this.user = user;
}

public int getPid() {
return pid;
}

public void setPid(int pid) {
this.pid = pid;
}

public short getType() {
return type;
}

public void setType(short type) {
this.type = type;
}

public int getTime() {
return time;
}

public void setTime(int time) {
this.time = time;
}

public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public String toString(){
return user + "," + pid + "," +
type + "," + time + "," +
host;
}
}
二 下面的是需要解析的config.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<config>
<!-- unix系统日志文件名 -->
<logfile>wtmpx</logfile>
<!-- 保存解析后日志的文件名 -->
<textlogfile>log.txt</textlogfile>
<!-- 书签文件名 -->
<lastpositionfile>last-position.txt</lastpositionfile>
<!-- 每次解析日志的条目数 -->
<batch>10</batch>
<!-- 配对日志文件名 -->
<logrecfile>logrec.txt</logrecfile>
<!-- 未配对日志文件名 -->
<loginlogfile>login.txt</loginlogfile>
<!-- 服务端地址 -->
<serverhost>localhost</serverhost>
<!-- 服务端端口 -->
<serverport>8088</serverport>
</config>


三:下面客户端的程序,下面没实现的方法,在项目day02上在实现,没实现的那部分由于全部设计到文件的读取(I/O)

IOUtil类,<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">所以单独用一个类去实现</span>


import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.dms.bo.LogData;

/**
* 该客户端运行在给用户提供unix服务的服务器上。
* 用来读取并收集该服务器上用户的上下线信息,并
* 进行配对整理后发送给服务端汇总。
* @author Administrator
*
*/
public class DMSClient {
//属性定义
//第一步:解析日志所需属性
//unix系统日志文件
private File logFile;
//保存解析后日志的文件
private File textLogFile;
//书签文件
private File lastPositionFile;
//每次解析日志的条目数
private int batch;
//第二步:配对日志所需要属性
//保存配对日志的文件
private File logRecFile;
//保存未配对日志的文件
private File loginLogFile;
//第三步:发送日志所需要属性
//服务端地址
private String serverHost;
//服务端端口
private int serverPort;
/**
* 构造方法,用来初始化客户端
* @throws Exception
*/
public DMSClient() throws Exception{
try {
//1 解析配置文件config.xml
Map<String,String> config = loadConfig();
//打桩
System.out.println(config);
//2 根据配置文件内容初始化属性
init(config);
} catch (Exception e) {
System.out.println("初始化失败!");
throw e;
}
}
/**
* 构造方法初始化第二步,根据配置项初始化属性
* Map中的key与属性同名(区别仅在于key中的字符串全小写),根据
* 对应的key去除value来初始化相应的属性。
* @param config
* @throws Exception
*/
private void init(Map<String,String> config) throws Exception{
try {
this.logFile=new File(config.get("logfile"));
this.textLogFile=new File(config.get("textlogfile"));
this.lastPositionFile=new File(config.get("lastpositionfile"));
this.batch=Integer.parseInt(config.get("batch"));
this.logRecFile=new File(config.get("logrecfile"));
this.loginLogFile=new File(config.get("loginlogfile"));
this.serverHost=config.get("serverhost");
this.serverPort=Integer.parseInt(config.get("serverport"));
} catch (Exception e) {
System.out.println("初始化属性失败!");
e.printStackTrace();
throw e;
}
}
/**
* 构造方法初始化第一步,解析配置文件
* 读取并解析项目根目录中的配置文件config.xml,将
* 根标签<config>中的所有子标签解析出来,并将每一个子
* 标签的名字作为key,标签中间的文本作为value,存入到
* 一个Map后返回。
* @return 返回的Map中保存的是配置文件中的
*         每一条内容,其中key:标签的名字,
*         value为标签中间的文本
* @throws Exception
*/
private Map<String,String> loadConfig() throws Exception{
try {
Map<String,String> config
= new HashMap<String,String>();
SAXReader reader=new SAXReader();
Document doc=reader.read(new File("config.xml"));
Element root=doc.getRootElement();
List<Element> rootall=root.elements();
for(Element e: rootall){
String key=e.getName();
config.put(key,e.getText());
}
return config;
} catch (Exception e) {
System.out.println("解析配置文件异常!");
e.printStackTrace();
throw e;
}
}
/**
* 客户端开始工作的方法
* 循环执行三步:
* 1:解析日志
*/
public void start(){
parseLogs();
}
/**
* 第一步:解析日志
* @return true:解析成功
*         false:解析失败
*/
private boolean parseLogs(){
/*
* 实现要求:
* 从logFile表示的文件中循环读取batch条日志,
* 然后将每条日志中的5个信息解析出来并以一个LogData实例
* 表示,最终将这些LogData对象的toString返回的字符串按
* 行写入到textLogFile表示的文件中
*
* 实现步骤:
* 1:必要的判断工作
*   1.1:为了避免解析的日志还没有被使用,而
*       第一步又重复执行导致之前日志被覆盖
*       的问题,这里需要判断,若保存解析后
*       的日志文件存在,则第一步不再执行。
*       该日志文件会在第二步配对完毕后删除。
*   1.2:logFile文件必须存在(wtmpx文件)
*   1.3:是否还有日志可以解析
* 2:创建RandomAccessFile来读取logFile
* 3:将指针移动到上次最后读取的位置,准备
*   开始新的解析工作
* 4:解析工作
*   4.1:创建一个List集合,用于保存解析后
*       的每一条日志(LogData实例)
*   4.2:循环batch次,解析每条日志中的
*       5项内容(user,pid,type,time,host)
*       并用一个LogData实例保存,然后将
*       该LogData实例存入集合
* 5:将集合中的所有的日志以行为单位保存到
*   textLogFile中
* 6:保存书签信息
* 7:返回true,表示工作完毕
*
*/
/*
*   为啥要用RandAccessFile?	而不用其他的文件类
*   1 可以调用 seek()这个方法
*   2 读取的字节 基本的文件类
*/
RandomAccessFile raf = null;
//	RandomAcessFile raf=null;
try {
//1
//1.1
if(textLogFile.exists()){
return true;
}
//1.2
if(!logFile.exists()){
System.out.println(logFile+"不存在!");
return false;
}
//1.3
long lastPosition = hasLogs();
//打桩
if(lastPosition<0){
System.out.println("没有日志可以解析了!");
return false;
}
//2
raf = new RandomAccessFile(logFile,"r");
//3
raf.seek(lastPosition);
//4
List<LogData> list
= new ArrayList<LogData>();
//List<LogData> lsit=new ArrayList<LogData>();
for(int i=0;i<batch;i++){
//每次解析前都判断是否还有日志可以解析
if(logFile.length()-lastPosition
<LogData.LOG_LENGTH
){
break;
}
//解析user
raf.seek(lastPosition+LogData.USER_OFFSET);
String user
= IOUtil.readString(
raf, LogData.USER_LENGTH
).trim();//trim是把user文件存储的空格去掉
//解析PID
raf.seek(lastPosition+LogData.PID_OFFSET);
int pid = raf.readInt();
//解析TYPE
raf.seek(lastPosition+LogData.TYPE_OFFSET);
short type = raf.readShort();
//解析TIME
raf.seek(lastPosition+LogData.TIME_OFFSET);
int time = raf.readInt();
//解析HOST
raf.seek(lastPosition+LogData.HOST_OFFSET);
String host
= IOUtil.readString(
raf, LogData.HOST_LENGTH
).trim();
LogData log = new LogData(user, pid, type, time, host);
list.add(log);
//打桩
//	System.out.println(log);
//当解析完一条日志后,更新lastPosition
lastPosition = raf.getFilePointer();
}
//5
IOUtil.saveCollection(list, textLogFile);
//6 保存书签文件
IOUtil.saveLong(
lastPosition, lastPositionFile);
//7
return true;
} catch (Exception e) {
System.out.println("解析日志失败!");
e.printStackTrace();
} finally{
if(raf != null){
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
/**
* 第一步解析日志中的一个环节,
* 根据书签文件记录的位置判断是否还有
* 日志可以解析,若有,则将上次最后的位置
* 返回,若没有则返回-1。
* @return
*/
private long hasLogs(){
try {
/*
* 实现步骤:
* 1:若lastPositionFile不存在,则说明
*   从来没有解析过,那么从头开始解析即可,返回0
*   若存在,则读取里面记录的数字,该数字在文件中
*   的第一行,是一个字符串,读取出来需要转换为long
*   值。
* 2:判断上次读取的位置与文件的长度,若文件的长度
*   减去上次读取的位置大于等于一条日志的长度,则
*   说明还有日志可以读取,那么直接将上次的位置返回,
*   从这里读取即可,若不足则返回-1.
*/
/*
if(!lastPositionFile.exists()){
return 0;
}
long lastPosition
= IOUtil.readLong(lastPositionFile);
if(logFile.length()-lastPosition
>=LogData.LOG_LENGTH){
return lastPosition;
}
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
*/
if(!lastPositionFile.exists()){
return 0;
}
long lastPosition=IOUtil.readLong(lastPositionFile);
if(logFile.length()-lastPosition>=LogData.LOG_LENGTH){
return lastPosition;
}
}catch(Exception e){
e.printStackTrace();
}
return -1;
}
public static void main(String[] args) {
try {
DMSClient client = new DMSClient();
client.start();
} catch (Exception e) {
System.out.println("客户端运行失败!");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: