The Netty realizes long connection communication between server and client, and heartbeat detection.
Basic idea: The Netty server saves all connected client Socketchannel through a map, and the ID of the client is the key of the map. Every time the server side to send a message to a client, just according to ClientID out the corresponding Socketchannel, to write messages inside. Heartbeat detection through the Idleevent event, timed to the service side of the ping message, detection socketchannel whether the end of the break.
Environmental JDK1.8 and Netty5
The following is a detailed code implementation and introduction:
1 The 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 sequence, serialversionuid must have, no person in the Netty message serialization deserialization will be problematic, receive no message ... Public
abstract class Basemsg implements Serializable {
private static final long Serialversionuid = 1l;
private msgtype type;
Must be unique, no one will appear channel call Chaos
private String clientId;
Initialize 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 parameter
//must implement serialization interface 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 like public class Replybody implements Serializable {private static final long serialversionuid = 1L;} p
Ublic 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; } publicvoid SetServerInfo (String serverinfo) {this.serverinfo = ServerInfo; }
}
2 server-side: primarily contains map,channelhandler implementations and bootstrap for Socketchannel references.
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 Channel Inactive (Channelhandlercontext ctx) throws Exception {//channel invalid, remove Nettychannelmap.remove from map (socketc
Hannel) Ctx.channel ()); @Override protected void messagereceived (Channelhandlercontext channelhandlercontext, basemsg basemsg) throws Ex
ception {if (MsgType.LOGIN.equals (Basemsg.gettype ())) {loginmsg loginmsg= (loginmsg) basemsg; if ("Robin". Equals (Loginmsg.getusername ()) && "Yao". Equals (Loginmsg.getpassword ()) {//Login succeeded, put Cha
Nnel is saved to the server's map Nettychannelmap.add (Loginmsg.getclientid (), (Socketchannel) Channelhandlercontext.channel ());
SYSTEM.OUT.PRINTLN ("Client" +loginmsg.getclientid () + "login Successful"); }else{if (Nettychannelmap.get (Basemsg.getclientid ()) ==null) {//description is not logged in, or the connection is broken, the server
Client initiates login request, lets client log in 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 ("s
ERVER 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 the message is sent immediately, do not wait for a certain amount of data to send out to Bootstrap.option (Channeloption.tcp_nodelay, true);
Maintain long connection state bootstrap.childoption (channeloption.so_keepalive, true); Bootstrap.childhandler (New channelinitializer<socketchannel> () {@Override protected void in Itchannel (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=ne
W 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> {//Use write idle to send heartbeat detection messages @Override public void usereventtriggered (Channelhandlercontext ctx, Object evt) throws Exception {if (evt instanceof idles
tateevent) {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 Ba
Semsg) throws Exception {Msgtype msgtype=basemsg.gettype ();
Switch (msgtype) {case login:{//Login to 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 Initcha Nnel (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 successful---------");
} 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); }
}
}
Specific examples and corresponding pom.xml see https://github.com/WangErXiao/ServerClient
Forwarding please indicate the source: http://my.oschina.net/robinyao/blog/399060
Summarize:
When Java implements TCP serialization and deserialization, it is best to use a JSON string to pass data. This ensures that the client deserialization succeeds. If both the client and the server are using Java implementations, it is possible to use object serialization and deserialization. If the client is implemented using a non-Java language, the Java object serialization and deserialization cannot be used. It is best to use strings to pass data. It is recommended that you use a JSON string.