之前自己寫了socket建立的連接系馆,但是涉及到一些需要完善的地方送漠,表示自己有點(diǎn)懶。然后就在網(wǎng)上找了一個(gè)socket框架由蘑, 拿來直接用闽寡。
閱讀測試后代兵,個(gè)人認(rèn)為NettyClient 主要分為幾個(gè)部分:
1,異步端口連接 Bootstrap
2爷狈,NettyClient的解碼方式等 pipeline
3植影,配套的HandlerAdapter : ChannelInboundHandlerAdapter等。
4涎永,計(jì)算斷開后重新調(diào)用連接思币。
- 使用的時(shí)候下載個(gè)jar包就可以了
https://download.csdn.net/download/binbinxiaoz/10488657
提供一下jar包下載,不知道怎么設(shè)置免費(fèi)下載羡微,沒分的可以網(wǎng)上自行找資源谷饿,資源一堆的說! 我就直接上代碼了妈倔,上的代碼都是簡單的封裝了一下的博投。使用的時(shí)候直接調(diào)用指定的方法就可以。 (這字一放大盯蝴,變色的字就沒了毅哗,真是不知道怎么用!)
- 我先把定義的接口寫了捧挺,下面的代碼里面有用到虑绵。
public interface NettyListener {
public final static byte STATUS_CONNECT_SUCCESS = 1;//連接成功
public final static byte STATUS_CONNECT_CLOSED = 0;//關(guān)閉連接
public final static byte STATUS_CONNECT_ERROR = 0;//連接失敗
/**
* 當(dāng)接收到系統(tǒng)消息
*/
void onMessageResponse(Object msg);
/**
* 當(dāng)連接狀態(tài)發(fā)生變化時(shí)調(diào)用
*/
public void onServiceStatusConnectChanged(int statusCode);
}
- NettyClient 類的書寫
public class NettyClient {
private static final String TAG = "NettyClient";
private EventLoopGroup group;//Bootstrap參數(shù)
private NettyListener listener;//寫的接口用來接收服務(wù)端返回的值
private Channel channel;//通過對象發(fā)送數(shù)據(jù)到服務(wù)端
private boolean isConnect = false;//判斷是否連接了
private static int reconnectNum = Integer.MAX_VALUE;//定義的重連到時(shí)候用
private boolean isNeedReconnect = true;//是否需要重連
private boolean isConnecting = false;//是否正在連接
private long reconnectIntervalTime = 5000;//重連的時(shí)間
public String host;//ip
public int tcp_port;//端口
/*
構(gòu)造 傳入 ip和端口
*/
public NettyClient(String host, int tcp_port) {
this.host = host;
this.tcp_port = tcp_port;
}
/*
連接方法
*/
public void connect() {
if (isConnecting) {
return;
}
//起個(gè)線程
Thread clientThread = new Thread("client-Netty") {
@Override
public void run() {
super.run();
isNeedReconnect = true;
reconnectNum = Integer.MAX_VALUE;
connectServer();
}
};
clientThread.start();
}
//連接時(shí)的具體參數(shù)設(shè)置
private void connectServer() {
synchronized (NettyClient.this) {
ChannelFuture channelFuture = null;//連接管理對象
if (!isConnect) {
isConnecting = true;
group = new NioEventLoopGroup();//設(shè)置的連接group
Bootstrap bootstrap = new Bootstrap().group(group)//設(shè)置的一系列連接參數(shù)操作等
.option(ChannelOption.TCP_NODELAY, true)//屏蔽Nagle算法試圖
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() { // 5
@Override
public void initChannel(SocketChannel ch) throws Exception {
// ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));//服務(wù)端最后以"\n"作為結(jié)束標(biāo)識
// ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));//解碼
// ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));//解碼
ch.pipeline().addLast(new NettyClientHandler(listener));//需要的handlerAdapter
}
});
try {
//連接監(jiān)聽
channelFuture = bootstrap.connect(host, tcp_port).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()) {
Log.e(TAG, "連接成功");
isConnect = true;
channel = channelFuture.channel();
} else {
Log.e(TAG, "連接失敗");
isConnect = false;
}
isConnecting = false;
}
}).sync();
// 等待連接關(guān)閉
channelFuture.channel().closeFuture().sync();
Log.e(TAG, " 斷開連接");
} catch (Exception e) {
e.printStackTrace();
} finally {
isConnect = false;
listener.onServiceStatusConnectChanged(NettyListener.STATUS_CONNECT_CLOSED);//STATUS_CONNECT_CLOSED 這我自己定義的 接口標(biāo)識
if (null != channelFuture) {
if (channelFuture.channel() != null && channelFuture.channel().isOpen()) {
channelFuture.channel().close();
}
}
group.shutdownGracefully();
reconnect();//重新連接
}
}
}
}
//斷開連接
public void disconnect() {
Log.e(TAG, "disconnect");
isNeedReconnect = false;
group.shutdownGracefully();
}
//重新連接
public void reconnect() {
Log.e(TAG, "reconnect");
if (isNeedReconnect && reconnectNum > 0 && !isConnect) {
reconnectNum--;
SystemClock.sleep(reconnectIntervalTime);
if (isNeedReconnect && reconnectNum > 0 && !isConnect) {
Log.e(TAG, "重新連接");
connectServer();
}
}
}
//發(fā)送消息到服務(wù)端。 Bootstrap設(shè)置的時(shí)候我沒有設(shè)置解碼松忍,這邊才轉(zhuǎn)的
public boolean sendMsgToServer(String data, ChannelFutureListener listener) {
boolean flag = channel != null && isConnect;
if (flag) {
ByteBuf byteBuf = Unpooled.copiedBuffer(data + System.getProperty("line.separator"), //2
CharsetUtil.UTF_8);
channel.writeAndFlush(byteBuf).addListener(listener);
}
return flag;
}
//重連時(shí)間
public void setReconnectNum(int reconnectNum) {
this.reconnectNum = reconnectNum;
}
public void setReconnectIntervalTime(long reconnectIntervalTime) {
this.reconnectIntervalTime = reconnectIntervalTime;
}
//現(xiàn)在連接的狀態(tài)
public boolean getConnectStatus() {
return isConnect;
}
public boolean isConnecting() {
return isConnecting;
}
public void setConnectStatus(boolean status) {
this.isConnect = status;
}
public void setListener(NettyListener listener) {
this.listener = listener;
}
}
好累,感覺我要睡著了鸣峭。
我要原諒我太菜宏所,沒有時(shí)間休息,公司開展的新項(xiàng)目的技術(shù)點(diǎn)不會(huì)摊溶,哈哈爬骤,沒辦法。莫换。霞玄。太菜,抓把勁攻克拉岁!
- netty socket 配套的handlerAdapter,直接上代碼直接上
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
private static final String TAG = "NettyClientHandler";
private NettyListener listener;
public NettyClientHandler(NettyListener listener) {
this.listener = listener;
}
//每次給服務(wù)器發(fā)送的東西坷剧, 讓服務(wù)器知道我們在連接中哎
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.WRITER_IDLE) {
ctx.channel().writeAndFlush("Heartbeat" + System.getProperty("line.separator"));
}
}
}
/**
* 連接成功
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Log.e(TAG, "channelActive");
super.channelActive(ctx);
listener.onServiceStatusConnectChanged(NettyListener.STATUS_CONNECT_SUCCESS);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
Log.e(TAG, "channelInactive");
}
//接收消息的地方, 接口調(diào)用返回到activity了
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("客戶端開始讀取服務(wù)端過來的信息");
listener.onMessageResponse(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 當(dāng)引發(fā)異常時(shí)關(guān)閉連接喊暖。
Log.e(TAG, "exceptionCaught");
listener.onServiceStatusConnectChanged(NettyListener.STATUS_CONNECT_ERROR);
cause.printStackTrace();
ctx.close();
}
}
我覺得我注釋寫的還挺多的惫企,哈哈,
對了,還有個(gè)定義的host和port類狞尔。雖然就幾行丛版,但我還是決定寫上
public class Const { //百度找的markdown語法框框,表示有點(diǎn)丑
public static final String HOST = "192.168.0.46";
public static final int TCP_PORT = 5212;
public static final int LIVE_PORT = 5500;
}
-
最重要的來襲了偏序, 使用页畦!
-
布局沒什么東西, 就是個(gè)Button發(fā)送消息的研儒,連接我寫成進(jìn)入app后默認(rèn)連接了豫缨,失敗和成功會(huì)有提示的昂, 失敗了會(huì)一直重連殉摔。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="發(fā)送消息" />
</LinearLayout>
其他核心的使用代碼:V莞臁!R菰隆!
public class MainActivity extends BaseActivitiy implements NettyListener {
private Button btn_send;//發(fā)送
private NettyClient nettyClient;//socket操作連接對象
private final String TAG = "MainActivity.class";
@Override
protected int getContentView() {//這我自己的基類抽象的
return R.layout.activity_main;
}
@Override
protected void initView() {//這我自己的基類抽象的
initSocketTcp();//默認(rèn)自動(dòng)連接socket
btn_send = findViewById(R.id.btn_send);//發(fā)送
btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!nettyClient.getConnectStatus()) {//獲取連接狀態(tài)遍膜,必須連接才能點(diǎn)碗硬。
Toast.makeText(MainActivity.this, "先連接", Toast.LENGTH_SHORT).show();
} else {
JSONObject jsonObject = new JSONObject();//傳個(gè)jsonobject給服務(wù)器
long l = System.currentTimeMillis();
try {
jsonObject.put("msgType", "infomation");
jsonObject.put("msgValue", "status");
jsonObject.put("msgTime", l + "");
} catch (JSONException e) {
e.printStackTrace();
}
/*
調(diào)用的發(fā)送。
*/
nettyClient.sendMsgToServer(jsonObject.toString(), new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()) { //4
Log.d(TAG, "Write auth successful");
} else {
Log.d(TAG, "Write auth error");
}
}
});
}
}
});
}
/*
socket 端口號以及開始連接瓢颅,配置接口監(jiān)聽
*/
private void initSocketTcp() {
nettyClient = new NettyClient(Const.HOST, Const.TCP_PORT);
if (!nettyClient.getConnectStatus()) {
nettyClient.setListener(MainActivity.this);
nettyClient.connect();
} else {
nettyClient.disconnect();
}
}
/*
回調(diào)客戶端接收的信息 解析 數(shù)據(jù)流恩尾, 轉(zhuǎn)換為string
*/
@Override
public void onMessageResponse(final Object msg) {
ByteBuf result = (ByteBuf) msg;
byte[] result1 = new byte[result.readableBytes()];
result.readBytes(result1);
result.release();
final String ss = new String(result1);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "接收成功" + ss, Toast.LENGTH_SHORT).show();
}
});
}
//連接狀態(tài)變化的時(shí)候 會(huì)走這
@Override
public void onServiceStatusConnectChanged(final int statusCode) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (statusCode == NettyListener.STATUS_CONNECT_SUCCESS) {
Log.e(TAG, "STATUS_CONNECT_SUCCESS:");
if (nettyClient.getConnectStatus()) {
Toast.makeText(MainActivity.this, "連接成功", Toast.LENGTH_SHORT).show();
}
} else {
Log.e(TAG, "onServiceStatusConnectChanged:" + statusCode);
if (!nettyClient.getConnectStatus()) {
Toast.makeText(MainActivity.this, "網(wǎng)路不好,正在重連", Toast.LENGTH_SHORT).show();
}
}
}
});
}
}
基本就這些了挽懦,我覺得我寫非常清楚翰意, 按照步驟的話直接就可以實(shí)現(xiàn),
如果想知道到底能不能使用信柿,能不能行冀偶。
可以使用工具..等等,肯定是能用的渔嚷,好嗎进鸠??形病?客年!
哈哈,模擬服務(wù)端的話漠吻,可以下載個(gè) 量瓜,windows還是mac都有,網(wǎng)上找就有途乃,我沒有提供鏈接绍傲。
sokit
好了好了,可算寫完了欺劳,技術(shù)太菜望見諒唧取,我是一名又懶又菜的猿铅鲤。