https://www.zhihu.com/question/24938934
https://blog.csdn.net/hfut_why/article/details/95330988
websocket :
1: WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
2: 服务器怎么讲消息推送给客户端
- 服务端主动推送到客户端是怎么一个过程呢
- 应用服务器如何确定每一个应用所在的设备
- 服务端把消息推到哪,客户端又不像服务器有一个固定的地址
- 现在的APP是如何使用消息推送的。
3 :那么服务端是怎么把一个消息推送给客户端的了?
目前服务端给客户端推送,普遍做法是客户端与服务端维持一个长连接,客户端定时向服务端发送心跳以维持这个长连接。当有新消息过来的时候,服务端查出该消息对应的TCP Channel的ID并找到对应的通道进行消息下发。
这只是最基本的通讯模型,在此之上,有衍生出针对消息的发布/订阅模型,客户端可以订阅某一个Topic,服务端根据Topic找到对应的Channel进行批量的消息下发。所有的客户端隐式的订阅的all这个opic,所以『类似中国移动给全网信号内所有手机发消息的模式』亦可以理解『广播消息』,即给all这个Topic发消息。
在此基础上,又要几个开源的协议来帮你定义这个事情,比较有名的如MQTT协议(刚好这几天我看到MQTT协议的中文翻译,分享给大家),Github上搜索MQTT可以找到对应的开源的协议实现项目,有兴趣可以自行搜索。
4:Android 现在主流的 消息推送机制
实际上,主流的移动平台都已经有系统级的推送产品,Android上有GCM,iOS上有APNS,WinPhone有MPNS。但因为某些你懂的原因,GCM在国内处于不可用状态,所以国内的移动应用采用另外一种做法---在后台运行一个Service,维持应用于服务端的TCP长连接,以达到实时消息送达的效果。
但是在移动端如何稳定的维持长连接是一件非常复杂的事情,前面说了,客户端通过定时发送心跳信号(Heartbeat)以维持与服务端的长连接,但是,如果心跳的频率太频繁,移动设备耗电增加,心跳间隔太久又可能使得连接被断开。并且普遍认为移动设备处于一个多变的网络环境中,WIFI,2G,4G切换,基站切换都会引起网络变动,在不同网络环境下的心跳频率,与网络变动的重连动作,都需要大量的数据统计分析总结出来。
这仅仅是客户端的难题,在如今移动应用动辄成百上千的用户量的情况下,如何维护如此多的长连接,如果应对大规模的消息下发以及后续针对下发消息的各种统计动作都是技术难点。
再者,现在应用一般都是全平台的,发送一条消息,应该同时发送给Android,iOS, WinPhone,Android端走自建的TCP长连接通道,iOS与WinPhone走自家的系统推送通道。那么意味着你服务端要维护这三套推送系统。
实践 :
1: 在moudel 中使用jar 包: java-websocket-1.4.0.jar (implementation "org.java-websocket:Java-WebSocket:1.4.0")
2: 在项目 gradle repositories中添加 mavenCentral()
3: 代码实例
/**
* 用于socket通信
*/
public class SocketService extends Service {
public static final String ACTION_MESSAGE = "com.maxvision.push.message";
public static final int RECONNECT = 1;
private WebSocketClient client;
private URI uri;
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == RECONNECT) {
client.reconnect();
}
}
};
@Override
public void onCreate() {
super.onCreate();
try {
uri = URI.create(IpManager.getInstance().getWebSocketUrl());
} catch (Exception e) {
Logger.i("初始化Web Socket 地址失败!");
}
if (uri != null) {
client = new WebSocketClient(uri) {
@Override
public void onOpen(ServerHandshake handshakedata) {
Logger.i("onOpen");
}
@Override
public void onMessage(String message) {
Logger.i("消息%s:", message);
Intent intent = new Intent(ACTION_MESSAGE);
intent.putExtra("message", message);
sendBroadcast(intent);
}
@Override
public void onClose(int code, String reason, boolean remote) {
Logger.i(reason);
handler.sendEmptyMessageDelayed(RECONNECT, 10000);
}
@Override
public void onError(Exception ex) {
Logger.e("error:%s", ex.getMessage());
}
};
client.connect();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
if (client != null) {
client.close();
}
}