Absrtact: The long connection between server and client is realized through Netty, and heartbeat detection
The long connection between server and client is realized through Netty, and heartbeat detection.
Basic idea: The Netty server saves all connected client Socketchannel through a map, and the client ID is the key to the map. Each time the server side wants to send a message to a client, simply take out the corresponding Socketchannel according to ClientID, and write the message to it. Heartbeat detection through the Idleevent event, timed to the server to broadcast a ping message to detect whether the socketchannel is broken.
Environmental JDK1.8 and Netty5
The following are specific code implementations and descriptions:
1 public Share section (mainly contains the definition of the Message protocol type)
Design Message Type:
public enum Msgtype {
Ping,ask,reply,login
}
Message base class:
must implement the sequence, Serialversionuid must have, no one in the Netty message serialization deserialization will be problematic, receive messages!!!
Public abstract class Basemsg implements Serializable {
Private static final long serialversionuid = 1L;
Rivate msgtype type;
Must be unique, no one will have channel call chaos
Private String clientId;
Initializing the Client ID
Public basemsg () {
This.clientid = Constants.getclientid ();
}
Public String Getclientid () {
return clientId;
}
public void Setclientid (String clientId) {
This.clientid = clientId;
}
Public Msgtype GetType () {
return type;
}
public void SetType (Msgtype type) {
This.type = type;
}
}
Constant settings:
public class Constants {
private static String clientId;
public static String Getclientid () {
return clientId;
}
public static void Setclientid (String clientId) {
Constants.clientid = clientId;
}
}
Logon Type message:
public class Loginmsg extends Basemsg {
Private String UserName;
private String password;
Public loginmsg () {
Super ();
SetType (Msgtype.login);
}
Public String GetUserName () {
return userName;
}
public void Setusername (String userName) {
This.username = UserName;
}
Public String GetPassword () {
return password;
}
public void SetPassword (String password) {
This.password = password;
}
}
Heartbeat detection Ping Type message:
public class Pingmsg extends Basemsg {
Public pingmsg () {
Super ();
SetType (msgtype.ping);
}
}
Request Type message:
public class Askmsg extends Basemsg {
Public askmsg () {
Super ();
SetType (Msgtype.ask);
}
Private Askparams params;
Public Askparams Getparams () {
return params;
}
public void SetParams (Askparams params) {
This.params = params;
}
}
Request Type Parameters
The serialization interface must be implemented
public class Askparams implements Serializable {
Private static final long serialversionuid = 1L;
Private String auth;
Public String GetAuth () {
return auth;
}
public void Setauth (String auth) {
This.auth = auth;
}
}
Response type message:
public class Replymsg extends Basemsg {
Public replymsg () {
Super ();
SetType (msgtype.reply);
}
Private Replybody body;
Public Replybody GetBody () {
return body;
}
public void Setbody (Replybody body) {
This.body = body;
}
}
Corresponding type body pair image
public class Replybody implements Serializable {
Private static final long serialversionuid = 1L;
}
public class Replyclientbody extends Replybody {
Private String Clientinfo;
Public Replyclientbody (String clientinfo) {
This.clientinfo = Clientinfo;
}
Public String Getclientinfo () {
return clientinfo;
}
public void Setclientinfo (String clientinfo) {
This.clientinfo = Clientinfo;
}
}
public class Replyserverbody extends Replybody {
Private String ServerInfo;
Public Replyserverbody (String serverinfo) {
This.serverinfo = ServerInfo;
}
Public String Getserverinfo () {
return serverinfo;
}
public void SetServerInfo (String serverinfo) {
This.serverinfo = ServerInfo;
}
}
2 server side: mainly contains the implementation and bootstrap of the Map,channelhandler referenced by Socketchannel.
MAP:
public class Nettychannelmap {
private static map<string,socketchannel> map=new concurrenthashmap<string, socketchannel> ();
public static void Add (String clientid,socketchannel socketchannel) {
Map.put (Clientid,socketchannel);
}
public static Channel Get (String clientId) {
Return Map.get (CLIENTID);
}
public static void Remove (Socketchannel socketchannel) {
For (Map.entry Entry:map.entrySet ()) {
if (Entry.getvalue () ==socketchannel) {
Map.Remove (Entry.getkey ());
}
}
}
}
Handler:
public class Nettyserverhandler extends Simplechannelinboundhandler<basemsg> {
@Override
public void Channelinactive (Channelhandlercontext ctx) throws Exception {
Channel invalidation, removing from map
Nettychannelmap.remove ((Socketchannel) Ctx.channel ());
}
@Override
protected void messagereceived (Channelhandlercontext channelhandlercontext, basemsg basemsg) throws Exception {
if (MsgType.LOGIN.equals (Basemsg.gettype ())) {
Loginmsg loginmsg= (loginmsg) basemsg;
if ("Robin". Equals (Loginmsg.getusername ()) && "Yao". Equals (Loginmsg.getpassword ())) {
Log in successfully and save the channel to the server map
Nettychannelmap.add (Loginmsg.getclientid (), (Socketchannel) Channelhandlercontext.channel ());
SYSTEM.OUT.PRINTLN ("Client" +loginmsg.getclientid () + "login Success");
}
}else{
if (Nettychannelmap.get (Basemsg.getclientid ()) ==null) {
Description is not logged in, or the connection is broken, the server initiates a login request to the client to have the client log on again
Loginmsg loginmsg=new loginmsg ();
Channelhandlercontext.channel (). Writeandflush (LOGINMSG);
}
}
Switch (Basemsg.gettype ()) {
Case ping:{
Pingmsg pingmsg= (pingmsg) basemsg;
Pingmsg replyping=new pingmsg ();
Nettychannelmap.get (Pingmsg.getclientid ()). Writeandflush (replyping);
}break;
Case ask:{
Request received from client
Askmsg askmsg= (askmsg) basemsg;
if ("AuthToken". Equals (Askmsg.getparams (). GetAuth ())) {
Replyserverbody replybody=new replyserverbody ("Server info $!!!");
Replymsg replymsg=new replymsg ();
Replymsg.setbody (Replybody);
Nettychannelmap.get (Askmsg.getclientid ()). Writeandflush (REPLYMSG);
}
}break;
Case reply:{
Receive client reply
Replymsg replymsg= (replymsg) basemsg;
Replyclientbody clientbody= (Replyclientbody) replymsg.getbody ();
SYSTEM.OUT.PRINTLN ("Receive client msg:" +clientbody.getclientinfo ());
}break;
Default:break;
}
Referencecountutil.release (BASEMSG);
}
}
Serverbootstrap:
public class Nettyserverbootstrap {
private int port;
Private Socketchannel Socketchannel;
public nettyserverbootstrap (int port) throws Interruptedexception {
This.port = port;
Bind ();
}
private void Bind () throws Interruptedexception {
Eventloopgroup boss=new Nioeventloopgroup ();
Eventloopgroup worker=new Nioeventloopgroup ();
Serverbootstrap bootstrap=new serverbootstrap ();
Bootstrap.group (Boss,worker);
Bootstrap.channel (Nioserversocketchannel.class);
Bootstrap.option (Channeloption.so_backlog, 128);
Disable Nagle through Nodelay so that messages are sent out immediately without waiting for a certain amount of data to be sent out
Bootstrap.option (Channeloption.tcp_nodelay, true);
Maintain Long connection status
Bootstrap.childoption (channeloption.so_keepalive, true);
Bootstrap.childhandler (New channelinitializer<socketchannel> () {
@Override
protected void Initchannel (Socketchannel socketchannel) throws Exception {
Channelpipeline p = socketchannel.pipeline ();
P.addlast (New Objectencoder ());
P.addlast (New Objectdecoder (classresolvers.cachedisabled (null)));
P.addlast (New Nettyserverhandler ());
}
});
Channelfuture f= Bootstrap.bind (port). sync ();
if (f.issuccess ()) {
SYSTEM.OUT.PRINTLN ("Server Start---------------");
}
}
public static void Main (String []args) throws Interruptedexception {
Nettyserverbootstrap bootstrap=new Nettyserverbootstrap (9999);
while (true) {
Socketchannel channel= (Socketchannel) nettychannelmap.get ("001");
if (channel!=null) {
Askmsg askmsg=new askmsg ();
Channel.writeandflush (ASKMSG);
}
TimeUnit.SECONDS.sleep (5);
}
}
}
3 Client side: Includes initiating login, sending heartbeat, and corresponding message processing
Handler
public class Nettyclienthandler extends Simplechannelinboundhandler<basemsg> {
Send heartbeat detection messages with write idle
@Override
public void usereventtriggered (Channelhandlercontext ctx, Object evt) throws Exception {
if (evt instanceof idlestateevent) {
Idlestateevent e = (idlestateevent) evt;
Switch (e.state ()) {
Case Writer_idle:
Pingmsg pingmsg=new pingmsg ();
Ctx.writeandflush (PINGMSG);
System.out.println ("Send ping to Server----------");
Break
Default
Break
}
}
}
@Override
protected void messagereceived (Channelhandlercontext channelhandlercontext, basemsg basemsg) throws Exception {
Msgtype Msgtype=basemsg.gettype ();
Switch (msgtype) {
Case login:{
Initiating a login to the server
Loginmsg loginmsg=new loginmsg ();
Loginmsg.setpassword ("Yao");
Loginmsg.setusername ("Robin");
Channelhandlercontext.writeandflush (LOGINMSG);
}break;
Case ping:{
System.out.println ("Receive ping from Server----------");
}break;
Case ask:{
Replyclientbody replyclientbody=new replyclientbody ("Client Info * * * *!!!");
Replymsg replymsg=new replymsg ();
Replymsg.setbody (Replyclientbody);
Channelhandlercontext.writeandflush (REPLYMSG);
}break;
Case reply:{
Replymsg replymsg= (replymsg) basemsg;
Replyserverbody replyserverbody= (Replyserverbody) replymsg.getbody ();
SYSTEM.OUT.PRINTLN ("Receive client msg:" +replyserverbody.getserverinfo ());
}
Default:break;
}
Referencecountutil.release (Msgtype);
}
}
Bootstrap
public class Nettyclientbootstrap {
private int port;
Private String host;
Private Socketchannel Socketchannel;
Private static final Eventexecutorgroup group = new Defaulteventexecutorgroup (20);
public nettyclientbootstrap (int port, String host) throws Interruptedexception {
This.port = port;
This.host = host;
Start ();
}
private void Start () throws Interruptedexception {
Eventloopgroup eventloopgroup=new Nioeventloopgroup ();
Bootstrap bootstrap=new Bootstrap ();
Bootstrap.channel (Niosocketchannel.class);
Bootstrap.option (channeloption.so_keepalive,true);
Bootstrap.group (Eventloopgroup);
Bootstrap.remoteaddress (Host,port);
Bootstrap.handler (New channelinitializer<socketchannel> () {
@Override
protected void Initchannel (Socketchannel socketchannel) throws Exception {
Socketchannel.pipeline (). AddLast (New Idlestatehandler (20,10,0));
Socketchannel.pipeline (). AddLast (New Objectencoder ());
Socketchannel.pipeline (). AddLast (New Objectdecoder (classresolvers.cachedisabled (null)));
Socketchannel.pipeline (). AddLast (New Nettyclienthandler ());
}
});
Channelfuture future =bootstrap.connect (host,port). sync ();
if (future.issuccess ()) {
Socketchannel = (Socketchannel) future.channel ();
SYSTEM.OUT.PRINTLN ("Connect server successfully---------");
}
}
public static void Main (String[]args) throws Interruptedexception {
Constants.setclientid ("001");
Nettyclientbootstrap bootstrap=new Nettyclientbootstrap (9999, "localhost");
Loginmsg loginmsg=new loginmsg ();
Loginmsg.setpassword ("Yao");
Loginmsg.setusername ("Robin");
Bootstrap.socketChannel.writeAndFlush (LOGINMSG);
while (true) {
TimeUnit.SECONDS.sleep (3);
Askmsg askmsg=new askmsg ();
Askparams askparams=new askparams ();
Askparams.setauth ("AuthToken");
Askmsg.setparams (Askparams);
Bootstrap.socketChannel.writeAndFlush (ASKMSG);
}
}
}
Netty for long-service client connection communication and heartbeat detection