这货是什么,这货能做消息推送,和其他手机端push一样有自己的协议体和算法讲究
首先看下例子使用:
public void testPubSub() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
IMqttClient client = null;
try {
String topicStr = "topic" + "_02";
String clientId = methodName;
client = clientFactory.createMqttClient(serverURI, clientId);
log.info("Assigning callback...");
MessageListener listener = new MessageListener();
client.setCallback(listener);
log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + clientId);
client.connect();
log.info("Subscribing to..." + topicStr);
client.subscribe(topicStr);
log.info("Publishing to..." + topicStr);
MqttTopic topic = client.getTopic(topicStr);
MqttMessage message = new MqttMessage("foo".getBytes());
topic.publish(message);
log.info("Checking msg");
MqttMessage msg = listener.getNextMessage();
Assert.assertNotNull(msg);
Assert.assertEquals("foo", msg.toString());
log.info("getTopic name");
String topicName = topic.getName();
log.info("topicName = " + topicName);
Assert.assertEquals(topicName, topicStr);
log.info("Disconnecting...");
client.disconnect();
}
finally {
if (client != null) {
log.info("Close...");
client.close();
}
}
}
然后看张图:
先介绍外部接口IMqttClient和MqttMessage消息质量,你所有的请求和接收都是从这个接口实现的。
Mqtt协议起初是IBM提出的,关于clinet端,有很多包,Eclipse只是其中之一
public interface IMqttClient { //extends IMqttAsyncClient {
public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException;
public IMqttToken connectWithResult(MqttConnectOptions options) throws MqttSecurityException, MqttException;
public void disconnect(long quiesceTimeout) throws MqttException;
// Mqtt基于Tcp协议,在关闭时候,有4次握手机会,为了最后一次不会超时,造成server资源泄露,强制关闭
public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException;
public void subscribe(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners) throws MqttException; // 订阅主题,有自己的长度和规则限制
public void unsubscribe(String topicFilter) throws MqttException;
public void publish(String topic, byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException; // 发布消息,有QOS3种质量
public MqttTopic getTopic(String topic);
public boolean isConnected();
public String getClientId();
public String getServerURI();
public IMqttDeliveryToken[] getPendingDeliveryTokens();// 每一个Message都有个Token,用于追踪,cleanSession会清空前面状态,推荐使用false
public void setManualAcks(boolean manualAcks); // 为true表示收到ack需要自己手动通知messageArrived
public void messageArrivedComplete(int messageId, int qos) throws MqttException; // 接收到所有信息回掉的完成
public void close() throws MqttException;
}
质量有3种
Qos 0 : 这个消息最多发送一次,不能被持久化到磁盘,不能通过网络被传递,一般内部消息转换
Qos 1 : 这个消息至少发送一次,能被重传,能持久化,能通过网络传递,需要实现MqttConnectOptions中的持久化,否则,挂了以后,不能重传。
Qos 2:这个消息精准只发一次,能持久化,能通过网络传递,客户端和服务器都会收到消息确认
上面接口方法的用途,基本你都能猜出来,我们接着看实现类
public class MqttClient implements IMqttClient { //), DestinationProvider {
//private static final String CLASS_NAME = MqttClient.class.getName();
//private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME);
protected MqttAsyncClient aClient = null; // Delegate implementation to MqttAsyncClient
protected long timeToWait = -1; // How long each method should wait for action to complete
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long, long)
*/
public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException {
aClient.disconnectForcibly(quiesceTimeout, disconnectTimeout);
}
/*
* @see IMqttClient#subscribe(String[], int[])
*/
public void subscribe(String[] topicFilters, int[] qos) throws MqttException {
IMqttToken tok = aClient.subscribe(topicFilters, qos, null, null);
tok.waitForCompletion(getTimeToWait());
int[] grantedQos = tok.getGrantedQos();
for (int i = 0; i < grantedQos.length; ++i) {
qos[i] = grantedQos[i];
}
if (grantedQos.length == 1 && qos[0] == 0x80) {
throw new MqttException(MqttException.REASON_CODE_SUBSCRIBE_FAILED);
}
}
/*
* @see IMqttClient#publishBlock(String, byte[], int, boolean)
*/
public void publish(String topic, byte[] payload,int qos, boolean retained) throws MqttException,
MqttPersistenceException {
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
message.setRetained(retained);
this.publish(topic, message);
}
public void setManualAcks(boolean manualAcks) {
aClient.setManualAcks(manualAcks);
}
public void messageArrivedComplete(int messageId, int qos) throws MqttException {
aClient.messageArrivedComplete(messageId, qos);
}
........
}
这个类是代理类,其实是MqttAsyncClient干活
- 1.MqttAsyncClient是使用无阻塞运行在后台的轻量级链接server。基于Tcp,包含了ssl
- 2.为了确保消息通过网络被送达和重启客户端带有质量标志的消息要求被保存直到重新被送达。mqtt提供了一种自己持久化机制来保存这些消息。MqttDefaultFilePersistence是默认方式,如果为null则为瞬时消息保存在内存中。MqttClientPersistence可以自己现实接口
- 3.如果在connecting中MqttConnectOptions.setCleanSession(boolean)这个flag,为true也就说,如果client掉线disconnect,下次重连,将清空内存persistence消息,如果为false,就会使用持久化机制去重传
public class MqttAsyncClient implements IMqttAsyncClient { // DestinationProvider {
.......
public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence, MqttPingSender pingSender) throws MqttException {
final String methodName = "MqttAsyncClient";
log.setResourceName(clientId);
if (clientId == null) { //Support empty client Id, 3.1.1 standard
throw new IllegalArgumentException("Null clientId");
}
// Count characters, surrogate pairs count as one character.
int clientIdLength = 0;
for (int i = 0; i < clientId.length() - 1; i++) {
if (Character_isHighSurrogate(clientId.charAt(i)))
i++;
clientIdLength++;
}
if ( clientIdLength > 65535) {
throw new IllegalArgumentException("ClientId longer than 65535 characters");
}
MqttConnectOptions.validateURI(serverURI);
this.serverURI = serverURI;
this.clientId = clientId; // 【: 1】
this.persistence = persistence;
if (this.persistence == null) {
this.persistence = new MemoryPersistence();
}
// @TRACE 101=<init> ClientID={0} ServerURI={1} PersistenceType={2}
log.fine(CLASS_NAME,methodName,"101",new Object[]{clientId,serverURI,persistence});
this.persistence.open(clientId, serverURI); // 【: 2】
this.comms = new ClientComms(this, this.persistence, pingSender); // 【: 3】
this.persistence.close();
this.topics = new Hashtable();
}
protected NetworkModule[] createNetworkModules(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException { // 【: 4】
final String methodName = "createNetworkModules";
// @TRACE 116=URI={0}
log.fine(CLASS_NAME, methodName, "116", new Object[]{address});
NetworkModule[] networkModules = null;
String[] serverURIs = options.getServerURIs();
String[] array = null;
if (serverURIs == null) {
array = new String[]{address};
} else if (serverURIs.length == 0) {
array = new String[]{address};
} else {
array = serverURIs;
}
networkModules = new NetworkModule[array.length];
for (int i = 0; i < array.length; i++) {
networkModules[i] = createNetworkModule(array[i], options);
}
log.fine(CLASS_NAME, methodName, "108");
return networkModules;
}
private NetworkModule createNetworkModule(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException {
final String methodName = "createNetworkModule";
// @TRACE 115=URI={0}
log.fine(CLASS_NAME,methodName, "115", new Object[] {address});
NetworkModule netModule;
String shortAddress;
String host;
int port;
SocketFactory factory = options.getSocketFactory();
int serverURIType = MqttConnectOptions.validateURI(address); // 【: 5】
switch (serverURIType) {
case MqttConnectOptions.URI_TYPE_TCP :
shortAddress = address.substring(6);
host = getHostName(shortAddress);
port = getPort(shortAddress, 1883);
if (factory == null) {
factory = SocketFactory.getDefault();
}
else if (factory instanceof SSLSocketFactory) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
}
netModule = new TCPNetworkModule(factory, host, port, clientId); // 【: 7】
((TCPNetworkModule)netModule).setConnectTimeout(options.getConnectionTimeout());
break;
case MqttConnectOptions.URI_TYPE_SSL:
shortAddress = address.substring(6);
host = getHostName(shortAddress);
port = getPort(shortAddress, 8883);
SSLSocketFactoryFactory factoryFactory = null;
if (factory == null) {
// try {
factoryFactory = new SSLSocketFactoryFactory();
Properties sslClientProps = options.getSSLProperties();
if (null != sslClientProps)
factoryFactory.initialize(sslClientProps, null);
factory = factoryFactory.createSocketFactory(null);
// }
// catch (MqttDirectException ex) {
// throw ExceptionHelper.createMqttException(ex.getCause());
// }
}
else if ((factory instanceof SSLSocketFactory) == false) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
}
// Create the network module...
netModule = new SSLNetworkModule((SSLSocketFactory) factory, host, port, clientId);
((SSLNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout());
// Ciphers suites need to be set, if they are available
if (factoryFactory != null) {
String[] enabledCiphers = factoryFactory.getEnabledCipherSuites(null);
if (enabledCiphers != null) {
((SSLNetworkModule) netModule).setEnabledCiphers(enabledCiphers);
}
}
break;
case MqttConnectOptions.URI_TYPE_WS:
shortAddress = address.substring(5);
host = getHostName(shortAddress);
port = getPort(shortAddress, 80);
if (factory == null) {
factory = SocketFactory.getDefault();
}
else if (factory instanceof SSLSocketFactory) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
}
netModule = new WebSocketNetworkModule(factory, address, host, port, clientId);
((WebSocketNetworkModule)netModule).setConnectTimeout(options.getConnectionTimeout());
break;
case MqttConnectOptions.URI_TYPE_WSS:
shortAddress = address.substring(6);
host = getHostName(shortAddress);
port = getPort(shortAddress, 443);
SSLSocketFactoryFactory wSSFactoryFactory = null;
if (factory == null) {
wSSFactoryFactory = new SSLSocketFactoryFactory();
Properties sslClientProps = options.getSSLProperties();
if (null != sslClientProps)
wSSFactoryFactory.initialize(sslClientProps, null);
factory = wSSFactoryFactory.createSocketFactory(null);
}
else if ((factory instanceof SSLSocketFactory) == false) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
}
// Create the network module...
netModule = new WebSocketSecureNetworkModule((SSLSocketFactory) factory, address, host, port, clientId);
((WebSocketSecureNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout());
// Ciphers suites need to be set, if they are available
if (wSSFactoryFactory != null) {
String[] enabledCiphers = wSSFactoryFactory.getEnabledCipherSuites(null);
if (enabledCiphers != null) {
((SSLNetworkModule) netModule).setEnabledCiphers(enabledCiphers);
}
}
break;
case MqttConnectOptions.URI_TYPE_LOCAL :
netModule = new LocalNetworkModule(address.substring(8));
break;
default:
// This shouldn't happen, as long as validateURI() has been called.
netModule = null;
}
return netModule;
}
.......
public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback)
throws MqttException, MqttSecurityException {
final String methodName = "connect";
if (comms.isConnected()) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
}
if (comms.isConnecting()) {
throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
}
if (comms.isDisconnecting()) {
throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
}
if (comms.isClosed()) {
throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED);
}
this.connOpts = options;
this.userContext = userContext;
final boolean automaticReconnect = options.isAutomaticReconnect();
// @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7}
log.fine(CLASS_NAME,methodName, "103",
new Object[]{
Boolean.valueOf(options.isCleanSession()),
new Integer(options.getConnectionTimeout()),
new Integer(options.getKeepAliveInterval()),
options.getUserName(),
((null == options.getPassword())?"[null]":"[notnull]"),
((null == options.getWillMessage())?"[null]":"[notnull]"),
userContext,
callback });
comms.setNetworkModules(createNetworkModules(serverURI, options));
comms.setReconnectCallback(new MqttCallbackExtended() {
public void messageArrived(String topic, MqttMessage message) throws Exception {
}
public void deliveryComplete(IMqttDeliveryToken token) {
}
public void connectComplete(boolean reconnect, String serverURI) {
}
public void connectionLost(Throwable cause) {
if(automaticReconnect){
// Automatic reconnect is set so make sure comms is in resting state
comms.setRestingState(true);
reconnecting = true;
startReconnectCycle();
}
}
});
// Insert our own callback to iterate through the URIs till the connect succeeds
MqttToken userToken = new MqttToken(getClientId()); // 【: 8】....
ConnectActionListener connectActionListener =