这个分布式主要实现的功能是将数据库中的需要下载的url从数据库中读取出来,同时server端通过将其中的数据读取出来后,同时将每一个对象发送给客户端
个人能力有限,这个还有待改进...
服务端分为: 数据分发,连接处理,以及连接检测和server主类共四个部分
客户端主要分为: 数据处理,连接检测以及客户端主类等三个部分
数据库部分分为: 数据库操作,对象封装,线程读取等三个部分
实体类分为: 待下载url的字段属性封装类,客户端链接实体类等两个实体类
工具类分为: json处理类
接口分为: 传输数据实体类接口,客户端数据接受接口
分布式功能描述:
1:数据读取线程通过数据库操作将读取到的数据库内容通过调用json操作类使用反射机制来实现数据库数据封装到对象,同时将对象放入到缓存中等待服务端读取。
message接口是用来实现多个对象的基层这个接口来实现上转型传递,在客户端通过Message msg = (Message )obj;
这个地方也不是很好,这是自己个人的一个小想法就这么写了。下面那个是实体类
public interface Message extends Serializable{
}
public class NeedFetchUrl implements Message{
private long _id;
private String url;
private String title;
private String boardUrl;
private long hostId;
private long fetchTime;
private int status;
private int type;
private int dynamic;
private int foreign;
private int siteWeight;
private String address;
private String host;
private int boardDeep;
private int contentType;
}
数据库操作类,主要是通过针对底层的一个简单封装,方便数据看操作,MongoDB主要是封装数据库链接,数据库查询删除等操作的操作,MongoFactory主要用于封装MongoDB提供方便操作,同时通过反射来实现对象的初始化
public class MongoDB
{
/**
* 数据库所在机器地址
*/
private String ip;
/**
* 数据库名称
*/
private String dbName;
/**
* 端口
*/
private int port;
/**
* 数据库连接对象
*/
private DB db;
/**
* 构造函数
* 作者: Sunx
* @param ip
* @param dbName
* @param port
*/
public MongoDB(String
ip, int port,String dbName){
this. ip =
ip;
this. dbName =
dbName;
this. port =
port;
//初始化 db连接对象
getDB();
}
/**
* 方法名:getDB
* 作者: Sunx
* 创建时间: Oct 9, 2014 9:39:19 AM
* 描述:获取数据库连接对象
* @return
*/
public void getDB(){
try {
DBAddress address = new DBAddress( ip, port, dbName);
DB db2 = MongoClient. connect(address);
db =
db2;
} catch (UnknownHostException
e) {
e.printStackTrace();
}
}
/**
* 方法名:findAll
* 作者: Sunx
* 创建时间: Oct 9, 2014 9:46:57 AM
* 描述:查询某一张表的所有数据
*/
public void findAll(String
tableName){
//获取操作表的对象
DBCollection table = db.getCollection(tableName);
DBCursor cur = table.find();
while (cur.hasNext())
{
System. out.println(cur.next());
}
}
/**
* 方法名:find
* 作者: Sunx
* 创建时间: Oct 13, 2014 2:12:54 PM
* 描述:根据条件从数据库中查询数据
* @param tableName
* @param keys
* @param values
* @return
*/
public List<DBObject>
find(String tableName,String[]keys,Object[] values){
//获取操作表的对象
DBCollection table = db.getCollection(tableName);
DBObject query = put(keys, values);
//获取查询结果
DBCursor curs = table.find(query);
List<DBObject> temp = new ArrayList<DBObject>();
for(DBObject
obj : curs.toArray()){
temp.add(obj);
}
return temp;
}
/**
* 方法名:update
* 作者: Sunx
* 创建时间: Oct 9, 2014 10:54:23 AM
* 描述:更新数据库中字段
* @param keys
* @param values
* @param newKeys
* @param newValues
*/
public void update(String
tableName,String[]keys,Object[] values,String[]newKeys,Object[] newValues){
//获取操作表的数据信息
DBCollection curs = db.getCollection(tableName);
//创建插入数据对象
DBObject oldObj = put(keys, values);
DBObject temp = put(newKeys, newValues);
DBObject newObj = new BasicDBObject( "$set",temp);
//执行更新
curs.update(oldObj,newObj, false, true);
}
/**
* 方法名:insert
* 作者: Sunx
* 创建时间: Oct 9, 2014 10:54:37 AM
* 描述:向数据库中插入数据信息
*/
public boolean insert(String
tableName,String[] keys,Object[]values){
//获取操作表的数据信息
DBCollection curs = db.getCollection(tableName);
DBObject obj = put(keys, values);
//将数据信息保存到数据库中
try{
WriteResult result = curs.insert(obj);
return (result.getError()
!= null) ? false : true;
} catch (MongoException
e) {
e.printStackTrace();
}
return false;
}
/**
* 方法名:delete
* 作者: Sunx
* 创建时间: Oct 9, 2014 1:54:47 PM
* 描述:根据条件删除数据信息
* @param tableName
* @param keys
* @param values
*/
public void delete(String
tableName,String[] keys,Object[]values){
//获取操作表的数据信息
DBCollection curs = db.getCollection(tableName);
DBObject obj = put(keys, values);
curs.remove(obj);
}
/**
* 方法名:isExits
* 作者: Sunx
* 创建时间: Oct 9, 2014 2:21:30 PM
* 描述:查询是否已经存在
* @param tableName
* @param keys
* @param values
* @return
*/
public boolean isExits(String
tableName,String[] keys,Object[]values){
//获取操作表的对象
DBCollection table = db.getCollection(tableName);
DBObject query = put(keys, values);
//获取查询结果
DBCursor curs = table.find(query);
List<DBObject> temp = curs.toArray();
if(temp.size()
<= 0) return true;
return false;
}
/**
* 方法名:execute
* 作者: Sunx
* 创建时间: Oct 9, 2014 1:55:46 PM
* 描述:获取数据信息
* @param tableName
* @param keys
* @param values
* @return
*/
public DBObject
put(String[] keys,Object[]values){
//创建插入数据对象
DBObject obj = new BasicDBObject();
//封装要插入的数据信息
for( int i=0;i<keys. length;i++){
obj.put(keys[i], values[i]);
}
//返回对象
return obj;
}
}
public class MongoFactory
{
/**
* 数据库操作对象
*/
private MongoDB mongo;
/**
* 将字符串解析为对象格式
*/
private JSONObject json;
/**
* 构造函数
* 作者: Sunx
* 初始化参数
*/
public MongoFactory(){
mongo = new MongoDB(Contant.MONGO_IP ,Contant.MONGO_DB_PORT ,Contant. MONGO_DB_NAME);
json = new JSONObject();
}
/**
* 方法名:get
* 作者: Sunx
* 创建时间: Oct 13, 2014 2:08:12 PM
* 描述:从数据库中读取数据
* @param <T>
* @return
*/
public <T>
List<T> find(String tableName,String[] keys,Object[] values,Class bean){
List<DBObject> curs = mongo.find(tableName,
keys, values);
List<T> result = json.parser(curs,bean);
return result;
}
/**
* 方法名:get
* 作者: Sunx
* 创建时间: Oct 13, 2014 2:22:58 PM
* 描述:从数据库中读取数据
* @param <T>
* @param tableName
* @param keys
* @param values
* @param bean
* @param num
* @return
*/
public <T>
List<T> find(String tableName,String[] keys,Object[] values,Class bean,int num){
List<DBObject> curs = mongo.find(tableName,
keys, values);
if(curs
== null || curs.size() <= 0) return null;
int temp
= num > curs.size()?curs.size():num;
curs = curs.subList(0,temp);
List<T> result = json.parser(curs,bean);
return result;
}
/**
* 方法名:update
* 作者: Sunx
* 创建时间: Oct 13, 2014 2:35:22 PM
* 描述:更新字段
* @param tableName
* @param keys
* @param values
* @param newKeys
* @param newValues
*/
public void update(String
tableName,String[] keys,Object[] values,String[] newKeys,Object[] newValues){
mongo.update(tableName,
keys, values, newKeys, newValues);
}
/**
* 方法名:isExits
* 作者: Sunx
* 创建时间: Oct 13, 2014 2:39:42 PM
* 描述:查询数据是否在数据库中已经存在
* @param tableName
* @param keys
* @param values
* @return
*/
public boolean isExits(String
tableName,String[] keys,Object[] values){
return mongo.isExits(tableName,
keys, values);
}
/**
* 方法名:main
* 作者: wangerkang
* 创建时间:2014 -10 -31
下午06:14:41
* 描述:
* @param args
*/
public static void main(String[]
args) {
MongoFactory factory = new MongoFactory();
List<Board> list = factory.find(Contant.MONGO_BOARD_TABLE_NAME , new String[]{"type" }, new Object[]{1},
Board. class);
System. err.println(list.size());
}
}
public class JSONObject
{
/**
* 方法名:parser
* 作者: Sunx
* 创建时间: Oct 11, 2014 10:37:08 AM
* 描述:解析查询出来的结果,并将其转化为对象
* @param tt
* @param bean
* @return
*/
public <T>
List<T> parser(List<DBObject> list,Class bean){
//创建返回集合
List<T> temp = new ArrayList<T>();
//遍历集合
for(DBObject
dbObj : list){
T result = (T)toNewBean(dbObj.toMap(), dbObj.keySet(), bean);
temp.add(result);
}
return temp;
}
/**
* 方法名:toNewBean
* 作者: Sunx
* 创建时间: Oct 11, 2014 9:57:16 AM
* 描述:字段对应的名称已经有了,查询bean中找到相应的属性值,同时在map中找到相应的属性对应的值
* @param map
* @param keys
* @param bean
* @return
*/
public Object
toNewBean(Map map,Set<String> keys,Class bean){
boolean bool
= true;
try {
//创建对象
Object obj = bean.newInstance();
for(String
str : keys){
//获取属性
Field field = bean.getDeclaredField(str);
if(field
== null){
bool = false;
break;
}
field.setAccessible( true);
//获取属性值
if(!map.containsKey(str)){
bool = false;
break;
}
Object value = map.get(str);
//设置属性值
setValue(field,obj,value);
}
if(bool) return obj;
} catch (SecurityException
e) {
e.printStackTrace();
} catch (NoSuchFieldException
e) {
e.printStackTrace();
} catch (InstantiationException
e) {
e.printStackTrace();
} catch (IllegalAccessException
e) {
e.printStackTrace();
}
return null;
}
/**
* 方法名:setValue
* 作者: Sunx
* 创建时间: Oct 11, 2014 9:48:37 AM
* 描述:设置参数值
* @param field
* @param obj
* @param value
* @return
*/
public Field
setValue(Field field,Object obj,Object value){
try {
if(field.getType().toString().contains( "int")){
int t
= getInt(value.toString());
field.set(obj, t);
} else if(field.getType().toString().contains( "long")){
long t
= getLong(value.toString());
field.set(obj, t);
} else if(field.getType().toString().contains( "float")){
float t
= getFloat(value.toString());
field.set(obj, t);
} else if(field.getType().toString().contains( "double")){
double t
= getDouble(value.toString());
field.set(obj, t);
} else if(field.getType().toString().contains( "char")){
char t
= getChar(value.toString());
field.set(obj, t);
} else if(field.getType().toString().contains( "String")){
field.set(obj, value.toString());
} else if(field.getType().toString().contains( "boolean")){
boolean bool
= getBoolean(value.toString());
field.set(obj, bool);
}
} catch (IllegalArgumentException
e) {
e.printStackTrace();
} catch (IllegalAccessException
e) {
e.printStackTrace();
}
return field;
}
/**
* 方法名:getInt
* 作者: Sunx
* 创建时间: Oct 11, 2014 9:48:12 AM
* 描述:获取 int值
* @param value
* @return
*/
public int getInt(String
value){
int t
= 0;
try {
if(value.contains( ".")){
t = ( int)Double. parseDouble(value);
} else{
t = Integer. parseInt(value);
}
} catch (Exception
e) {
e.printStackTrace();
}
return t;
}
/**
* 方法名:getLong
* 作者: Sunx
* 创建时间: Oct 11, 2014 9:48:08 AM
* 描述:获取long值
* @param value
* @return
*/
public long getLong(String
value){
long t
= 0;
try {
if(value.contains( ".")){
t = ( int)Double. parseDouble(value);
} else{
t = Long. parseLong(value);
}
} catch (Exception
e) {
e.printStackTrace();
}
return t;
}
/**
* 方法名:getFloat
* 作者: Sunx
* 创建时间: Oct 11, 2014 9:47:53 AM
* 描述:获取float值
* @param value
* @return
*/
public float getFloat(String
value){
try {
return Float. parseFloat(value);
} catch (Exception
e) {
e.printStackTrace();
}
return 0;
}
/**
* 方法名:getDouble
* 作者: Sunx
* 创建时间: Oct 11, 2014 9:47:38 AM
* 描述:获取double值
* @param value
* @return
*/
public double getDouble(String
value){
try {
return Double. parseDouble(value);
} catch (Exception
e) {
e.printStackTrace();
}
return 0;
}
/**
* 方法名:getChar
* 作者: Sunx
* 创建时间: Oct 11, 2014 9:47:13 AM
* 描述:获取char值
* @param value
* @return
*/
public char getChar(String
value){
if(value.length()
< 0 || value == null) return '0';
return value.charAt(0);
}
/**
* 方法名:getBoolean
* 作者: Sunx
* 创建时间: Oct 11, 2014 9:46:18 AM
* 描述:获取Boolean值
* @param value
* @return
*/
public boolean getBoolean(String
value){
if(value.contains( "true")
|| value.contains("True" ))return true;
return false;
}
}
通过数据库操作的封装后,下面要做的就是通过现场来读取数据将其放入到缓存中以便给服务端获取数据,数据读取线程通过继承Runnable接口来实现线程任务的,针对实现Runnable接口,主要是Runnbale接口的出现时针对线程任务的,实现了任务的对象封装,同时java中是单继承的,不用因继承了某个类而不能再继承其他类了
public class GetUrl implements Runnable{
private List<NeedFetchUrl> list;
private Set<String> cache;
private MongoFactory factory;
/**
* 构造函数
* 作者:Administrator
*/
public GetUrl(){
list = new LinkedList<NeedFetchUrl>();
cache = new HashSet<String>();
factory = new MongoFactory();
}
@Override
public void run()
{
while( true){
try {
System. err.println( "start
to get url from db ...");
List<NeedFetchUrl> list = factory.find(Contant. MONGO_URL_TABLE_NAME , new
String[]{"status" }, new Object[]{0},
NeedFetchUrl. class);
if(list
== null || list.size() <= 0){
Thread. sleep(1000 * 60);
continue;
}
set(list);
Thread. sleep(1000 * 60);
} catch (Exception
e) {
e.printStackTrace();
}
}
}
public synchronized void set(List<NeedFetchUrl>
list){
try {
int j
= 0;
for( int i=0;i<list.size();i++){
if(! cache.contains(list.get(i).getUrl())){
this. list.add(list.get(i));
cache.add(list.get(i).getUrl());
j++;
}
}
System. out.println( "wait
add url to cache,add.size:" + j);
} catch (Exception
e) {
e.printStackTrace();
}
}
public synchronized NeedFetchUrl
get(){
NeedFetchUrl need = null;
try {
System. out.println( list.size());
if(! list.isEmpty()){
need = list.remove(0);
cache.remove(need.getUrl());
}
} catch (Exception
e) {
e.printStackTrace();
}
return need;
}
public static void main(String[]
args) {
GetUrl getUrl = new GetUrl();
Thread tt = new Thread(getUrl);
tt.start();
}
}
2:服务端读取对象以后,由于该对象继承了数据传输接口,针对不同的类只要实现这个接口就行,服务端分发这个接类对象,在客户端通过上转型来接受传递过来的对象,然后判断实际的类型。在这个分布式代码中没有这么做,因为这个事第一版,只是分发一个对象就没有写这个复杂。
3:服务端通过绑定数据分发线程,连接检测线程以及连接接收线程等部分来实现程序的调用,数据分发线程从数据读取线程中的缓存中拿去一个对象,通过服务端主类获取客户端链接缓存队列,遍历队列将队列中的客户端链接读取出来,判断客户端状态,如果客户端状态为1,表示该客户端已经处于离线状态或者是当时网络处于堵塞状态,那么这次就不会发送数据给该客户端,如果客户端状态为0,则将该对象发送过去。
链接检测线程:如果链接五次都没有链接上该客户端,那么认定这个客户端已经失效,服务端丢弃掉这个客户端。链接检测线程通过向客户端发送心跳包的形式来检测客户端是否还处于正常状态,同样在win7系统中发送心跳包17次以后,操作系统底层会强制将该socket该关闭掉,那么这个时候链接就会中断。链接检测线程就会重新链接服务端,在这个过程中可以将以前的客户端的端口绑定到重新链接服务端的socket上,针对这个想法做了一个实现,如果这个时候没有链接上主机,从而导致在下载再次链接时报该端口已经被占用,这个是我代码里面的一个小错误,在下面会优化
服务端的主类代码:
public class Server{
private Map<String,Connection> map;
private ServerHandler serverHandler;
private AcceptHandler acceptHandler;
private CheckHandler checkHandler;
public Server(){
map = new HashMap<String,Connection>();
}
public Map<String,Connection>
getMap() {
return map;
}
public synchronized void setList(Connection
conn){
if( map.containsKey(conn.getId())){
map.get(conn.getId()).setStatus(0);
map.get(conn.getNum()).setNum(0);
} else{
map.put(conn.getId(),
conn);
}
}
public synchronized void remove(Connection
conn){
if( map.containsKey(conn.getId())){
map.remove(conn.getId());
}
}
//绑定分发url机制
public void setServerHandler(ServerHandler
serverHadler){
this. serverHandler =
serverHadler;
this. serverHandler.setServer( this);
//启动url 分发服务器
Thread t = new Thread( this. serverHandler);
t.start();
}
public void setAcceptHandler(AcceptHandler
acceptHandler){
this. acceptHandler =
acceptHandler;
this. acceptHandler.setServer( this);
//启动连接检测线程
acceptHandler.runAccept();
Thread tt = new Thread( this. acceptHandler);
tt.start();
}
public void steCheckHandler(CheckHandler
checkHandler){
this. checkHandler =
checkHandler;
this. checkHandler.setServer( this);
//启动连接检测线程
Thread tt = new Thread( this. checkHandler);
tt.start();
}
public void runServer(){
}
public static void main(String[]
args) {
//启动数据读取线程
GetUrl getUrl = new GetUrl();
Thread tt = new Thread(getUrl);
tt.start();
//启动server
Server server = new Server();
//为server绑定数据分发线程
server.setServerHandler( new ServerHandler(getUrl));
//为server绑定链接检测线程
server.steCheckHandler( new CheckHandler());
//为server绑定接受连接线程
server.setAcceptHandler( new AcceptHandler(10000));
//运行server
server.runServer();
}
}
服务端主类代码中主要是给服务端绑定各种处理机制,分发线程,检测线程以及连接线程等。下面是代码实现:
public class ServerHandler implements Runnable{
private GetUrl getUrl;
private Server server;
public ServerHandler(GetUrl
getUrl){
this. getUrl =
getUrl;
}
public void setServer(Server
server){
this. server =
server;
}
@Override
public void run()
{
NeedFetchUrl need = null;
while( true){
try {
System. err.println( "server
handler start...");
if( server == null){
System. err.println( "server
handler to init server error");
break;
}
need = getUrl.get();
if(need
== null){
System. err.println( "server
handler to get url error");
Thread. sleep(1000 * 1);
continue;
}
sendData(need);
Thread. sleep(1000 * 10);
} catch (Exception
e) {
e.printStackTrace();
}
}
}
public void sendData(List<NeedFetchUrl>
needs){
sendDataToClient(needs);
}
public void sendData(NeedFetchUrl
need){
sendDataToClient(need);
}
public <T> void sendDataToClient(T
t){
try {
Map<String,Connection> map = server.getMap();
if(map
== null || map.size() <= 0){
System. err.println( "没有客户端到服务器..." );
return;
}
for(Entry<String,
Connection> entry : map.entrySet()){
sendData(entry.getValue(),t);
}
} catch (Exception
e) {
e.printStackTrace();
}
}
public void sendData(Connection
conn,Object obj){
ObjectOutputStream oos = null;
try {
oos = getWriter(conn);
if(oos
== null){
System. err.println( "客户端链接已经丢失,等待重新链接..." );
return;
}
oos.writeObject(obj);
oos.flush();
System. err.println( "send
to client data:" + obj.toString());
} catch (Exception
e) {
e.printStackTrace();
}
}
public ObjectOutputStream
getWriter(Connection conn) throws Exception{
if(conn.getStatus()
== 1) return null;
return new ObjectOutputStream(conn.getSocket().getOutputStream());
}
public static void main(String[]
args) {
//启动数据读取线程
GetUrl getUrl = new GetUrl();
Thread tt = new Thread(getUrl);
tt.start();
//
ServerHandler serverHandler = new ServerHandler(getUrl);
//启动url 分发服务器
Thread t = new Thread(serverHandler);
t.start();
}
}
下面是链接检测线程:
public class CheckHandler implements Runnable{
private Server server;
public void setServer(Server
server){
this. server =
server;
}
@Override
public void run()
{
while( true){
try {
System. err.println( "checkHandler");
//获取所有链接
check();
Thread. sleep(1000 * 2);
} catch (Exception
e) {
e.printStackTrace();
}
}
}
public void check(){
try {
Map<String,Connection> map = server.getMap();
if(map
== null || map.size() <= 0){
System. err.println( "没有客户端到服务器..." );
return;
}
for(Entry<String,
Connection> entry : map.entrySet()){
//判断这个客户端是否已经挂了
if(entry.getValue().getStatus()
!= 0){
System. err.println( "客户端已经处于离线状态,等待下次链接:" +
entry.getKey() + "\t该客户端的num:" + entry.getValue().getNum());
if(entry.getValue().getStatus()
== 1 && entry.getValue().getNum() >= 5){
//移除该客户端
server.remove(entry.getValue());
System. err.println( "客户端链接次数超过限制,系统决定移除该客户端:" +
entry.getKey());
continue;
}
}
check(entry.getValue());
}
} catch (Exception
e) {
e.printStackTrace();
}
}
public void check(Connection
conn){
try {
System. err.println( "检测客户端是否连接:" +
conn.getId());
conn.getSocket().sendUrgentData(0xff);
setRig(conn);
} catch (Exception
e) {
e.printStackTrace();
//这个地方报错,说明客户端与服务端链接不正常
setErr(conn);
}
}
public void setRig(Connection
conn){
if(conn.getStatus()
!= 0){
conn.setStatus(0);
conn.setNum(0);
}
}
public void setErr(Connection
conn){
if(conn.getStatus()
== 1 && conn.getNum() > 5){
//移除该客户端
server.remove(conn);
} else{
conn.setStatus(1);
conn.setNum(conn.getNum() + 1);
}
}
}
接收连接线程:
public class AcceptHandler implements Runnable{
private ServerSocket serverSocket;
private int port;
private Server server;
public AcceptHandler( int port){
this. port =
port;
}
public void setServer(Server
server){
this. server =
server;
}
@Override
public void run()
{
//监听连接
while( true){
try {
System. err.println( "wait
to client to connecte...");
Socket socket = serverSocket.accept();
//封装连接,将连接存储到缓存中
Connection conn = new Connection();
conn.setSocket(socket);
conn.setStatus(0);
conn.setId(socket.getInetAddress().getHostAddress() + "/" +
socket.getPort());
conn.setNum(0);
//将连接对象存储到缓存中
server.setList(conn);
System. err.println( "a
new client to connecte the server,client.ip:" + socket.getInetAddress().getHostAddress());
} catch (Exception
e) {
e.printStackTrace();
}
}
}
public void runAccept(){
try {
serverSocket = new ServerSocket( port);
// accept();
} catch (Exception
e) {
e.printStackTrace();
}
}
public void accept(){
//监听连接
while( true){
try {
System. err.println( "wait
to client to connecte...");
Socket socket = serverSocket.accept();
//封装连接,将连接存储到缓存中
Connection conn = new Connection();
conn.setSocket(socket);
conn.setStatus(0);
conn.setId(socket.getInetAddress().getHostAddress());
conn.setNum(0);
//将连接对象存储到缓存中
server.setList(conn);
System. err.println( "a
new client to connecte the server,client.ip:" + socket.getInetAddress().getHostAddress());
} catch (Exception
e) {
e.printStackTrace();
}
}
}
}
4:客户端通过数据处理线程来接收服务端发送过来的对象,并将对象中的信息打印出来,通过链接检测线程来实现客户端与服务端的连接
客户端主类:
public class Client
{
private Socket socket;
private DataHandler dataHandler;
private ClientCheckHandler checkHandler;
public Socket
getSocket(){
return socket;
}
public void setSocket(Socket
socket){
this. socket =
socket;
}
//绑定主机
public void bind(String
host, int port){
try {
socket = new Socket(host,port);
socket.setKeepAlive( true);
} catch (Exception
e) {
e.printStackTrace();
}
}
//绑定数据接受线程
public void setDataHandler(DataHandler
dataHandler){
this. dataHandler =
dataHandler;
this. dataHandler.setClient( this);
}
//
public void setCheckHandler(ClientCheckHandler
checkHandler){
this. checkHandler =
checkHandler;
this. checkHandler.setClient( this);
}
public void runClient(){
Thread t = new Thread( dataHandler);
t.start();
Thread tt = new Thread( checkHandler);
tt.start();
}
public static void main(String[]
args) {
Client client = new Client();
//给客户端绑定服务器
client.bind( "192.168.1.103",10000);
//给客户端绑定数据接受器
client.setDataHandler( new ClientDataHandler());
//客户端链接检测线程
client.setCheckHandler( new ClientCheckHandler());
//运行客户端
client.runClient();
}
}
客户端链接检测线程:
public class ClientCheckHandler implements Runnable{
private Client client;
public void setClient(Client
client){
this. client =
client;
}
@Override
public void run()
{
while( true){
try {
check();
Thread. sleep(1000 * 2);
} catch (Exception
e) {
e.printStackTrace();
}
}
}
public void check(){
try {
client.getSocket().sendUrgentData(0xff);
} catch (Exception
e) {
e.printStackTrace();
System. err.println( "客户端与主机链接异常,将重新链接主机..." );
try {
// InetAddress inetAddr = InetAddress.getLocalHost();
Socket socket = new Socket(
client.getSocket().getInetAddress().getHostAddress(),
client.getSocket().getPort()
// inetAddr,
// client.getSocket().getLocalPort()
);
client.setSocket(socket);
System. err.println( "客户端重新链接上主机,客户端信息为:" +
socket.getLocalAddress().getHostAddress() + "/" + socket.getLocalPort());
} catch (UnknownHostException
e1) {
e1.printStackTrace();
} catch (IOException
e1) {
e1.printStackTrace();
}
}
}
}
客户端数据接受线程:
public class ClientDataHandler implements DataHandler{
private Client client;
@Override
public void setClient(Client
client) {
this. client =
client;
}
@Override
public void run()
{
while( true){
try {
Socket socket = client.getSocket();
if(socket
== null){
System. err.println( "client
to get socket error");
Thread. sleep(1000);
continue;
}
getData(socket);
} catch (Exception
e) {
e.printStackTrace();
}
}
}
public void getData(Socket
socket){
try {
ObjectInputStream ois = getReader(socket);
if(ois
== null) return;
Object obj = ois.readObject();
System. err.println( "client接受到的数据为:" +
obj.toString());
} catch (Exception
e) {
e.printStackTrace();
}
}
public ObjectInputStream
getReader(Socket socket) throws Exception{
if(socket.getInputStream().available()
< 1) return null;
return new ObjectInputStream(socket.getInputStream());
}
}
个人能力有限,这个还有待改进...