Устойчивое, самовосстанавливающееся TCP-соединение

В случае построения системы из необслуживаемых сервера и клиентов необходимо обеспечить надёжность соединения между ними, добавив самовосстановление/переподключение после потери связи. Нужно отслеживать эту ситуацию и на сервере, и на клиенте.

В качестве модельного проекта я вновь использую систему из статьи «JSON-транспорт между микроконтроллером и сервером». Подробное описание процесса установки TCP-соединения — в статье «Установка TCP-соединения в стеке lwIP», обязательно прочитайте её, а то будет непонятно.

Клиент

Во-первых, назначим функцию-обработчик ошибок:

tcp_err(TCPSockJSON, json_client_err);

Эта функция (json_client_err) будет пытаться снова поднять соединение с сервером:

static void json_client_err(void *arg, err_t err)
{
LOG("JSON transport error\n");
json_status = 0;
setTimeout(InitJSON, 1000);
}

Функция setTimeout ставит в расписание выполнение функции InitJSON через 1000 миллисекунд. Эти асинхронные действия назначаются так:

struct struct_async_task {
uint32_t timeout;
void (* async)(void);
} async_task;

void setTimeout(void (* async)(void), uint32_t delay)
{
async_task.timeout = MS_TIMER + delay;
async_task.async = async;
}

И обрабатываются вызовом функции MakeAsyncTasks() по прерываниям от системного таймера. Переменная MS_TIMER инкрементируется каждую миллисекунду и служит таймштампом. В этой реализации пока можно назначить только одно асинхронное задание.

void MakeAsyncTasks()
{
if(async_task.timeout > MS_TIMER) {
async_task.async();
async_task.timeout = 0;
}
}

Также нам потребуется глобальный флаг, говорящий о наличии связи с сервером. При успешном подключении мы устанавливаем его, а после ошибки сбрасываем.

int json_status = 0;

static err_t json_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
json_status = 1;
return err;
}

Все функции отправки данных теперь нужно обвязать условием if(json_status) { … }.

Теперь при потере связи клиент будет уходить в начальное состояние и ежесекундно пытаться вновь связаться с сервером вплоть до успеха.

Сервер

По сути, в коде сервера на node.js нужно лишь добавить обработчик ошибки, который может к примеру просто сообщать об ошибке в лог.

socket.on('error', function(error) {
console.log('Error:', error);
});

Больше действий не требуется.

Тест

Для проверки можно выключить клиент и включить снова. Сервер напишет в логе об ошибке, и снова примет подключение клиента.

Теперь выключим сервер, заранее открыв лог клиента (у меня он сделан по telnet). При падении связи клиент напишет ошибку, и спустя секунду попробует подключиться снова. Если не запускать сервер — клиент так и будет раз в секунду пытаться подключиться.

Как только сервер будет запущен вновь, клиент успешно к нему подключится.

Ссылка на основную публикацию