Как стать автором
Обновить

Flutter + Socket.io — Обмен информацией в режиме реального времени

Время на прочтение7 мин
Количество просмотров16K
Автор оригинала: Chiziaruhoma Ogbonda

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

Представьте себе трубу, по которой вода (данные) может течь в обе стороны.

Преимущества WebSocket над HTTPS

WebSocket имеет множество преимуществ над привычным HTTPS, в зависимости от архитектуры вашего проекта, WebSocket обычно быстрее в обмене информацией.

В отличии от принципа запрос-ответ, как в HTTP, WebSocket предлагает обмен информации в реальном времени с двунаправленной системой передачи данных, в которой нет необходимости отправлять запросы для получения ответов.

Благодаря такому методу мгновенной коммуникации разработчики могут предоставлять пользователям возможность получения уведомлений, оповещений, а также обмен сообщениями и использование других сервисов.

Создание Node сервера

Нам предстоит поработать над двумя приложениями: сервером на Node.js и, собственно, Flutter-приложением. В сущности, наш сервер будет эхо-сервер, который получает данные и отправляет их всем, кто слушает (другими словами - тем, кто подписан на получение этих данных). Условно, сервер является мостом между клиентами.

В роли клиента будет выступать Flutter приложение, которое будет использовать WebSocket для получения информации. Чтобы установить сервер нужно:

  • Создать папку с именем проекта, например socket_chat_server

  • Создать файл package.json в этой папке

  • Переключиться в текущую папку, если еще не сделали это

  • Запустить команду npm init и завершить установку

Измените файл package.json, чтобы добавить нужные нам две зависимости:

package.json

{
  "name": "socket_chat_server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node ./index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "http": "0.0.1-security",
    "socket.io": "^2.3.0"
  },
  "engines": {
    "node": "10.x"
  }
}

Затем установим npm модули с помощью команды npm install.

index.js

Последним шагом будет написание логики сервера, как в примере:

const server = require('http').createServer()
const io = require('socket.io')(server)

io.on('connection', function (client) {

  console.log('client connect...', client.id);

  client.on('typing', function name(data) {
    console.log(data);
    io.emit('typing', data)
  })

  client.on('message', function name(data) {
    console.log(data);
    io.emit('message', data)
  })

  client.on('location', function name(data) {
    console.log(data);
    io.emit('location', data);
  })

  client.on('connect', function () {
  })

  client.on('disconnect', function () {
    console.log('client disconnect...', client.id)
    // handleDisconnect()
  })

  client.on('error', function (err) {
    console.log('received error from client:', client.id)
    console.log(err)
  })
})

var server_port = process.env.PORT || 3000;
server.listen(server_port, function (err) {
  if (err) throw err
  console.log('Listening on port %d', server_port);
});

Здесь все очень просто, запускается соединение, затем идет прослушивание данных через client.on, передача события для прослушивания, к примеру "сообщение", и отправка информации назад всем, кто подключен к серверу. Ваш WebSocket должен быть запущен на http://localhost:3000.

С помощью команды npm start можно протестировать сервер. Вывод должен быть таким:

Создание приложения Flutter

Flutter приложение будет выступать как клиент, которые получает сообщения от сервера и отправляет их к нему. Создайте новый проект с помощью команды flutter create socket_chat. На pub.dev есть несколько socket.io пакетов, но, как мне кажется, socket_io_client будет проще для понимания, так как является копией js библиотеки:

​ Обратите внимание

При подключении к localhost на Android эмуляторе может потребоваться немного больше настроек. Команда adb reverse tcp:3000 tcp:3000 разрешает подключаться к localhost через http://127.0.0.1:3000 на Android (а без команды можно подключится к localhost с Android эмулятора посредством подключения к адресу http://10.0.2.2:3000. прим. переводчика) . После удаления стандартного приложения-счетчика приступим к написанию home.dart.

home.dart

...
 
  Socket socket;
  
  @override
  void initState() {
    super.initState();
    connectToServer();
  }

  void connectToServer() {
    try {
     
      // Configure socket transports must be sepecified
      socket = io('http://127.0.0.1:3000', <String, dynamic>{
        'transports': ['websocket'],
        'autoConnect': false,
      });
     
      // Connect to websocket
      socket.connect();
     
      // Handle socket events
      socket.on('connect', (_) => print('connect: ${socket.id}'));
      socket.on('location', handleLocationListen);
      socket.on('typing', handleTyping);
      socket.on('message', handleMessage);
      socket.on('disconnect', (_) => print('disconnect'));
      socket.on('fromServer', (_) => print(_));

    } catch (e) {
      print(e.toString());
    }

   
  }
   
  // Send Location to Server
  sendLocation(Map<String, dynamic> data) {
    socket.emit("location", data);
  }
   
  // Listen to Location updates of connected usersfrom server
  handleLocationListen(Map<String, dynamic> data) async {
    print(data);
  }

  // Send update of user's typing status 
  sendTyping(bool typing) {
    socket.emit("typing",
      {
        "id": socket.id,
         "typing": typing,
      });
  }
   
  // Listen to update of typing status from connected users
  void handleTyping(Map<String, dynamic> data) {
    print(data);
  }

  // Send a Message to the server
  sendMessage(String message) {
      socket.emit("message",
        {
          "id": socket.id,
          "message": message, // Message to be sent
          "timestamp": DateTime.now().millisecondsSinceEpoch,
        },
      );
  }

  // Listen to all message events from connected users
  void handleMessage(Map<String, dynamic> data) {
    print(data);
  }


...

Пример выше - простая реализация WebSocket, в которой есть функции прослушивания и отправления событий к серверу.

Настройка CI/CD для тестирования

Возможно, во время разработки вам потребуется использование различных WebSocket для разных версий приложения(Test, Dev, или Prod). Хорошей практикой считается вынесение секретных участков кода(к примеру, URL-адрес хоста) в зашифрованную CI/CD сборку.

Кроме того, использование CI/CD стало нормой для хороших тестируемых и масштабируемых мобильных приложений, и поэтому важно и рекомендуется всегда поддерживать наличие оного для ваших Flutter пактов, Flutter приложений и тому подобного.

Существует множество способов интеграции среды CI/CD в Codemagic. Я хотел бы пойти более интересным, но сложным путем и объяснить, как создать среды dev и prod с помощью простого старого .yaml файла и загрузить его на свой Codemagic.io проект в кратчайшие сроки.

Да, я знаю, что есть UI, но иногда вам нужно протестировать поток CI/CD, не прерывая существующие, или создать новый поток с графическим интерфейсом. Итак, вот быстрый, но простой способ сделать это:

Настройки и Среды

Мы будем создавать нашу структуру CI/CD и среды таким образом, чтобы мы могли использовать как динамические значения из Codemagic, так и фиксированные значения среды из наших настроек.

Структура проста, у нас есть наш ненастроенный JSON файл config/appconfig.json, в нем будут размещены наши URL-адреса хостов для разработки и готовой сборки. Вам также нужно будет установить его следующим образом.

assets:
  - config/app_config.json

Код в lib/config/config_loader.dart управляет загрузкой данных из нашего JSON файла (config/appconfig.json).

env.dart и env_config.dart управляют созданием наших различных сред для использования в_dev.dart и _prod.dart .

Для большинства Flutter приложений main.dart является входной точкой. Но для нас, чтобы наша задумка работала, нужно создать файлы main_common.dart , main_dev.dart и main_prod.dart. Как следует из названия, main_common.dart будет мостом, связывающим с предпочитаемой средой. Теперь для запуска кода нужно будет указывать настройки, с которыми мы хотим запустить приложение.

У нас есть flutter run -t lib/main_dev.dart для разработки и flutter run -t lib/main_prod.dart для готовой сборки.

Переменные среды

Теперь нам нужно преобразовать JSON конфиг в base64, чтобы его можно было добавить как переменные среды в codemagic.yaml, здесь можно найти более подробную информацию об этом.

Codemagic.yaml

Codemagic дает возможность записывать настройки работы приложения в легко настраиваемом yaml синтаксисе. Мы создадим и изменим наш файл codemagic.yaml следующим образом:

# Automatically generated on 2020-11-16 UTC from https://codemagic.io/app/5fb2a25c605096f91720b983/settings
# Note that this configuration is not an exact match to UI settings. Review and adjust as necessary.

workflows:
  default-workflow:
    name: Default Workflow
    max_build_duration: 60
    environment:
      vars:
        APP_CONFIG: ewogICJnQXBpS2V5IjogIkFJemFTeUEzbl95bTlWUUU2NURyRUVpdDZobnNtWDgyR3FGb3Q0QSIKfQo=
      flutter: stable
      xcode: latest
      cocoapods: default
    scripts:
      - |
        # set up debug keystore
        rm -f ~/.android/debug.keystore
        keytool -genkeypair \
          -alias androiddebugkey \
          -keypass android \
          -keystore ~/.android/debug.keystore \
          -storepass android \
          -dname 'CN=Android Debug,O=Android,C=US' \
          -keyalg 'RSA' \
          -keysize 2048 \
          -validity 10000        
      - |
        # set up local properties
        echo "flutter.sdk=$HOME/programs/flutter" > "$FCI_BUILD_DIR/android/local.properties"        
      - cd . && flutter packages pub get
      - |
        # Create directory if it doesn't exist
        mkdir -p $FCI_BUILD_DIR/config
        # Write out the environment variable as a json file
        echo $APP_CONFIG | base64 --decode > $FCI_BUILD_DIR/config/app_config.json        
      - cd . && flutter build apk --debug -t lib/main_dev.dart
      - find . -name "Podfile" -execdir pod install \;
      - cd . && flutter build ios --debug --no-codesign lib/main_dev.dart
    artifacts:
      - build/**/outputs/**/*.apk
      - build/**/outputs/**/*.aab
      - build/**/outputs/**/mapping.txt
      - build/ios/ipa/*.ipa
      - /tmp/xcodebuild_logs/*.log
      - flutter_drive.log
    publishing:
      email:
        recipients:
          - ogbondachiziaruhoma@gmail.com

Определив наши переменные среды следующим образом:

vars:
  APP_CONFIG: ewogICJkZXZIb3N0IjogIkRldmVsb3BtZW50IEhvc3QiLAogICJwcm9kSG9zdCI6ICJQcm9kdWN0aW9uIEhvc3QiCn0K

Вставив этот фрагмент кода выше, мы можем удалить файл /config/app_config.json и создать его при запуске CI сборки.

# Write out the environment variable as a json file        
echo $APP_CONFIG | base64 --decode > $FCI_BUILD_DIR/config/app_config.json

Примечание: Наш входной файл lib/main_dev.dart в файле codemagic.yaml должен быть изменен на lib/main_prod.dartдля готовой сборки.

Запуск сборки

После сохранения codemagic.yaml файла на предпочитаемой системе контроля версий(GitHub, bitbucket и т.д), нажмите на “Start your first build”.

Файл среды определится автоматически:

Нажмите на “Start new build”:

Вуаля, сборка запущена.

The end

Веб-сокеты быстрые, просты в настройке и все еще могут быть "RESTful". Их можно использовать для большого количества приложений. И мы также видим, что очень просто настроить работу приложения и интегрировать различные переменные среды/скрыть части кода в Codemagic CI/CD, и радость в том, что вам нужно сделать это только один раз, после этой настройки вы можете добавить больше конфигураций или изменить существующие.

Если вы хотите познакомиться с более продвинутым использованием веб-сокетов и Codemagic CI/CD, пройдите по ссылки, чтобы посмотреть полный код.

Теги:
Хабы:
Всего голосов 2: ↑2 и ↓0+2
Комментарии4

Публикации

Информация

Сайт
www.rshb.ru
Дата регистрации
Дата основания
Численность
свыше 10 000 человек
Местоположение
Россия
Представитель
Юлия Князева