1. 程式人生 > >netty框架學習之初始篇---多客戶端的實現補充部分

netty框架學習之初始篇---多客戶端的實現補充部分

上一篇文章中並沒有太過詳細的講解,而且經過今天一天的瞎搞,弄清了幾個問題,於是在這裡先補充一下,也有幾個地方對前面的文章做一下修正。

1.關於HelloServerInitializer(後面我改成了ServerInitalizer,畢竟專案不能叫做hello什麼吧。。。),在ServerInitializer中新增的解碼編碼器,前面文章說明有點問題,經過測試,並非一定要配置解碼編碼器才能通訊,如果採用byte型別的資料通訊時沒有問題的,只需要有自己的邏輯處理部分就行。但是另一問題也隨之而來,資料型別的轉換,太費勁了。。。還是建議加上netty自帶的解碼編碼器

另外,ServerSimpleHandler繼承的類可以是SimpleChannelInboundHandler,也可以是ChannelInboundHandlerAdapter

SimpleChannelInboundHandler繼承了ChannelInboundHandlerAdapter

至於其他的還沒測試,這兩個作為父類的時候子類所需要實現的方法並不相同,ChannelInboundHandlerAdapter要實現的關鍵方法是channelRead(ChannelHandlerContext ctx, Object msg),而不是channelRead0();而且注意channelRead方法的第二個引數是object類,這與SimpleChannelInboundHandler中的channelRead0(ChannelHandlerContext ctx, I msg)不同,channelRead0這個方法的第二個引數是一個I

,一開始也是一頭霧水,後來發現,public abstract class SimpleChannelInboundHandler<I>,也就是說,第二個引數與父類的引數型別保持一致,瞬間明悟了。一開始我侷限於SimpleChannelInboundHandler<String>,以為這裡只能是String,原來這裡可以使任何型別的資料,於是我改成了byte[],因為我需要處理byte資料,而且儘管這裡也可以寫成object,但是當你發現用bytebuf去處理資料型別轉換的時候是何等的費勁,還不如直接寫明這裡需要什麼資料,這樣在ServerInitializer中配置解碼編碼器的時候就要加上byte的解碼編碼器了。如下程式碼

package com.yl.netty.server;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.base64.Base64Decoder;
import io.netty.handler.codec.base64.Base64Encoder;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class ServerInitializer extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        // 以("\n")為結尾分割的 解碼器
//        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));

        // 字串解碼 和 編碼
//        pipeline.addLast("decoder", new StringDecoder());//解碼
//        pipeline.addLast("encoder", new StringEncoder());//編碼
        pipeline.addLast("bytesDecoder",new ByteArrayDecoder());
        pipeline.addLast("bytesEncoder",new ByteArrayEncoder());
//        pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
//        pipeline.addLast("Encoder",new ObjectEncoder());
        // 自己的邏輯Handler
        pipeline.addLast("handler", new ServerSimpleHandler());
    }
}
上面的程式碼可以看到,最後一個pipeline.addLast("handler", new ServerSimpleHandler());

這個是自己的邏輯handler。

public class ServerSimpleHandler extends SimpleChannelInboundHandler<byte[]> {
 /**
     * 覆蓋了 channelRead0() 事件處理方法。每當從服務端讀到客戶端寫入資訊時,將資訊轉發給其他客戶端的 Channel。
     */
	
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, byte[] msg) throws Exception {
    	Channel channel = ctx.channel();
    	hexStrMsg= StringUtil.bytesToHexString(msg);// 轉換為十六進位制字串
		strMsg = new String(msg, 0, msg.length);
		System.out.println("7/string-->>>" + hexStrMsg);
		System.out.println("8/string-->>>" + strMsg);
		//遍歷獲取到的裝置資訊
    	for (int i = 0; i < PublicHelp.deviceConfigList.size(); i++) {
    		//檢測裝置id是否存在如果存在,那麼將其新增到map中。
    		if(strMsg.equals(PublicHelp.deviceConfigList.get(i).getTxmkid())){
    			PublicHelp.channelMap.put(strMsg,channel);
    			txmkid = strMsg;
        	}
    		//檢測心跳資料,如果有與之匹配的心跳,更新map
    		if(strMsg.equals(PublicHelp.deviceConfigList.get(i).getTxmkxt())){
    			PublicHelp.channelMap.put(PublicHelp.deviceConfigList.get(i).getTxmkid(),channel);
        	}
		}
    	//遍歷裝置資訊,檢視獲得的訊息是否是php端傳送的有訂單訊息
		if (hexStrMsg.substring(0,6).equals(PublicHelp.deviceConfigList.get(0).getPhpmsg().substring(0, 3))) {
			ReceiveMsgDao.saveRecMsg("php--" + hexStrMsg);
			for (int i = 0; i < PublicHelp.deviceConfigList.size(); i++) {
				if(hexStrMsg.equals(PublicHelp.deviceConfigList.get(i).getPhpmsg())){
					ordermsg = ReceiveMsgDao.getTemp1OrderMsg();
					jqid = ordermsg[2];
				}
			}

		}
上面是部分程式碼並不完整,還沒整理完,而且也沒法貼上完整程式碼。只是想說明SimpleChannelInboundHandler<byte[]>問題。上面程式碼也能看出是如何區分不同的客戶端的。

而且我做了測試,每當一個client連線到server 的時候,ServerSimpleHandler 類就會重新new一個新的出來,也就是說,如果你在該類中定義了變數如果不是靜態的,那麼每次有新的client接入的時候,各個客戶端之間是無法通過這些變數進行資訊互動的,所以我將這些需要共享的資訊放在了一個公共類中,把類中的方法變數都靜態化,減少類的重複例項化。這種情況對於多客戶端通訊是很有利的,因為很多私有變數不需要特殊去宣告或者找一個單獨的類物件去維護,只需要在ServerSimpleHandler 類中直接定義就行,該類每次例項化一個物件的時候其中的區域性變數就自動私有化了。至少對於我現在要更改的專案來說是有利的。

愛程式設計,愛學習,愛挑戰。

程式猿就是我,我就是程式猿。