读取用户登入出日志并上传服务端

清华大佬耗费三个月吐血整理的几百G的资源,免费分享!....>>>

该客户端运行在给用户提供unix服务的服务器上。用来读取并收集该服务器上用户的上下线信息,并进行配对整理后发送给服务端汇总。
package com.dms;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

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



/**
 * DMS服务端,用来接收每个客户端发送过来的
 * 配对日志并保存在本地文件中
 * @author Administrator
 *
 */
public class DMSServer {
	//属性定义
	//用来接收客户端连接的服务端的ServerSocket
	private ServerSocket server;
	//用来管理处理客户端请求的线程的线程池
	private ExecutorService threadPool;
	//保存所有客户端发送过来配对日志的文件
	private File serverLogFile;
	//消息队列
	private BlockingQueue<String> messageQueue = new LinkedBlockingQueue<String>();
	
	public DMSServer() throws Exception{
		try {
			System.out.println("服务端正在初始化...");
			//1 解析配置文件server-config.xml
			Map<String,String> config = loadConfig();
			
			//2 根据配置文件内容初始化属性
			init(config);
			System.out.println("服务端初始化完毕...");
		} catch (Exception e) {
			System.out.println("初始化服务端失败!");
			throw e;
		}
	}
	
	/**
	 * 构造方法初始化第一步,解析配置文件
	 * @return 返回的Map中保存的是配置文件中的
	 *         每一条内容,其中key:标签的名字,
	 *         value为标签中间的文本
	 * @throws Exception 
	 */
	private Map<String,String> loadConfig() throws Exception{
		try {
			SAXReader reader = new SAXReader();
			Document doc
				= reader.read(new File("server-config.xml"));
			Element root = doc.getRootElement();
			
			Map<String,String> config
				= new HashMap<String,String>();
			/*
			 * 获取<config>标签中的所有子标签
			 * 并将每一个子标签的名字作为key,中间的
			 * 文本作为value存入Map集合
			 */
			List<Element> list = root.elements();
			for(Element e : list){
				String key = e.getName();
				String value = e.getTextTrim();
				config.put(key, value);
			}
			return config;
		} catch (Exception e) {
			System.out.println("解析配置文件异常!");
			e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * 构造方法初始化第二步,根据配置项初始化属性
	 * @param config
	 * @throws Exception 
	 */
	private void init(Map<String,String> config) throws Exception{
		/*
		 * 用配置文件中的<logrecfile>初始化属性:serverLogFile
		 * 用配置文件中的<threadsum>初始化属性:threadPool,这里创建固定大小线程池。该值作为线程池线程数量
		 * 用配置文件中的<serverport>初始化属性:server,这里这个值为ServerSocket的服务端口
		 */
		this.server = new ServerSocket(
			Integer.parseInt(config.get("serverport"))	
		);
		
		this.serverLogFile = new File(
			config.get("logrecfile")	
		);
		this.threadPool = Executors.newFixedThreadPool(
			Integer.parseInt(config.get("threadsum"))	
		);
	}	
	/**
	 * 服务端开始工作的方法
	 * @throws Exception
	 */
	public void start() throws Exception{
		/*
		 * 实现要求:
		 *  首先单独启动一个线程,用来运行SaveLogHandler
		 *  这个任务,目的是保存所有配对日志
		 *  然后开始循环监听服务端端口,一旦一个客户端连接了,
		 *  就实例化一个ClientHander,然后将该任务交给线程池
		 *  使其分配线程来处理与该客户端的交互。
		 *  
		 */
		try {
			System.out.println("服务端开始工作...");
			SaveLogHandler slh=new SaveLogHandler();
			new Thread(slh).start();
			
			while(true){
				Socket socket=server.accept();
				
				threadPool.execute(new ClientHandler(socket));
			}
			
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} 
	}
	
	public static void main(String[] args) {
		try {
			DMSServer server = new DMSServer();
			server.start();
		} catch (Exception e) {
			System.out.println("启动服务端失败!");
		}
	}
	/**
	 * 该线程负责从消息队列中取出每一条配对日志,
	 * 并存入到serverLogFile文件
	 * @author Administrator
	 *
	 */
	private class SaveLogHandler implements Runnable{
		public void run(){
			PrintWriter pw = null;
			try {
				pw = new PrintWriter(
					new FileOutputStream(
						serverLogFile,true	
					)	
				);
				while(true){
					if(messageQueue.size()>0){
						pw.println(messageQueue.poll());
					}else{
						pw.flush();
						Thread.sleep(500);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally{
				if(pw != null){
					pw.close();
				}
			}
		}
	}
	
	/**
	 * 处理一个指定客户端请求
	 * @author Administrator
	 *
	 */
	private class ClientHandler implements Runnable{
		private Socket socket;
		public ClientHandler(Socket socket){
			this.socket = socket;
		}
		public void run(){
			/*
			 * 思路:
			 * 首先接收客户端发送过来的所有配对日志,
			 * 直到读取到"OVER"为止,然后将这些配对
			 * 日志保存到本地的文件中,并回复客户端
			 * "OK"
			 * 执行步骤:
			 * 1:通过Socket创建输出流,用来给客户端
			 *   发送响应
			 * 2:通过Socket创建输入流,读取客户端发送
			 *   过来的日志
			 * 3:循环读取客户端发送过来的每一行字符串,并
			 *   先判断是否为字符串"OVER",若不是,则是
			 *   一条配对日志,那么保存到本地文件,若是,
			 *   则停止读取。
			 * 4:成功读取所有日志后回复客户端"OK"      
			 */
			PrintWriter pw = null;
			try {
				//1
				pw = new PrintWriter(
					new OutputStreamWriter(
						socket.getOutputStream(),"UTF-8"	
					)	
				);
				//2
				BufferedReader br = new BufferedReader(
					new InputStreamReader(
						socket.getInputStream(),"UTF-8"	
					)	
				);
				
				//3
				String message = null;
				while((message = br.readLine())!=null){
					if("OVER".equals(message)){
						break;
					}
					//将该日志写入文件保存
					messageQueue.offer(message);
				}
				
				//4
				pw.println("OK");
				pw.flush();
				
				
			} catch (Exception e) {
				e.printStackTrace();
				pw.println("ERROR");
				pw.flush();
			} finally{
				try {
					//与客户端断开连接释放资源
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}





package com.dms;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

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

import com.dms.bo.LogData;
import com.dms.bo.LogRec;

/**
 * 该客户端运行在给用户提供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;
		}
	}
	/**
	 * 构造方法初始化第二步,根据配置项初始化属性
	 * @param config
	 * @throws Exception 
	 */
	private void init(Map<String,String> config) throws Exception{
		try {
			logFile = new File(
				config.get("logfile")
			);
			textLogFile = new File(
				config.get("textlogfile")	
			);
			lastPositionFile = new File(
				config.get("lastpositionfile")	
			);
			batch = Integer.parseInt(
				config.get("batch")
			);
			logRecFile = new File(
				config.get("logrecfile")	
			);
			loginLogFile = new File(
				config.get("loginlogfile")	
			);
			serverHost = config.get("serverhost");
			serverPort = Integer.parseInt(
				config.get("serverport")	
			);			
			
		} catch (Exception e) {
			System.out.println("初始化属性失败!");
			e.printStackTrace();
			throw e;
		}
	}
	
	
	/**
	 * 构造方法初始化第一步,解析配置文件
	 * @return 返回的Map中保存的是配置文件中的
	 *         每一条内容,其中key:标签的名字,
	 *         value为标签中间的文本
	 * @throws Exception 
	 */
	private Map<String,String> loadConfig() throws Exception{
		try {
			SAXReader reader = new SAXReader();
			Document doc
				= reader.read(new File("config.xml"));
			Element root = doc.getRootElement();
			
			Map<String,String> config
				= new HashMap<String,String>();
			/*
			 * 获取<config>标签中的所有子标签
			 * 并将每一个子标签的名字作为key,中间的
			 * 文本作为value存入Map集合
			 */
			List<Element> list = root.elements();
			for(Element e : list){
				String key = e.getName();
				String value = e.getTextTrim();
				config.put(key, value);
			}
			return config;
		} catch (Exception e) {
			System.out.println("解析配置文件异常!");
			e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * 客户端开始工作的方法
	 * 循环执行三步:
	 * 1:解析日志
	 * 2:配对日志
	 * 3:发送日志
	 */
	public void start(){
		parseLogs();
		matchLogs();
		sendLogs();
//		while(true){
//			//解析日志
//			if(!parseLogs()){
//				continue;
//			}
//			//配对日志
//			if(!matchLogs()){
//				continue;
//			}
//			//发送日志
//			sendLogs();
//		}
	}
	/**
	 * 第三步:发送日志
	 * @return true:发送成功
	 *         false:发送失败
	 */
	private boolean sendLogs(){
			/*
			 * 实现思路:
			 * 将logRecFile文件中的所有配对日志读取
			 * 出来然后连接上服务端并发送过去,若服务端
			 * 全部接收,就可以将该文件删除,表示发送
			 * 完毕了。
			 * 实现步骤:
			 * 1:logRecFile文件必须存在
			 * 2:将所有配对日志读取出来并存入一个集合
			 *   等待发送
			 * 3:通过Socket连接服务端
			 * 4:创建输出流
			 * 5:顺序将所有配对日志按行发送给服务端
			 * 6:单独发送一个字符串"OVER"表示所有日志
			 *   均已发送完毕
			 * 7:创建输入流
			 * 8:读取服务端发送回来的响应字符串
			 * 9:若响应的字符串为"OK",表示服务端正常
			 *   接收了所有日志,这时就可以将logRecFile
			 *   文件删除并返回true表示发送完毕。
			 *       
			 */
		Socket socket = null;
		try {
			//1
			if(!logRecFile.exists()){
				System.out.println(logRecFile+"不存在!");
				return false;
			}
			//2
			List<String> matches 
				= IOUtil.loadLogRec(logRecFile);
			
			//3
			socket = new Socket(serverHost,serverPort);
			
			//4
			PrintWriter pw = new PrintWriter(
				new OutputStreamWriter(
					socket.getOutputStream(),"UTF-8"	
				)	
			);
			
			//5
			for(String log : matches){
				pw.println(log);
			}
			
			//6
			pw.println("OVER");
			pw.flush();
			
			//7
			BufferedReader br = new BufferedReader(
				new InputStreamReader(
					socket.getInputStream(),"UTF-8"	
				)	
			);
			
			//8
			String response = br.readLine();
			//9
			if("OK".equals(response)){
				logRecFile.delete();
				return true;
			}else{
				System.out.println("发送日志失败!");
				return false;
			}
			
		} catch (Exception e) {
			System.out.println("发送日志失败!");
			e.printStackTrace();
		} finally{
			if(socket != null){
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return false;
	}
	
	/**
	 * 第二步:配对日志
	 * @return true:配对成功
	 *         false:配对失败
	 */
	private boolean matchLogs(){
		
			/*
			 * 实现思路:
			 * 将第一步解析的新日志,与上次为配对成功
			 * 的登入日志全部读取出来,然后再按照user,
			 * pid相同,type一个是7,一个是8进行配对。
			 * 只要能找到类型为8的,一定可以找到一个
			 * 能与之配对的登入日志。
			 * 
			 * 实现步骤:
			 * 1:必要的判断
			 *   1.1:logRecFile是否存在,存在则不再
			 *       进行新的配对工作,避免覆盖。
			 *   1.2:textLogFile文件必须存在。    
			 * 2:读取textLogFile将日志读取出来,并
			 *   存入到集合中。(若干LogData实例)
			 * 3:若loginLogFile文件若存在,则说明
			 *   有上次未配对成功的日志,也将其读取
			 *   出来存入集合等待一起配对
			 * 4:配对工作
			 *   4.1:创建一个集合,用于保存所有配对日志
			 *   4.2:创建两个Map分别保存登入日志与登出日志
			 *   4.3:遍历所有待配对的日志,按照登入与登出
			 *       分别存入两个Map中,
			 *       其中key:user,pid
			 *           value:LogData实例
			 *   4.4:遍历登出Map,并根据每条登出日志的key
			 *       去登入Map中找到对应的登入日志,并
			 *       以一个LogRec实例保存该配对日志,然后
			 *       存入配对日志的集合中。并将该配对日志
			 *       中的登入日志从登入Map中删除。这样一来
			 *       登入Map中应当只剩下没有配对的了。
			 *  5:将配对日志写入到logRecFile中
			 *  6:将所有未配对日志写入到loginLogFile中
			 *  7:将textLogFile文件删除
			 *  8:返回true,表示配对完毕
			 * 
			 */
		try {
			//1
			//1.1
			if(logRecFile.exists()){
				return true;
			}
			//1.2
			if(!textLogFile.exists()){
				System.out.println(textLogFile+"不存在!");
				return false;
			}
			
			//2
			List<LogData> list 
				= IOUtil.loadLogData(textLogFile);
			
			//3
			if(loginLogFile.exists()){
				list.addAll(
					IOUtil.loadLogData(loginLogFile)
				);
			}
			
			//4
			//4.1
			List<LogRec> matches 
				= new ArrayList<LogRec>();
			//4.2
			Map<String,LogData> loginMap
				= new HashMap<String,LogData>();
			Map<String,LogData> logoutMap
				= new HashMap<String,LogData>();
			
			//4.3
			for(LogData logData : list){
				String key = logData.getUser()+","+
			               logData.getPid();
				if(logData.getType()==LogData.TYPE_LOGIN){
					loginMap.put(key, logData);
				}else	if(logData.getType()==LogData.TYPE_LOGOUT){
					logoutMap.put(key, logData);
				}
			}
			
			//4.4
			Set<Entry<String,LogData>> entrySet
					= logoutMap.entrySet();
			for(Entry<String,LogData> e : entrySet){
				LogData logout = e.getValue();
				LogData login = loginMap.remove(e.getKey());
				LogRec logRec = new LogRec(login,logout);
				matches.add(logRec);
			}
			
			//5
			IOUtil.saveCollection(matches, logRecFile);
			
			//6
			IOUtil.saveCollection(
					loginMap.values(),loginLogFile
			);
			
			//7
			textLogFile.delete();
			
			//8
			return true;
			
			
		} catch (Exception e) {
			System.out.println("配对日志失败!");
			e.printStackTrace();
		}
		return false;
	}
	
	/**
	 * 第一步:解析日志
	 * @return true:解析成功
	 *         false:解析失败
	 */
	private boolean parseLogs(){
		/*
		 * 实现思路:
		 * 循环读取batch条日志,然后将每条日志中的
		 * 5个信息解析出来,最终组成一个字符串,以
		 * 行为单位,写入到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,表示工作完毕
		 * 
		 */
		RandomAccessFile 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();
			//打桩
//			System.out.println(
//					"lastPosition:"+lastPosition
//			);
			
			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>();
			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();
				
				//解析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 {
			/*
			 * 若lastPositionFile不存在,则说明
			 * 从来没有解析过,那么从头开始解析即可
			 */
			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("客户端运行失败!");
		}
	}
}








package com.dms;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.dms.bo.LogData;

/**
 * 该类是一个工具类,负责客户端的IO操作
 * @author Administrator
 *
 */
public class IOUtil {
	/**
	 * 从给定的文件中读取每一行字符串(配对日志)
	 * 并存入一个集合后返回
	 * @param file
	 * @return
	 * @throws Exception 
	 */
	public static List<String> loadLogRec(File file) throws Exception{
		BufferedReader br = null;
		try {
			br = new BufferedReader(
				new InputStreamReader(
					new FileInputStream(
						file	
					)	
				)	
			);
			List<String> list 
				= new ArrayList<String>();
			String line = null;
			while((line = br.readLine())!=null){
				list.add(line);
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally{
			if(br != null){
				br.close();
			}
		}
	}
	/**
	 * 从给定的文件中读取每一条配对日志,并存入
	 * 一个集合中然后返回。
	 * @param file
	 * @return
	 * @throws Exception 
	 */
	public static List<LogData> loadLogData(File file) throws Exception{
		BufferedReader br = null;
		try {
			br = new BufferedReader(
				new InputStreamReader(
					new FileInputStream(
						file	
					)	
				)	
			);
			List<LogData> list = new ArrayList<LogData>();
			String line = null;
			while((line = br.readLine())!=null){
				LogData logData = new LogData(line);
				list.add(logData);
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally{
			if(br!=null){
				br.close();
			}
		}
	}
	
	/**
	 * 将指定的long值以字符串的形式写入到
	 * 给定文件的第一行
	 * @param l
	 * @param file
	 * @throws Exception 
	 */
	public static void saveLong(
					long lon,File file) throws Exception{
		PrintWriter pw = null;
		try {
			pw = new PrintWriter(file);
			pw.println(lon);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally{
			if(pw != null){
				pw.close();
			}
		}
	}
	
	/**
	 * 将集合中每个元素的toString方法返回的字符串
	 * 以行为单位写入到指定文件中。
	 * @param c
	 * @param file
	 * @throws Exception 
	 */
	public static void saveCollection(
								Collection c,File file) throws Exception{
		PrintWriter pw = null;
		try {
			pw = new PrintWriter(file);
			for(Object o : c){
				pw.println(o);
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally{
			if(pw != null){
				pw.close();
			}
		}
	}
	
	/**
	 * 从给定的RandomAccessFile当前位置开始连续
	 * 读取length个字节,并转换为字符串后返回
	 * @param raf
	 * @param length
	 * @return
	 * @throws Exception 
	 */
	public static String readString(
					RandomAccessFile raf,int length) throws Exception{
		try {
			byte[] data = new byte[length];
			raf.read(data);
			return new String(data,"ISO8859-1");
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * 从给定文件中读取第一行字符串,然后将其
	 * 转换为一个long值后返回
	 * @param file
	 * @return
	 * @throws Exception 
	 */
	public static long readLong(File file) throws Exception{
		BufferedReader br = null;
		try {
			br = new BufferedReader(
				new InputStreamReader(
					new FileInputStream(
						file	
					)	
				)	
			);
			String line = br.readLine();
			return Long.parseLong(line);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally{
			if(br != null){
				br.close();
			}
		}
	}
}







<?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>








<?xml version="1.0" encoding="UTF-8"?>
<config>
	<!-- 服务端保存配对日志文件的文件名 -->
	<logrecfile>server-logs.txt</logrecfile>
	<!-- 线程池线程数量 -->
	<threadsum>30</threadsum>
	<!-- 服务端端口 -->
	<serverport>8088</serverport>
</config>