项目day01--<客户上下线时间程序实现>(上)
2016-05-16 21:19
435 查看
1 这是项目
2 建立maven的工程,所以先建立Maven,里面的包需要自己导入一下
3 程序里面有详细的注释
一:
这里的wtmpx文件是服务器的一个日志文件,里面记录了用户的信息,自己看着下面的属性,也可以自己定义一个文件,在windows的txt文件就可以
三:下面客户端的程序,下面没实现的方法,在项目day02上在实现,没实现的那部分由于全部设计到文件的读取(I/O)
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("客户端运行失败!"); } } }
相关文章推荐
- [剑指offer]正则表达式匹配
- 不可变类中的一个问题
- Java学习之InputStream中read()与read(byte[] b)
- 51nod 1225 余数的和 数学
- 大道至简阅读笔记02
- [PWA] 8.Unobtrusive update: Delete old cache and only keep one, hard refresh to let new SW to take control
- Spring IoC
- hdu 1542 矩形面积并
- android天气预报源码
- leetcode 204. Count Primes-素数统计
- 如何动态显示图片
- 关于PreparedStatement操纵数据库
- [置顶] 【推荐】算法详解
- 前端---CSS中display-block & display-inline
- C++ 温习笔记(1) - main函数,类对象,作用域,防止头文件二次编译
- 整数解
- python的web问题
- python的web问题
- 记使用Android SQLite遇到的小问题
- http接口自动化测试框架实现