MQTT-Eclipse paho mqtt源码分析-连接MQTT Broker

MQTT

MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个消息中间件 。
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。

以上说明来源与百度:百度关于MQTT的解释说明

paho mqtt

paho mqtt是IBM根据MQTT协议编写的client jar

源码分析

org.eclipse.paho.client.mqttv3.MqttClient

MqttClient的类图,我们发现其非常简单,就是实现了IMqttClient接口类
MqttClient
我们主要看connect()方法,我们发现MqttClient中的connect()方法调用的是MqttAsyncClient类中的connect()方法,我们在一路像下走,会发现真正的连接方法在ClientComms类中

	/*
	 * @see IMqttClient#connect(MqttConnectOptions)
	 */
	public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException {
		aClient.connect(options, null, null).waitForCompletion(getTimeToWait());
	}

在MqttAsyncClient的connect方法中,程序会创建对应的NetworkModule来进行与服务端的连接。NetworkModule一共有4种,分别是:TCPNetworkModule、SSLNetworkModule、WebSocketSecureNetworkModule、WebSocketNetworkModule。具体选择哪种实现,是通过Uri的secheme来实现。具体进行connect连接是在ClientComms的connect方法中。

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.
	 * client.mqttv3.MqttConnectOptions, java.lang.Object,
	 * org.eclipse.paho.client.mqttv3.IMqttActionListener)
	 */
	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);
		}
		if (options == null) {
			options = new MqttConnectOptions();
		}
		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()), Integer.valueOf(options.getConnectionTimeout()),
						Integer.valueOf(options.getKeepAliveInterval()), options.getUserName(),
						((null == options.getPassword()) ? "[null]" : "[notnull]"),
						((null == options.getWillMessage()) ? "[null]" : "[notnull]"), userContext, callback });
		//创建network module 并对comms设置networkmodules,此处通过spi技术,加载所有的NetworkModuleFactory接口实现,并通过uri的scheme进行匹配,来选择对应的NetworkModuleFactory,这里我们以TCPNetworkModuleFactory为例
		//networkmodule有:
		//org.eclipse.paho.client.mqttv3.internal.TCPNetworkModuleFactory
		//org.eclipse.paho.client.mqttv3.internal.SSLNetworkModuleFactory
	    //org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketNetworkModuleFactory
		//org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketSecureNetworkModuleFactory				
		comms.setNetworkModules(createNetworkModules(serverURI, options));
		comms.setReconnectCallback(new MqttReconnectCallback(automaticReconnect));

		// 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();

		return userToken;
	}

我们来看createNetworkModules方法的实现,它本身并没有什么复杂的逻辑。主要是构建参数,并调用createNetworkModule方法,在createNetworkModule方法种,它有调用了NetworkModuleService类中的createInstance方法。

public static NetworkModule createInstance(String address, MqttConnectOptions options, String clientId)
			throws MqttException, IllegalArgumentException
	{
		try {
			URI brokerUri = new URI(address);
			//应用RFC3986的权限补丁
			applyRFC3986AuthorityPatch(brokerUri);
			String scheme = brokerUri.getScheme().toLowerCase();
			//NetworkModule是通过Factory来进行创建的,我们在看FACTORY_SERVICE_LOADER属性,发现它用到了SPI机制,什么是SPI呢,我们会在后面的博客中进行讲解
			for (NetworkModuleFactory factory : FACTORY_SERVICE_LOADER) {
				if (factory.getSupportedUriSchemes().contains(scheme)) {
					return factory.createNetworkModule(brokerUri, options, clientId);
				}
			}
			/*
			 * To throw an IllegalArgumentException exception matches the previous behavior of
			 * MqttConnectOptions.validateURI(String), but it would be nice to provide something more meaningful.
			 */
			throw new IllegalArgumentException(brokerUri.toString());
		} catch (URISyntaxException e) {
			throw new IllegalArgumentException(address, e);
		}
	}

在这里我们分析一下ClientComms的connect方法,在此方法中并不会真证进行tcp/ip的连接,具体的连接是通过内部类(ConnectBG)启动线程的方式进行创建。我们可以发现在paho mqtt中,基本上都是通过线程进行启动的。在线程中会同时启动:CommsReceiver(数据接收)、CommsSender(发送)、CommsCallback(回调)。

	private class ConnectBG implements Runnable {
		ClientComms 	clientComms = null;
		MqttToken 		conToken;
		MqttConnect 	conPacket;
		private String threadName;

		ConnectBG(ClientComms cc, MqttToken cToken, MqttConnect cPacket, ExecutorService executorService) {
			clientComms = cc;
			conToken 	= cToken;
			conPacket 	= cPacket;
			threadName = "MQTT Con: "+getClient().getClientId();
		}

		void start() {
			if (executorService == null) {
				new Thread(this).start();
			} else {
				executorService.execute(this);
			}
		}

		public void run() {
			Thread.currentThread().setName(threadName);
			final String methodName = "connectBG:run";
			MqttException mqttEx = null;
			//@TRACE 220=>
			log.fine(CLASS_NAME, methodName, "220");

			try {
				// Reset an exception on existing delivery tokens.
				// This will have been set if disconnect occurred before delivery was
				// fully processed.
				MqttDeliveryToken[] toks = tokenStore.getOutstandingDelTokens();
				for (MqttDeliveryToken tok : toks) {
					tok.internalTok.setException(null);
				}

				// Save the connect token in tokenStore as failure can occur before send
				tokenStore.saveToken(conToken,conPacket);

				// Connect to the server at the network level e.g. TCP socket and then
				// start the background processing threads before sending the connect
				// packet.
				NetworkModule networkModule = networkModules[networkModuleIndex];
				//这里真正进行tcp/ip的连接
				//启动网络模块,进行网络连接
				networkModule.start();
				//数据的接收,启动完成后,负责从broker接收消息
				receiver = new CommsReceiver(clientComms, clientState, tokenStore, networkModule.getInputStream());
				receiver.start("MQTT Rec: "+getClient().getClientId(), executorService);
				//数据发送,启动完成后,负责发送消息到broker
				sender = new CommsSender(clientComms, clientState, tokenStore, networkModule.getOutputStream());
				sender.start("MQTT Snd: "+getClient().getClientId(), executorService);
				//回调服务,在连接时,指定的Callback服务
				callback.start("MQTT Call: "+getClient().getClientId(), executorService);
				//发送一条遗言消息,如果在连接客户端代码中有设置的话
				internalSend(conPacket, conToken);
			} catch (MqttException ex) {
				//@TRACE 212=connect failed: unexpected exception
				log.fine(CLASS_NAME, methodName, "212", null, ex);
				mqttEx = ex;
			} catch (Exception ex) {
				//@TRACE 209=connect failed: unexpected exception
				log.fine(CLASS_NAME, methodName, "209", null, ex);
				mqttEx =  ExceptionHelper.createMqttException(ex);
			}

			if (mqttEx != null) {
				//如果发现异常,进行连接的shutdown操作
				shutdownConnection(conToken, mqttEx);
			}
		}
	}

以上就是paho mqtt连接MQTT服务器的流程,代码其实非常简单。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MQTT 协议中,消息的发送和接收是非常重要的。Eclipse Paho 是一个 MQTT 客户端库,支持多种编程语言,例如 Java、C++、Python 等。本文将重点介绍 Eclipse Paho 中消息的发送和接收。 一、消息的发送 1.1 发送消息的基本流程 在 Eclipse Paho 中,发送消息的基本流程如下: 1. 创建一个 MqttClient 对象。 2. 连接MQTT 服务端。 3. 创建一个 MqttMessage 对象,设置消息的内容和 QoS 等级。 4. 调用 MqttClient 的 publish() 方法发送消息。 5. 断开 MQTT 连接。 1.2 代码示例 下面是一个使用 Eclipse Paho 发送消息的 Java 代码示例: ```java import org.eclipse.paho.client.mqttv3.*; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; public class MqttPublishSample { public static void main(String[] args) { String topic = "MQTT Examples"; String content = "Message from MqttPublishSample"; int qos = 2; String broker = "tcp://localhost:1883"; String clientId = "JavaSample"; MemoryPersistence persistence = new MemoryPersistence(); try { MqttClient sampleClient = new MqttClient(broker, clientId, persistence); MqttConnectOptions connOpts = new MqttConnectOptions(); connOpts.setCleanSession(true); sampleClient.connect(connOpts); MqttMessage message = new MqttMessage(content.getBytes()); message.setQos(qos); sampleClient.publish(topic, message); sampleClient.disconnect(); System.out.println("Message published"); } catch(MqttException me) { System.out.println("Reason: " + me.getReasonCode()); System.out.println("Message: " + me.getMessage()); System.out.println("Loc: " + me.getLocalizedMessage()); System.out.println("Cause: " + me.getCause()); System.out.println("Excep: " + me); me.printStackTrace(); } } } ``` 在这个代码示例中,我们创建了一个 MqttClient 对象,并连接到本地的 MQTT 服务器。然后,我们创建了一个 MqttMessage 对象,设置了消息的内容和 QoS 等级,并使用 publish() 方法将消息发送到指定的主题。 1.3 QoS 等级 在 MQTT 协议中,有三种不同的 QoS 等级:0、1 和 2。QoS 等级越高,消息的可靠性越高,但是网络负载也会相应增加。 - QoS 0:最多发送一次,没有确认,可能会出现消息丢失或重复。 - QoS 1:至少发送一次,确保消息至少被接收一次,但可能会出现重复消息。 - QoS 2:只发送一次,确保消息仅被接收一次,但网络负担较大。 二、消息的接收 2.1 接收消息的基本流程 在 Eclipse Paho 中,接收消息的基本流程如下: 1. 创建一个 MqttClient 对象。 2. 连接MQTT 服务端。 3. 创建一个 MqttCallback 对象,用于处理接收到的消息。 4. 订阅一个或多个主题。 5. 等待接收消息。 6. 断开 MQTT 连接。 2.2 代码示例 下面是一个使用 Eclipse Paho 接收消息的 Java 代码示例: ```java import org.eclipse.paho.client.mqttv3.*; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; public class MqttSubscribeSample { public static void main(String[] args) { String topic = "MQTT Examples"; String broker = "tcp://localhost:1883"; String clientId = "JavaSample"; MemoryPersistence persistence = new MemoryPersistence(); try { MqttClient sampleClient = new MqttClient(broker, clientId, persistence); MqttConnectOptions connOpts = new MqttConnectOptions(); connOpts.setCleanSession(true); sampleClient.connect(connOpts); sampleClient.setCallback(new MqttCallback() { public void connectionLost(Throwable cause) { System.out.println("Connection lost"); } public void messageArrived(String topic, MqttMessage message) throws Exception { System.out.println("Message received: " + new String(message.getPayload())); } public void deliveryComplete(IMqttDeliveryToken token) { System.out.println("Delivery complete"); } }); sampleClient.subscribe(topic); // Wait for messages Thread.sleep(5000); sampleClient.disconnect(); System.out.println("Disconnected"); } catch(MqttException me) { System.out.println("Reason: " + me.getReasonCode()); System.out.println("Message: " + me.getMessage()); System.out.println("Loc: " + me.getLocalizedMessage()); System.out.println("Cause: " + me.getCause()); System.out.println("Excep: " + me); me.printStackTrace(); } catch(InterruptedException e) { e.printStackTrace(); } } } ``` 在这个代码示例中,我们创建了一个 MqttClient 对象,并连接到本地的 MQTT 服务器。然后,我们创建了一个 MqttCallback 对象,用于处理接收到的消息。在 MqttCallback 的 messageArrived() 方法中,我们可以处理接收到的消息。 最后,我们使用 subscribe() 方法订阅了一个主题,并等待 5 秒钟接收消息。在接收完消息之后,我们断开了 MQTT 连接。 总结 在本文中,我们介绍了使用 Eclipse Paho 发送和接收 MQTT 消息的基本流程,并提供了 Java 代码示例。同时,我们还介绍了 MQTT 的 QoS 等级。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值