Connect分析
调用MqttClient的connect方法会触发:MqttAsyncClient的connect方法,其实源码是写了一套,MqttClient复用了MqttAsyncClient的代码。
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();
comms.setNetworkModules(createNetworkModules(serverURI, options));//见下面Code1
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());
ConnectActionListener connectActionListener = new ConnectActionListener(this, persistence, comms, options, userToken, userContext, callback, reconnecting);
userToken.setActionCallback(connectActionListener);
userToken.setUserContext(this);
// If we are using the MqttCallbackExtended, set it on the connectActionListener
if(this.mqttCallback instanceof MqttCallbackExtended){
connectActionListener.setMqttCallbackExtended((MqttCallbackExtended)this.mqttCallback);
}
comms.setNetworkModuleIndex(0);
connectActionListener.connect();// 调用连接API,参加Code3
return userToken;
}
Code1 ClientComms根据SSL/TCP/WS等创建Socket连接
大意就是根据address以及MqttConnectOptions options里面封装的ServerURI,如果options里面封装的serverURI为空或者数组length为0,则根据address创建一个NetworkModule实例,否则根据options里面的serverURI创建所有的NetworkModule实例,并存储到networkModules数组中去。
/**
* Factory method to create an array of network modules, one for
* each of the supplied URIs
*
* @param address the URI for the server.
* @return a network module appropriate to the specified address.
*/
// may need an array of these network modules
protected NetworkModule[] createNetworkModules(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException {
final String methodName = "createNetworkModules";
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);//分析一下createNetworkModule
}
return networkModules;
}
首先我们需要知道NetworkModule接口封装的是相关Socket网络操作,获取输入输出流,启动关闭Socket连接模块操作
public interface NetworkModule {
public void start() throws IOException, MqttException;
public InputStream getInputStream() throws IOException;
public OutputStream getOutputStream() throws IOException;
public void stop() throws IOException;
public String getServerURI();
}
//根据一个地址创建出一个NetworkModule
private NetworkModule createNetworkModule(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException {
final String methodName = “createNetworkModule”;
NetworkModule netModule;
String shortAddress;
String host;
int port;
SocketFactory factory = options.getSocketFactory();
int serverURIType = MqttConnectOptions.validateURI(address);//查询到底是SSL,TCP,WEBSOCKET等还是什么连接
switch (serverURIType) {
case MqttConnectOptions.URI_TYPE_TCP :
shortAddress = address.substring(6);
host = getHostName(shortAddress);
port = getPort(shortAddress, 1883);
//如果MqttConnectOptions没有设置SocketFactory则建立一个默认的SocketFactory,通过SocketFactory.getDefault();方法获取。
if (factory == null) {
factory = SocketFactory.getDefault();
}
else if (factory instanceof SSLSocketFactory) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
}
//创建一个TCPNetworkModule实例(实现NetworkModule接口)参见Code1.1
netModule = new TCPNetworkModule(factory, host, port, clientId);
((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;
}
Code1.1
/**
* A network module for connecting over TCP.
*/
public class TCPNetworkModule implements NetworkModule {
protected Socket socket;
private SocketFactory factory;
private String host;
private int port;
private int conTimeout;
/**
* Constructs a new TCPNetworkModule using the specified host and
* port. The supplied SocketFactory is used to supply the network
* socket.
*/
public TCPNetworkModule(SocketFactory factory, String host, int port, String resourceContext) {
this.factory = factory;
this.host = host;
this.port = port;
}
/**
* Starts the module, by creating a TCP socket to the server.建立连接,connect server
*/
public void start() throws IOException, MqttException {
final String methodName = "start";
try {
SocketAddress sockaddr = new InetSocketAddress(host, port);
socket = factory.createSocket();
socket.connect(sockaddr, conTimeout*1000);
}
catch (ConnectException ex) {
throw new MqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR, ex);
}
}
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
public OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
}
/**
* Stops the module, by closing the TCP socket.
*/
public void stop() throws IOException {
if (socket != null) {
socket.close();
}
}
/**
* Set the maximum time to wait for a socket to be established
* @param timeout
*/
public void setConnectTimeout(int timeout) {
this.conTimeout = timeout;
}
public String getServerURI() {
return "tcp://" + host + ":" + port;
}
}
同理可以分析SSLNetworkModule等等的实现,这里就是为了获取一个Socket的操作工具模块。
Code3 ConnectActionListener
首先分析ConnectActionListener类,
public ConnectActionListener(
MqttAsyncClient client,
MqttClientPersistence persistence,
ClientComms comms,
MqttConnectOptions options,
MqttToken userToken,
Object userContext,
IMqttActionListener userCallback,
boolean reconnect) {
this.persistence = persistence;
this.client = client;
this.comms = comms;
this.options = options;
this.userToken = userToken;
this.userContext = userContext;
this.userCallback = userCallback;
this.originalMqttVersion = options.getMqttVersion();
this.reconnect = reconnect;
}
将persistence,client,comms,options,userToken等对象通过构造函数传入,创建ConnectionActionListener.这个类是用来处理连接的,他调用connect()方法时使用的是自己内部的回掉函数,然后在自己的回调函数里面再调用userCallback的回调函数。具体如下:
/**
* Start the connect processing
* @throws MqttPersistenceException
*/
public void connect() throws MqttPersistenceException {
MqttToken token = new MqttToken(client.getClientId());
token.setActionCallback(this);//设置自己为当前的回调函数
token.setUserContext(this);
persistence.open(client.getClientId(), client.getServerURI());
if (options.isCleanSession()) {
persistence.clear();
}
if (options.getMqttVersion() == MqttConnectOptions.MQTT_VERSION_DEFAULT) {
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
}
try {
comms.connect(options, token);//真正连接的connect调用的是ClientComms的connect方法;
}
catch (MqttException e) {
onFailure(token, e);
}
}
我们可以知道当执行comms.connect()之后成功会触发ConnectActionListener的onSuccess回调,失败则会触发ConnectActionListener的onFailure回调,这里我们只分析一个,onSuccess回调
/**
* If the connect succeeded then call the users onSuccess callback
*
* @param token
*/
public void onSuccess(IMqttToken token) {
if (originalMqttVersion == MqttConnectOptions.MQTT_VERSION_DEFAULT) {
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_DEFAULT);
}
userToken.internalTok.markComplete(token.getResponse(), null);
userToken.internalTok.notifyComplete();
userToken.internalTok.setClient(this.client); // fix bug 469527 - maybe should be set elsewhere?
if(reconnect){
comms.notifyReconnect();
}
if (userCallback != null) {
userToken.setUserContext(userContext);
userCallback.onSuccess(userToken);//从这里我们可以知道会调用传给ConnectionActionListener的回调.这个所谓的用户层的回调,就是当我们调用MqttAsyncClient 或者 MqttClient mqttAsyncClient.setCallback(new MqttCallback() {里面的方法。
}
if(mqttCallbackExtended != null){
String serverURI = comms.getNetworkModules()[comms.getNetworkModuleIndex()].getServerURI();
mqttCallbackExtended.connectComplete(reconnect, serverURI);
}
}