前言
最近javaSwing开发中遇到了需要用TCP的长连接来维持服务端和客户端的连接,所以把自己项目中书写的服务端接收心跳包并回复客户端的方法记录下来,以方便以后遇到相同问题能更快的解决。
服务端代码

package tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import homektv.Fangjian; import net.sf.json.JSONObject; import utils.DoPOST; /** * C/S架构的服务端对象。 * <p> * 创建时间:2010-7-18 上午12:17:37 * @author weiyiji * @since 1.0 */ public class Server { private int port = 65432; private volatile boolean running=false; private long receiveTimeDelay=180000; //接收時間限制為3分鐘 private Thread connWatchDog; public Map<String, Socket> map = new HashMap<String, Socket>(); private static Server record; private Server() {}; static { record = new Server(); } public Map<String, Socket> getMap() { return map; } public void setMap(Map<String, Socket> map) { this.map = map; } public static Server getInServer() { return record; } public void start(){ if(running) { return; } running=true; connWatchDog(); } public void connWatchDog() { new Thread() { @Override public void run() { try { ServerSocket ss = new ServerSocket(port,5); while(running){ Socket s = ss.accept(); map.put(s.getInetAddress()+"", s); new SocketAction(s).start(); } } catch (IOException e) { e.printStackTrace(); } } }.start(); } //利用內部類創建線程 class SocketAction extends Thread{ Socket s; boolean run=true; String roomid; long lastReceiveTime = System.currentTimeMillis(); public SocketAction(Socket s) { this.s = s; } public void run() { while(running && run){ if(System.currentTimeMillis()-lastReceiveTime>receiveTimeDelay){ overThis(); currentThread().interrupt(); }else{ try { InputStream in = s.getInputStream(); if(in.available()>0){ lastReceiveTime = System.currentTimeMillis(); byte[] data = new byte[1024]; int len =in.read(data); String out = new String(data,0,len,"gbk"); if(!"0.0.0".equals(out)) { roomid = insert(out); } OutputStream oos = s.getOutputStream(); if(out!=null){ oos.write("OK".getBytes("gbk")); oos.flush(); } }else{ Thread.sleep(10); } } catch (Exception e) { e.printStackTrace(); overThis(); } } } } private void overThis() { if(run)run=false; if(s!=null){ try { String ipString = (s.getInetAddress()+"").substring(1); List<String> list1 = new ArrayList<String>(); List<String> list2 = new ArrayList<String>(); list1.add("roomid"); list2.add(roomid); list1.add("roomstatus"); list2.add("已关房"); String doPOST = DoPOST.doPOST("/roomController/updateRoomConfig", list1, list2); s.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 插入房间信息 * @param news * @return */ public String insert(String news) { List<String> list1 = new ArrayList<String>(); List<String> list2 = new ArrayList<String>(); list1.add("news"); list2.add(news); String doPOST = DoPOST.doPOST("/roomController/insertRoom", list1, list2); JSONObject jsonObject = JSONObject.fromObject(doPOST); return jsonObject.getString("data"); } } /** * 向客戶端發送消息 * @param sendMessString * @param ip */ public void SendMessage(String sendMessString,String ip) { try { Socket socket = map.get("/"+ip); OutputStream oos = socket.getOutputStream(); oos.write(sendMessString.getBytes("gbk")); oos.flush(); } catch (IOException e) { e.printStackTrace(); } } }
服务端发送消息

public void sendMessage() { List<String> list1 = new ArrayList<String>(); List<String> list2 = new ArrayList<String>(); list1.add("filename"); list2.add("information.txt"); new Thread() { public void run() { try { while(true) { String doPOST = DoPOST.doPOST("/informationController/read", list1, list2); if("fail".equals(doPOST)) { //如果没请求到则每隔半分钟请求一次 currentThread().sleep(30000); continue; } JSONObject record = JSONObject.parseObject(doPOST); String time = record.getString("time"); String content = record.getString("content"); if("0".equals(time)) { continue; } if(StringUtils.isEmpty(record.getString("chckbxNewCheckBox"))) { continue; } if(StringUtils.isEmpty(content)) { continue; } List<String> list = Fangjian.getAllSendIp(); if(list.size() != 0) { //发送火警指令 for(String sendip : list) { String sendOrder = CommonUtils.SendOrder(content,sendip); } } currentThread().sleep(Integer.valueOf(time)*60000); } } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }
代码说明
上面只是TCP协议中服务端的代码,整体思路是,客户端启动后,客户端向服务端发送信息,服务端向客户端返回OK,以此建立连接,然后每隔一定时间客户端向服务端发送一个心跳包,心跳包格式可自行定义,规定若是服务端3分钟之内为收到客户端的心跳包则认为客户端已下线,服务端则关闭与对应的客户端的连接。该案列中是一台服务器对应多个客户端,同时与多个客户端进行通话。
其中用一个Map<String, Socket> 的map来保存服务端监听的每一个客户端的ip和对应的Socket,若服务器需单独与某个客户端通信,只需根据ip到map中去获取对应的Socket即可向客户端发送信息。