Mina, Netty, Twisted learn Together (iv): Customize your own protocol

Source: Internet
Author: User
Tags unpack

In the previous blog post, we describe some of the scenarios for message splitting, as well as the related APIs that Mina, Netty, and twisted provide for these scenarios. such as Mina's textlinecodecfactory, Prefixedstringcodecfactory,netty's Linebasedframedecoder, Lengthfieldbasedframedecoder,twisted's Lineonlyreceiver and Int32stringreceiver.

In addition to these scenarios, there are many other options, and of course, you can define them yourself. Here, we customize a solution of our own, and use Mina, Netty, twisted, respectively, to implement this kind of message parsing and assembly, that is, encoding and decoding.

Previous blog post describes a message tessellation scheme that specifies the number of body bytes by a header with a fixed number of bytes, where the header portion is a regular large-endian (Big-endian) 4-byte integer. In this article, the scheme is slightly modified to change the header of a fixed number of bytes to a 4-byte integer of the Small byte order (Little-endian) .

The regular large-scale sequence indicates a number, and the high-byte storage of the number of low-level, more consistent with human habits. The small byte order and the large byte order are reversed, and the high byte bits are used to hold the digits.


In Python, the struct module supports size byte-order pack and unpack, in Java you can use the following two methods to implement a small byte-order byte array to int and int to a small byte sequence byte array, the following Java program will use the two methods:

public class Littleendian {/** * converts int to 4 bytes of small byte-order byte array */public static byte[] Tolittleendian (int i) {byte[] bytes = new byte[ 4];bytes[0] = (byte) i;bytes[1] = (byte) (I >>> 8); bytes[2] = (byte) (I >>> +); bytes[3] = (byte) (i ;>>); return bytes;} /** * Converts a 4 byte array of small endian bytes into an int */public static int getlittleendianint (byte[] bytes) {int b0 = bytes[0] & 0xFF;        int B1 = bytes[1] & 0xFF;        int b2 = bytes[2] & 0xFF;        int b3 = bytes[3] & 0xFF;        return B0 + (B1 << 8) + (B2 << +) + (B3 << 24);}}

Whether it is Mina, Netty or twisted, the encoding, decoding, and coding of the message should be separated from the business logic code, which facilitates the development, reuse, and maintenance of the code. Similar in Mina and Netty, encoding, decoding requires inheritance to implement the corresponding encoder, Decoder, and in twisted is inherited protocol implementation of encoding decoding. Although they are implemented in different ways, they function the same way:

1, the message according to certain rules, such as fixed-length message, by line, by delimiter, fixed length of the header to specify the body length, etc.;

2, the appropriate message from the bytecode to the type you want, such as Mina Iobuffer into a string, so messagereceived received message parameter is the string type;

3, write the time can pass in the parameters of the custom type, the encoder to complete the encoding.

The following is the Mina, Netty, twisted implementation of the 4-byte small byte-order int to specify the body length.

MINA:

Matching and decoding the received messages in the Mina generally defines a decoder class that inherits from the abstract class Cumulativeprotocoldecoder and implements the Dodecode method:

public class Myminadecoder extends Cumulativeprotocoldecoder {@Overrideprotected Boolean dodecode (iosession session, Iobuffer in, protocoldecoderoutput out) throws Exception {//If the header portion (4 bytes) is not received, return directly to Falseif (In.remaining () < 4) { return false;} else {//Mark start position, if a message does not transmit complete then return to this position in.mark (); byte[] bytes = new Byte[4];in.get (bytes);//Read 4 bytes of headerint bodylength = Li Ttleendian.getlittleendianint (bytes); Press the small byte sequence to int//if the body is not received intact, return directly Falseif (in.remaining () < Bodylength) {in.reset ();//Iobuffer Position return to the original marked place return false;} else {byte[] bodybytes = new Byte[bodylength];in.get (bodybytes); String BODY = new String (bodybytes, "UTF-8"); Out.write (body); Parse out a message return true;}}}

In addition, when session.write to encode data, it is necessary to define an encoder that inherits from the abstract class Protocolencoderadapter and implements the Encode method:

public class Myminaencoder extends Protocolencoderadapter {@Overridepublic void encode (iosession session, Object message , protocolencoderoutput out) throws Exception {string msg = (string) message;byte[] bytes = msg.getbytes ("UTF-8"); int lengt h = bytes.length;byte[] Header = Littleendian.tolittleendian (length); Iobuffer buffer = iobuffer.allocate (length + 4) by small byte order; buffer.put (header); Headerbuffer.put (bytes); Bodybuffer.flip (); out.write (buffer);}}

Add the appropriate encoder and decoder to the server when it is started:

public class TCPServer {public static void main (string[] args) throws IOException {Ioacceptor acceptor = new Niosocketacce Ptor ();//Specify Codec Acceptor.getfilterchain (). AddLast ("Codec", new Protocolcodecfilter (New Myminaencoder (), New Myminadecoder ())); Acceptor.sethandler (new Tcpserverhandle ()); Acceptor.bind (new inetsocketaddress (8080));}}

Here is the code for the business logic:

public class Tcpserverhandle extends Iohandleradapter {@Overridepublic void Exceptioncaught (iosession session, Throwable cause) throws Exception {Cause.printstacktrace ();} New data received @overridepublic void Messagereceived (iosession session, Object message) throws Exception {// Myminadecoder the received data is converted from Iobuffer to stringstring msg = (String) message; System.out.println ("messagereceived:" + msg);//Myminaencoder adds a small endian header to the write string and converts it to bytecode session.write ("received");}

Netty:

Similar to the Mina decoder in Netty, the decoder inherits the abstract class Bytetomessagedecoder, implementing the Decode method:

public class Mynettydecoder extends Bytetomessagedecoder {@Overrideprotected void decode (Channelhandlercontext ctx, Bytebuf in, list<object> out) throws Exception {//If the header portion (4 bytes) is not received, exit the method directly if (In.readablebytes () >= 4) {//superscript The starting position, if a message is not transferred, return to this position in.markreaderindex (); byte[] bytes = new byte[4];in.readbytes (bytes); Reads 4 bytes of Headerint bodylength = littleendian.getlittleendianint (bytes); Header by small byte order int//if body does not receive full if (In.readablebytes () < Bodylength) {in.resetreaderindex ();//Bytebuf Back to mark position} else {byte[] bodybytes = new Byte[bodylength];in.readbytes (bodybytes); String BODY = new String (bodybytes, "UTF-8"); Out.add (body); Parse out a Message}}}}

Here is the encoder, inheriting from the abstract class Messagetobyteencoder, implementing the Encode method:

public class Mynettyencoder extends messagetobyteencoder<string> {@Overrideprotected void encode ( Channelhandlercontext ctx, String msg, bytebuf out) throws Exception {byte[] bytes = msg.getbytes ("UTF-8"); int length = Byt Es.length;byte[] Header = Littleendian.tolittleendian (length); int by small byte order to byte array out.writebytes (header); Write Headerout.writebytes (bytes); Write Body}}

Add the appropriate encoder and decoder:

public class TCPServer {public static void main (string[] args) throws Interruptedexception {Eventloopgroup Bossgroup = new Nioeventloopgroup (); Eventloopgroup Workergroup = new Nioeventloopgroup (); try {serverbootstrap b = new Serverbootstrap (); B.group (BossGroup, Workergroup). Channel (Nioserversocketchannel.class). Childhandler (New channelinitializer<socketchannel> () {@ overridepublic void Initchannel (Socketchannel ch) throws Exception {Channelpipeline pipeline = Ch.pipeline ();// Add your own encoder and decoderpipeline.addlast (new Mynettydecoder ());p Ipeline.addlast (New Mynettyencoder ()); Pipeline.addlast (New Tcpserverhandler ());}); Channelfuture f = b.bind (8080). sync (); F.channel (). Closefuture (). sync (); finally {workergroup.shutdowngracefully (); bossgroup.shutdowngracefully ();}}}

Business logic Processing:

Class Tcpserverhandler extends Channelinboundhandleradapter {//Receive new data @overridepublic void Channelread ( Channelhandlercontext ctx, Object msg) {//Mynettydecoder the received data is converted from BYTEBUF to stringstring message = (String) msg; System.out.println ("Channelread:" + message);//Mynettyencoder adds a small byte-order header to the write string and converts it to a byte-code ctx.writeandflush (" Received ");} @Overridepublic void Exceptioncaught (Channelhandlercontext ctx, throwable cause) {cause.printstacktrace (); Ctx.close ( );}}

Twisted:

Twisted implementation is not the same as Mina, Netty, its implementation is relatively primitive, but the more primitive is closer to the underlying principle.

First, you define a MyProtocol class that inherits from protocol, which acts like a Mina, Netty encoding, and decoder. Classes that handle business logic tcpserverhandle inherit MyProtocol, overriding or invoking some of the methods provided by MyProtocol.

#-*-coding:utf-8–*-from struct import pack, Unpackfrom twisted.internet.protocol import Factoryfrom TWISTED.INTERNET.P Rotocol Import protocolfrom twisted.internet import reactor# encoding, decoder class MyProtocol (Protocol): # for temporarily storing received data _b        Uffer = B "" Def datareceived (Self, data): # Last unhandled data plus the data received this time Self._buffer = Self._buffer +                # Keep looping until the new message does not receive full while True: # If the header receives the full if Len (Self._buffer) >= 4: # turn int length by small byte order = unpack ("<i", Self._buffer[0:4]) # If the body receives the full if Le                    N (Self._buffer) >= 4 + Length: # body Part packet = self._buffer[4:4 + length] # New message received and decoded complete, call stringreceived self.stringreceived (packet) # Remove _buff            The message part that has been processed in er self._buffer = self._buffer[4 + length:] else:break;           Else     Break def stringreceived (self, data): Raise Notimplementederror def sendstring (Self, string): Self.transpor T.write (Pack ("<i", Len (String)) + string) # Logic code class Tcpserverhandle (MyProtocol): # Implement the stringreceived provided by MyProtocol instead of datareceived, otherwise you cannot decode Def stringreceived (self, data): # data is decoded after MyProtocol The data print ' stringreceived: ' + ' + data # calls sendstring instead of self.transport.write, otherwise it cannot be encoded SELF.SENDST Ring ("received") Factory = Factory () Factory.protocol = tcpserverhandlereactor.listentcp (8080, Factory) Reactor.run ()

Here is a client-side test program written in Java:

public class TcpClient {public static void main (string[] args) throws IOException {socket socket = Null;outputstream out = Null;inputstream in = null;try {socket = new socket (' localhost ', 8080); out = Socket.getoutputstream (); in = Socket.getinpu TStream ();//request Server String data = "I am client"; byte[] outputbytes = data.getbytes ("UTF-8"); Out.write ( Littleendian.tolittleendian (outputbytes.length)); Write Headerout.write (outputbytes); Write Bodyout.flush ();//Get response byte[] inputbytes = new Byte[1024];int length = In.read (inputbytes); if (length >= 4) {in T bodylength = Littleendian.getlittleendianint (inputbytes); if (length >= 4 + bodylength) {byte[] bodybytes = Arrays.cop Yofrange (Inputbytes, 4, 4 + bodylength); System.out.println ("Header:" + bodylength); System.out.println ("Body:" + new String (bodybytes, "UTf-8"));}}} Finally {//close connection in.close (); Out.close (); Socket.close ();}}}

Use the client to test the above three TCP servers separately:

Mina Server output results:

Messagereceived: I am the client

Netty Server Output results:

Channelread: I am the client

Twisted Server Output results:

Channelread: I am the client

The client tests the output of three servers as follows:

Header:6
Body: Received

Since a Chinese character generally accounts for 3 bytes, the header of two Chinese characters corresponds to 6.



Fork Brother reproduced please indicate the source: http://blog.csdn.net/xiao__gui/article/details/38819103



Mina, Netty, Twisted learn Together (iv): Customize your own protocol

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.