/** * Kick off a new publication, queue it or send it as appropriate. * @param pubclient the client to send the publication to * @param publish the publication data * @param qos the MQTT QoS to use * @param retained boolean - whether to set the MQTT retained flag * @param mm - pointer to the message to send * @return the completion code */ int MQTTProtocol_startOrQueuePublish(Clients* pubclient, Publish* publish, int qos, int retained, int priority, Messages** mm) { int rc = TCPSOCKET_COMPLETE; int socket = pubclient->socket; /* used when MQTT_MP protocol is included */ FUNC_ENTRY; #if defined(MQTTMP) if (pubclient->protocol == PROTOCOL_MQTT_MP) socket = pubclient->actualSock; #endif if (pubclient->connected && pubclient->good && /* client is connected and has no errors */ Socket_noPendingWrites(socket) && /* there aren't any previous packets still stacked up on the socket */ queuedMsgsCount(pubclient) == 0 && /* there are no messages ahead in the queue */ pubclient->outboundMsgs->count < bstate->max_inflight_messages) /* the queue is not full */ { #if defined(MQTTS) if (pubclient->protocol == PROTOCOL_MQTTS && strlen(publish->topic) > 2 && MQTTSProtocol_getRegisteredTopicId(pubclient, publish->topic) == 0) { if (pubclient->pendingRegistration == NULL) rc = MQTTSProtocol_startRegistration(pubclient, publish->topic); rc = MQTTProtocol_queuePublish(pubclient, publish, qos, retained, priority, mm); } else if (pubclient->protocol == PROTOCOL_MQTTS && qos > 0 && pubclient->outboundMsgs->count > 0) { /* can only have 1 qos 1/2 message in flight with MQTT-S */ rc = MQTTProtocol_queuePublish(pubclient, publish, qos, retained, priority, mm); } else { #endif rc = MQTTProtocol_startPublish(pubclient, publish, qos, retained, mm); /* We don't normally queue QoS 0 messages just send them straight through. But in the case when none of the packet * is written we need to queue it up. If only part of the packet is written, then it is buffered by the socket module. */ if (qos == 0 && rc == TCPSOCKET_NOWORK) rc = MQTTProtocol_queuePublish(pubclient, publish, qos, retained, priority, mm); #if defined(MQTTS) } #endif } else if (qos != 0 || (pubclient->connected && pubclient->good)) /* only queue qos 0 if the client is connected in a good way */ rc = MQTTProtocol_queuePublish(pubclient, publish, qos, retained, priority, mm); FUNC_EXIT_RC(rc); return rc; }
int MQTTClient_publish(MQTTClient handle, const char* topicName, int payloadlen, void* payload, int qos, int retained, MQTTClient_deliveryToken* deliveryToken) { int rc = MQTTCLIENT_SUCCESS; MQTTClients* m = handle; Messages* msg = NULL; Publish* p = NULL; int blocked = 0; int msgid = 0; FUNC_ENTRY; Thread_lock_mutex(mqttclient_mutex); if (m == NULL || m->c == NULL) rc = MQTTCLIENT_FAILURE; else if (m->c->connected == 0) rc = MQTTCLIENT_DISCONNECTED; else if (!UTF8_validateString(topicName)) rc = MQTTCLIENT_BAD_UTF8_STRING; if (rc != MQTTCLIENT_SUCCESS) goto exit; /* If outbound queue is full, block until it is not */ while (m->c->outboundMsgs->count >= m->c->maxInflightMessages || Socket_noPendingWrites(m->c->net.socket) == 0) /* wait until the socket is free of large packets being written */ { if (blocked == 0) { blocked = 1; Log(TRACE_MIN, -1, "Blocking publish on queue full for client %s", m->c->clientID); } Thread_unlock_mutex(mqttclient_mutex); MQTTClient_yield(); Thread_lock_mutex(mqttclient_mutex); if (m->c->connected == 0) { rc = MQTTCLIENT_FAILURE; goto exit; } } if (blocked == 1) Log(TRACE_MIN, -1, "Resuming publish now queue not full for client %s", m->c->clientID); if (qos > 0 && (msgid = MQTTProtocol_assignMsgId(m->c)) == 0) { /* this should never happen as we've waited for spaces in the queue */ rc = MQTTCLIENT_MAX_MESSAGES_INFLIGHT; goto exit; } p = malloc(sizeof(Publish)); p->payload = payload; p->payloadlen = payloadlen; p->topic = (char*)topicName; p->msgId = msgid; rc = MQTTProtocol_startPublish(m->c, p, qos, retained, &msg); /* If the packet was partially written to the socket, wait for it to complete. * However, if the client is disconnected during this time and qos is not 0, still return success, as * the packet has already been written to persistence and assigned a message id so will * be sent when the client next connects. */ if (rc == TCPSOCKET_INTERRUPTED) { while (m->c->connected == 1 && SocketBuffer_getWrite(m->c->net.socket)) { Thread_unlock_mutex(mqttclient_mutex); MQTTClient_yield(); Thread_lock_mutex(mqttclient_mutex); } rc = (qos > 0 || m->c->connected == 1) ? MQTTCLIENT_SUCCESS : MQTTCLIENT_FAILURE; } if (deliveryToken && qos > 0) *deliveryToken = msg->msgid; free(p); if (rc == SOCKET_ERROR) { Thread_unlock_mutex(mqttclient_mutex); MQTTClient_disconnect_internal(handle, 0); Thread_lock_mutex(mqttclient_mutex); /* Return success for qos > 0 as the send will be retried automatically */ rc = (qos > 0) ? MQTTCLIENT_SUCCESS : MQTTCLIENT_FAILURE; } exit: Thread_unlock_mutex(mqttclient_mutex); FUNC_EXIT_RC(rc); return rc; }