socket long connection
Performance issues caused by client maintaining heartbeat
Client code
It realizes a thread that sends heartbeat packets to the server regularly, and a thread that receives messages returned from the server.
package practice; import client.Client; import client.KeepAlive; import java.io.*; import java.net.Socket; import java.util.Scanner; /** * Created by sheng on 17/12/22. */ public class MyClient { private boolean running =false; interface ObjectAction { Object doAction(Object obj); }class DefaultObjectAction implements ObjectAction { @Override public Object doAction(Object object) { System.out.println(object); return object; } } public static void main(String[] args) { MyClient client = new MyClient(); client.doStart(); } public void doStart(){ try { if(running)return; Socket socket=new Socket("127.0.0.1",7890); running=true; Thread t1=new Thread(new KeepAliveWatchDog(socket)); t1.start(); Thread t2=new Thread(new ReceiveThread(socket)); t2.start(); Scanner input=new Scanner(System.in); String command=input.next(); if(command.equals("cancel")){ doStop(); } } catch (IOException e) { e.printStackTrace(); } } public void doStop(){ if(running) running=false; } class KeepAliveWatchDog extends Thread{ Socket socket; long lastReceive=System.currentTimeMillis(); public KeepAliveWatchDog(Socket socket){ this.socket=socket; } @Override public void run() { //The thread is kept continuously active by the running variable. Once the main thread requests to close, all the sub threads should actively close the socket //Watchdog beats every 3 seconds while(running){ try { if (System.currentTimeMillis() - lastReceive > 2000) { OutputStream outputStream = socket.getOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(new KeepAlive()); System.out.println("send to server..."); objectOutputStream.flush(); lastReceive = System.currentTimeMillis(); } else { Thread.sleep(10); } }catch(IOException e){ }catch(InterruptedException e){ } } if(!running){ close(); } } public void close(){ if(this.socket!=null){ try { this.socket.close(); }catch(IOException ex){ } } System.out.println("KeepAliveWatchDog socket closed"); } } class ReceiveThread extends Thread{ Socket socket; public ReceiveThread(Socket socket){ this.socket=socket; } @Override public void run() { while(running){ try { InputStream inputStream = socket.getInputStream(); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); if (objectInputStream.available() > 0) { Object object = objectInputStream.readObject(); ObjectAction oa = new DefaultObjectAction(); oa.doAction(object); } else { Thread.sleep(10); } }catch(IOException e){ }catch(InterruptedException e){ }catch(ClassNotFoundException e){ } } if(!running){ close(); } } public void close(){ if(this.socket!=null){ try { this.socket.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("ReceiveThread socket closed"); } }
}
Server code
The implementation of a guardian thread to listen to the client connection socket, each client socket will establish a new processing thread to handle the client request, and return the heartbeat package.
package practice; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; import java.util.Scanner; /** * Created by sheng on 17/12/20. * Automatically match processors based on the type of message */ public class MyServer { private final int PORT = 7890; private boolean running = false; private Thread thread1; private Map mapping=new HashMap(); interface ObjectAction { Object doAction(Object obj); } class DefaultObjectAction implements ObjectAction { @Override public Object doAction(Object object) { System.out.println(object); return object; } } public static void main(String[] args) { MyServer server = new MyServer(); server.doStart(); } public void doStart() { //Start the accept thread to if (running) return;//Ensure that the server is single threaded running = true; thread1 = new Thread(new ConnWatchDogThread()); thread1.start(); System.out.println("server initial...."); Scanner input=new Scanner(System.in); String next=input.next(); if(next.equals("cancel")){ doStop(); } //Start socket action thread to receive processing } public void doStop() { if (running) running = false; } /** * Message processor model to avoid redundant judgment * The way to get value object processor through key is simpler and more convenient than if..else... Or design pattern * */ public void addMapping(Class classes, ObjectAction oa){ this.mapping.put(classes,oa); } public ObjectAction getAction(Class classes){ return this.mapping.get(classes); } /** * Receiving thread */ class ConnWatchDogThread extends Thread { ServerSocket socket; @Override public void run() { try { socket = new ServerSocket(PORT, 5); while (running) { Socket socket1 = socket.accept();//Blocking method, but only one will be received, cyclic reception is required System.out.println("accepted client:"+socket1.getRemoteSocketAddress()); Thread thread1 = new Thread(new SocketActionThread(socket1)); thread1.start(); } if(!running) close(); } catch (IOException e) { e.printStackTrace(); } } public void close() { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * Message processing thread */ class SocketActionThread extends Thread { Socket socket; long lastReceiveTime = System.currentTimeMillis(); public SocketActionThread(Socket socket) { this.socket = socket; } private int delay = 3000; private boolean runnable=true; @Override public void run() { //A long connection requires a heartbeat to feed the dog every 3 seconds, while(running && runnable) { try { if (System.currentTimeMillis() - lastReceiveTime > delay && running) { executeOvertime(); } else { //Execute read write socket InputStream inputStream = socket.getInputStream(); if (inputStream.available() > 0) { ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); Object obj = objectInputStream.readObject(); lastReceiveTime = System.currentTimeMillis(); ObjectAction oa = new DefaultObjectAction(); Object out = oa.doAction(obj); if (out != null) { //Write back to client OutputStream outputStream = socket.getOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(out); } } else { Thread.sleep(10); } } } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } if(!running){ close(); } } //Timeout, disconnect socket actively public void executeOvertime() { if(runnable)runnable=false; if (this.socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("client over time:"+socket.getRemoteSocketAddress()); } public void close(){ if(this.socket!=null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
experimental result
I opened a server and five clients. client sends heartbeat every 2 seconds, and the server will reply after receiving it.
I didn't end it manually, but the log prompted that the client was disconnected.
2017-12-23 11:36:19 start of maintenance connection package
2017-12-23 16:06:04 end of maintenance connection package
Memory usage information:
Five java processes have been started, which occupy 598mb, 555mb, 471mb, 447mb and 432mb of memory respectively
After the client is closed manually, only the server process is left, occupying 48MB.
Excuse me?
Interested gods, please see where the client writes bug s, which leads to such a large memory utilization rate of the client, or the maintenance of long connection originally takes up a high memory. I can't find the reason. Please help the gods