作为开发一年的工程师来说这是我最顺利的一次开发。
为何呢?
因为当时开发已经有段时间了,所以思路很清晰。给了我一个web socket,大概也就用了一天就写完了。虽然说写的并不是那么完美。(对web socket的封装神的没做直接写)
首先呢我先说一下。socket技术有长连接和长轮询两种写法(比我牛逼的别来打我脸,但是欢迎告诉我哪里不对)。
长连接个人通俗的讲就是和服务器的链接不断开,并且能正常的传输信息。
长轮询就是隔一段时间断开与服务器的链接,再重新连接。(不是闲的啊,你想先几十万几百万人同时在线的情况,那服务器的压力就);
我在项目中使用的就是长连接技术。
怎么写的呢请看:
package com.chihuobao.app.base.client;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Timer;
import java.util.TimerTask;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.framing.Framedata;
import org.java_websocket.handshake.ServerHandshake;
import com.alibaba.fastjson.JSONObject;
import com.chihuobao.app.base.log.FrameLog;
import com.chihuobao.app.base.utils.PrefsConfig;
import com.chihuobao.app.itstack.netty.common.ModelAction;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
public class MessageService extends Service {
private String shopid = PrefsConfig.ShopId;// 获取本地Shopid
private MessageClient messageClient;
public int timer = 0, ts = 0;
private boolean isLoop = true;
private boolean isCallHead = false;//作用 心跳处理
private Timer timerl;//作用 重连线程
private TimerTask timerTask;
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message arg0) {
timerl = new Timer(true);
switch (arg0.what) {
case 91:
timerTask = new TimerTask() {
@Override
public void run() {
Message msg = new Message();
msg.what = 31;
FrameLog.e("计时器", timer + "");
mHandler.sendMessage(msg);
}
};
timerl.schedule(timerTask, 0, 8000);// 0s后执行 每3秒执行一次
break;
case 31:
if (messageClient != null) {
if (messageClient.isOpen()) {
messageClient.close();//主动关闭上一次长链接
}
messageClient = null;
}
System.out.println("开始重新连接");
getExampleClient();
break;
default:
break;
}
return false;
}
});
public int onStartCommand(Intent intent, int flags, int startId) {
getExampleClient();//初始化长连接
looperHead();//开启心跳线程
return START_STICKY;
};
//实例化长链接客户端
private void getExampleClient() {
new Thread() {
public void run() {
System.out.println("开始连接");
try {
// messageClient = new MessageClient(new URI(
// "ws://192.168.99.113:9876"));
messageClient.connectBlocking();//链接服务器长链接
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if (messageClient != null) {
if (messageClient.isOpen()) {
messageClient.close();//主动关闭上一次长链接
}
messageClient = null;
}
// onCreate();
}
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
// 写的不太好的心跳包
public void looperHead() {
Thread headThread = new Thread() {
@SuppressWarnings("static-access")
public void run() {
while (true) {
if (isCallHead == true && messageClient != null
&& !messageClient.isClosed()
&& !messageClient.isFlushAndClose()) {
if (timer == 15) {
System.out.println("心跳包");
if (messageClient.isOpen()) {
messageClient.send("&HEAT0053\n\n");//发送心跳包
}
}
if (timer > 25) {
System.out.println("断开连接");//主动断开
if (messageClient != null) {
if (messageClient.isOpen()) {
System.out.println("断开连接");
messageClient.close();//执行关闭方法
}
}
putService();
}
try {
Thread.sleep(1000);//1S增加一次计数器
timer = timer + 1;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
};
headThread.start();
}
//接受扫码的服务器推送
class MessageClient extends WebSocketClient {
private String mkey = null;
public MessageClient(URI serverUri, Draft draft) {
super(serverUri, draft);
}
public MessageClient(URI serverURI) {
super(serverURI);
}
@Override
public void onOpen(ServerHandshake handshakedata) {
System.out.println("连接完成");
timer = 0;// 初始化心跳包计数器
isCallHead = true;// 通知关闭方法可以放松消息
isLoop = true;// 开启心跳循环
if (timerTask != null) {
System.out.println("开始停止计时");
timerTask.cancel();// 关闭重连计数器执行线程
timerl.cancel();// 关闭重连计数器
}
}
@Override
public void onMessage(String message) {//接收到服务器的推送消息
FrameLog.e("接受到服务器的信息1", message + "\n" + message.length());
String temp = null;
if (message != null) {
if (message.length() == 4) {
temp = message.substring(0, 4);
FrameLog.e("接受到服务器的信息", "temp=" + "心跳包" + temp);
if (temp.equals("HEAT")) {
timer = 0;
FrameLog.e("接受到服务器的信息", "temp=" + "心跳包");
}
}
}
message = null;
}
}
@Override
public void onFragment(Framedata fragment) {
System.out.println("received fragment: "
+ new String(fragment.getPayloadData().array()));
}
@Override
public void onClose(int code, String reason, boolean remote) {//检测到关闭链接的方法
// The codecodes are documented in class
// org.java_websocket.framing.CloseFrame
System.out.println("Connection closed by 执行关闭方法");
if (isLoop == true) {
putService();// 开启重连线程
}
}
@Override
public void onError(Exception ex) {
ex.printStackTrace();
}
private boolean getMsg(String json) {
JSONObject jsonObject = JSONObject.parseObject(json);
String result = jsonObject.getString("result");
System.out.println("解析数据错误提示**********************" + json);
if (result.equals("success")) {
return true;
} else {
return false;
}
}
}
private void putService() {//发送重连信息
Message message = new Message();
message.what = 91;// 重连消息
isCallHead = false;// 关闭心跳包
isLoop = false;// 关闭重连复发
if (mHandler != null) {
mHandler.sendMessage(message);
}
}
}
大家发现没我喜欢用Handler而不是timer因为我习惯了这么写了,当然websocket有它自己的timer大家可以用哪个。
这里说几个地方。
没用网络开启,断开连接,网址不对,等等都会触发web socket的onClose方法,其实web socket也是对socket的一种封装,实质上也是socket。
心跳包要确定web socket在isOpen()的情况下close(),为什么不想connectBLOCKING方法一样用closeblocking()去关闭呢。你可以试试有时候关闭不上,特别是在断网的情况下。我猜可能是web socket封装了某些东西给close blocking导致有时候不能正常关闭,我会继续去查找资料确认下下。
这里说web socket的几个思想 第一是确认和服务器是否一直链接的心跳包。第二是短线之后重新链接。当别的activity开启这个service的时候大家会发现activity不会被回收,除非你清除进程。但是service会一直执行,所以要在activity的onDestroy()内把servicestop掉。