Open75

暗黙的な基礎知識

さしもんさしもん

Bundleとは

  • コードとリソースをカプセル化するために使われる技術
    簡単に言うと、入れ子になったフォルダを1つの書類に見せかけるもの。

たとえば

Resource Bundle

  • 画像やカラー、xibなど複数のファイルを1つにまとめている

アプリケーションバンドル(.app)

  • アプリ全体で使うコード(実行ファイル)やリソース(リソースファイル)を1つにまとめている
  • バンドルのしくみにより、ひとつに見えているアプリも実はフォルダーで、階層構造を持っています。
Notion.app パッケージの中身
Finder > アプリケーションに表示されているのはアプリケーションバンドル? 確かに.appの中には入れ子になったフォルダがあって色んなファイルがまとまっている

Bundleの用途

ライブラリで使う画像や文字列(Localizable.strings)などをResource Bundleにまとめて
ソースコードと一緒に配布する場面とかで使われる

実際CocoaPodsではライブラリ(のリソース)の配布形態としてResource Bundleを推奨してるみたい

参考: http://cocoadays.blogspot.com/2014/02/resource-bundle-cocoapods.html
参考: https://note.com/kaigian/n/n2c24e6c0d82d

さしもんさしもん

確かにプロジェクトでBuildPhases>Copy Bundle Resourcesをみてみると
リソースファイルだけがここにまとまってた

さしもんさしもん

こんなBundleの説明もあった

  • 開発を便利にするための、ファイルのように扱えるディレクトリ
  • ダブルクリックでアプリケーションが開いたりプロジェクトが開いたりする
  • 一定のルールを持った構造
  • ライブラリと違ってフレームワークには Resources など決まっている
  • コード以外のものをフレームワークには含めることができる

https://dev.classmethod.jp/articles/iosdc2019-report-library-link-import/

さしもんさしもん

ココナラiOSプロジェクトは、以下の単位で分割・依存関係を持っています。
App: ViewControllerとPresenter、セルなどの画面固有UIコンポーネント
CustomView: 汎用のカスタムUIコンポーネント
Core: ビジネスロジックを提供するUseCaseとRepository
API: モデル(DTO)と、APIへのリクエスト/レスポンスを行うロジック
参照: https://yomoyamablog.coconala.co.jp/entry/2018/09/05

そうかドメイン層とかビジネスロジックってUseCaseとかRepositoryになるのか

さしもんさしもん

arm64 とか x86_64ってなに?

さしもんさしもん

めっちゃ参考になる

Simulator(x86_64)はビルドできるけど、実機(arm64)はビルドできない...
もしかして、R.framework がx86_64はサポートしているけど、
arm64はサポートしてない的な...という仮説を考えました。

外部フレームワークを組み込むって結構あるしシュミレーターor実機でうまく行かないときにサポートされているのか確認する必要があるのか...

調べ方は
xcrun lipo -info [binary]を使うらしい

さしもんさしもん

FrameworkとLibraryは違う

Embedded Fraworkについて調べているときにstatic libraryとdynami frameworkの話が出てきて
それぞれの違いがわからなかったら調べてたらframeworkとlibraryは違うという記述を見つけて気になったのでしらべた

実際frameworkでlibraryを入れるなんていう記述も見つけたので
どうやらやはりframeworkとlibrary違うらしい

https://www.slideshare.net/syoikeda/iosframework

さしもんさしもん

Framework(.framework)とLibraryの違い

  • FrameworkはBundleの一種?
  • FrameworkがLibraryを内包する
  • Libraryは画像やXibなどのリソースを含むことができないがFrameworkはできる
さしもんさしもん

Static LibraryとDynamic Libraryのちがい

StaticLibrary(xxxx.a)

  • コンパイル時に静的に組み込まれるライブラリ
  • コンパイル時に組み込まれる形でリンクされるため、その分アプリケーションのサイズは大きくなる

DynamicLibrary(xxxx.dylib)

iOS,macOS のシステムライブラリ (UIKit, Foundation 等) は Dynamic Library として提供されている
そのため、アプリの更新をしなくともシステムライブラリの更新が行えるようになっている
https://qiita.com/tasuwo/items/be8188c3645801a00252

static libraryについてはこれもわかりやすく書かれている
static frameworkはどうやらリソースをもてないらしい
https://www.slideshare.net/asakahara/ios-63546954

さしもんさしもん

DynamicLibraryのランタイム時に実行されるの意味がわからなかったけど
この図がとてもわかりやすい

As you can see in the above diagram, static and dynamic libraries run differently. A static library will actually be copied into your program during compilation, whereas a dynamic library stays separate and is linked at the time a program runs. This implies that a static library will run faster, as a program with a static library won’t need to go find another object in memory during runtime. However, as detailed above, dynamic libraries are more modular, and you won’t have to recompile it all the time. Whether you use one or the other depends on what you’re developing. Now go make one!
David Knoppers
Follow
参照: https://medium.com/@davidknoppers/static-and-dynamic-libraries-in-linux-ae4440458506

Static Libraryに関してはコンパイル時にオブジェクトファイルに組み込まれる形でLinkされる
クリーンビルド時に時間がかかる理由はこれ

一方でdynamic Libraryの場合はStatic Libraryとちがってコンパイル時にそういったLinkを行わずに実行時(ランタイム時)に参照される形でLinkされるのでビルド自体は早い
Carthageが早い理由もこれ。たしかdynamic libraryだし
書いたとおり実行時(ランタイム時)にLinkされるので当然dynamic libraryが多いほど起動速度が遅くなる

さしもんさしもん

static frameworkはframeworkではあるけど実態は単なるstatic libraryだから、dynamic framework画像などのリソースをもつことができないっぽい
embedded frameworkにすれば、リソースをもたせられるからそれのほうが良さそう

さしもんさしもん

embedded frameworkってdynamicとstaticのどっちよりなんだろうか
importして使うからstaticよりなんだろうか
embedded framworkを使いすぎると起動速度が遅くなるっていうからdynamicよりなんだろうか

さしもんさしもん

モジュールもなんとなくで使っていたけど
きちんと定義している記事をみつけたからメモ

モジュールとは?
モジュールとは、ソースコードを外部に公開する場合の基本単位であり、その実態とも言えるものとして、公開べきシンボル (クラスや関数等) を定義したモジュールファイルがあります。異なるソースファイル間でインタフェースを共有するための仕組みという点では、C 言語ファミリーにおけるヘッダーファイルと似ています。実際、Objective-C におけるモジュールは、ヘッダーファイルの上位に位置する概念と言えます。モジュールという概念自体は Swift、Objective-C のどちらにも共通して存在していますが、利用されるモジュールファイルのフォーマットは異なっていて、主に Swift (swiftc) の世界で利用される .swiftmodule ファイルと、主に Objective-C (clang) の世界で利用される .modulemap ファイルの二種類が存在しています。
https://qiita.com/tasuwo/items/be8188c3645801a00252

さしもんさしもん

ポインタとは

メモリ上のデータにアクセスする時に使うもので
プログラミングでは、変数や関数などが置かれたメインメモリ上の番地などを格納する特殊な変数のことをポインタというらしい

さしもんさしもん

用途

例えばarraySliceは新規にメモリの確保を行わず、元の配列を参照しながら始点のindexとCountを保持しているらしい

元の配列を参照しながら 始点のindexとCountを保持していると考えられます

The ArraySlice type makes it fast and efficient for you to perform operations on sections of a larger array. Instead of copying over the elements of a slice to new storage, an ArraySlice instance presents a view onto the storage of a larger array. And because ArraySlice presents the same interface as Array, you can generally perform the same operations on a slice as you could on the original array.

これを確認してみると
ポインタに格納されているメモリのアドレスが同じであることがわかるので
確かに新規にメモリの確保を行わずに、元の配列を参照していることがわかる

さしもんさしもん

ただし配列の部分配列を取得してそこに値を追加するとアドレスは変わり、新規のメモリ領域になるみたい
この点は参照渡しの理屈とは少し違うみたい
参照渡しであれば一方に変更を加えれば三唱元が同じである全変数にその変更が反映されるので

さしもんさしもん

echo 'hogehoge' >> ~/.zshrc
sourcd ~/.zshrc
で.zshrcファイルに''の中身を書いてくれるっぽい

さしもんさしもん

cocoapods関連メモ

さしもんさしもん

use_framework!気にせず使っていたけど、公式によればxcode9,cocoaPods1.4.0まではSwiftをstatic libraryにビルドすることがサポートされてなくて、たくさんのダイナミックバイナリをリンクすることによる起動時のパフォーマンスが懸念されていた

Up until Xcode 9, support for building Swift into static libraries was non-existent and use of dynamic frameworks was required. This was a deal-breaker for some developers, particularly those worried about the launch performance implications of linked many dynamic binaries.

そういう背景があってstatic libraryを使えるようになることが求められた
その結果cocoaPods1.5.0からSwiftを使うPodsをインストールするためにpodfileにuse_frameworks!を書く必要がなくなった

With CocoaPods 1.5.0, developers are no longer restricted into specifying use_frameworks! in their Podfile in order to install pods that use Swift.

dynamic frameworkではなくstatic libraryを使う場合は use_frameworks!を外すか、あるいはuse_frameworks! :linkage => :staticみたいな書き方をするらしい(ダイナミックリンクならuse_frameworks! :linkage => :dynamicと書くこともできる)
参照: https://guides.cocoapods.org/syntax/podfile.html#use_frameworks_bang

実際dynamic linkしたものはPods Project>Target>combined>Linking>Mach-O Typeを見てみるとDyanmic Libraryに
static linkしたものもPods Project>Target>combined>Linking>Mach-O Typeを見てみるとStatic Libraryにぞれぞれなっている


参照: https://llcc.hatenablog.com/entry/2020/09/10/123552

*ただ注意なのがlink方法の設定はtarget共通になるのでdynamic linkしか対応してないものがあるとpod installでエラーになるはず

追記

Swift で書かれた Pods(ライブラリ)は、frameworks としてのみ統合できるとの事で、use_frameworks! の行のコメント(#)を外すことを忘れないでください。
参照: https://dev.classmethod.jp/articles/cocoapods/

さしもんさしもん

*知らなかった

  • Xcodeプロジェクトでビルドを行うと Productsフォルダからビルドによる成果物にアクセスができるようになる
  • そこからxxx.app>show in Finder >Show Package Contentsで実行ファイルにアクセスできる
  • そもそも実行ファイルがなんなのかもわかってなかった
  • ターミナルでこれを otool -LにわたすとDynamic Frameworkがログに出力してくれることやfileコマンドを使えばアーキテクチャ?を教えてくれる
  • Mach-O(マーク・オー)がコンパイラが生成するオブジェクトファイルおよび実行ファイルのファイルフォーマットである
    参照: https://medium.com/eureka-engineering/create-merged-framework-to-cut-appstartuptime-72ee67b2bbab ← めちゃくちゃ勉強になった
  • DYLD_PRINT_STATISTICSを設定するとDynamic Frameworkの読み込み時間が図れるの知らなかった
    参照: https://llcc.hatenablog.com/entry/2020/09/10/123552
  • Find Implicit Dependencies
    参照: https://medium.com/@bj1024/cocoapods-swift-compiler-error-70626b3f01bf

  • アプリケーション内でCocoapodsに依存したprojectを使用するにはpodspecファイルを持つ必要があり、以下の様に作成します。podspecがよくわからないという方はこちらを参照してください。(英語)Cocoapods: Creating a Pod Spec
    参照: https://qiita.com/InletOrder/items/fa1cb26f8450fc45468d

さしもんさしもん

余談
また別記事(これこれ)ではcocoapods0.35.0まではobjCで書かれたpodsのみをサポートとしていたところから、swiftの登場とcocoaPods0.36.0からdynamic frameworkのサポートが始まったことについても書かれている

さしもんさしもん

ターゲット(親)とテストターゲット(子)両方に共通のライブラリを持たせたい時には以下のようにターゲットの方に書くだけでおけ
親で書いたものが子側にもきちんと適用されてる
*スコープを見れば当たり前だと気づくけど

target 'Parent' do
  pod 'HogeHoge'

  target 'ParentTests' do
  end
end
さしもんさしもん

xcrun
Xcodeコマンド
例えば
xcrun --sdk macosx はmaxOS上で何かを実行することを意味する
使い方としてはmintでビルドするツールはmacOS上で動かすので
xcrun --sdk macosx mint run swiftlint みたいな感じらしい
https://twitter.com/kateinoigakukun/status/1331091750891372546

さしもんさしもん

iOSのViewのライフサイクル
https://qiita.com/shtnkgm/items/f133f73baaa71172efb2
https://medium.com/eureka-engineering/iosエンジニア必見-iosのレイアウトで押さえておきたいこと-総集編-b25342566ccc

AutoLayoutを使用している場合はsuper.layoutSubviewsを最初に呼び出す必要があります。(しないとクラッシュする) super.layoutSubviewsを最初に呼び出すことによって、制約をもとにサブビューのframeが決定され、操作できるようになります。

どんな時にlayoutSubviews()をオーバーライドするか
https://snoozelag.hatenablog.com/entry/2017/05/22/181048

この人の記事面白いかも(この記事は書いてあることが日本語記事とあまり変わらないけど
https://www.vadimbulavin.com/view-auto-layout-life-cycle/

さしもんさしもん

恥ずかしながら

実際のシステムではもっと多くのディレクトリとファイルが存在しますが、ここでは主要なものだけ

/
|-- Applications (アプリケーションがインストールされるディレクトリ)
|-- Library
|-- System
|-- Users
|-- [ユーザー名] (ユーザーのホームディレクトリ)
|-- Desktop
|-- Documents
|-- Downloads
|-- Music
|-- Pictures
|-- Public

ターミナルを開いたとき、デフォルトのワーキングディレクトリはユーザーのホームディレクトリです(上記の例でいうと/Users/[ユーザー名])。したがって、ここから相対パスでディレクトリを指定する場合、そのパスはホームディレクトリからのパスになります。例えば、cd Documentsとすると/Users/[ユーザー名]/Documentsディレクトリに移動します。

一方、絶対パスを使うと、そのパスはルートディレクトリ(/)からのパスになります。例えば、cd /Applicationsとすると、ルート直下の/Applicationsディレクトリに移動します。これはホームディレクトリからではなく、システム全体から見た場所を指しています。

macOSでは、ルート直下の/Applicationsディレクトリにアプリケーションがインストールされます。しかし、ユーザーのホームディレクトリには通常Applicationsディレクトリは存在しないため、cd Applicationsを実行するとエラーになります(存在しないディレクトリに移動しようとしたため)。

さしもんさしもん

WebRTC

  • 概要
  • 基本2ステップ
  • NATを利用する場合
  • セキュアなFWを利用する場合

参照: https://www.youtube.com/watch?v=kJoyZk2XMgc

さしもんさしもん

概要

Web Real-Time Communicationの略
Webベース(webブラウザ)で動画や音声をやりとりするために開発された技術で、Webブラウザを使ってP2P通信するための仕組みが提供されている

*P2Pとは
pc同士がサーバーを介さずに通信をする方法のこと
サーバークライアントでの通信方式とは違ってサーバーを介さずに通信をおこなう
なのでサーバーの影響が受けにくくなる。またピア同士が通信をするのでサーバーの負荷を下げられるなどといったメリットがある

さしもんさしもん

基本的な流れ

通信相手の特定

  1. 通信をするために端末はSDPというものを作る
SDPというのはSession Description Protocolの略
通信に必要なIPアドレス、ポート番号、暗号通信するための情報などを含む
  1. それぞれの端末でSDPを作ったらシグナリングサーバーを介してそれぞれのSDPを交換する
  2. このSDPを交換することで、SDPに含まれているIPアドレスなどから通信相手を特定できる

つまり、P2Pとは言っても全くサーバーを介さないわけではなく、通信相手であるピアを特定するためにシグナリングサーバーが必要になる

通信の開始

通信はP2Pでの暗号通信が行われる
やりとりされるデータはSDPで共有した暗号方式を使って暗号通信が行われるためインターネットを経由した通信でも安全性が確保される


*WebRTCはリアルタイム性が求められるためTCPではなくUDPを利用することが多い

さしもんさしもん

ここまでが基本的な内容以降ちょっと発展版

NATを利用する場合

インターネットを経由して通信するにはNATを使う場合がほとんどで、NATを利用する場合はNAT超えという課題がある

というのはNAT配下にあるブラウザは自身が利用しているグローバルIPアドレスを把握する仕様になっていない
通信相手を特定するためにはグローバルIPが必要
そこでSTUN(*Session Traversal Utilities for NAT)サーバーを利用する

STUNサーバーにアクセスすると利用しているグローバルIPアドレスを教えてくれる

STUNサーバーからグローバルIPの返答 グローバルIPを元にSDPの作成

この教えてもらったグローバルIPを含んだSDPを作ることで基本的な流れに書いたように通信相手の特定ができるようになる

ここに記載した通り、インターネットを経由してP2P通信をするほとんどの場合NATが機能しているため、STUNサーバーへアクセスできることが大切な要素となる

さしもんさしもん

それ以外にも注意点があり、その一つがセキュアなFW(ファイアウォール)を利用する場合

セキュアなFWを利用する場合

FWでP2Pが禁止されていたりUDPが禁止されている場合、相手を特定できても通信できない場合がある

その場合にはTURNサーバーを使って通信をリレーしてもらう
TURNサーバーとTCPを利用することでFWによるP2Pの禁止やUDPの禁止を通過できる
というのもTURNサーバーにデータを送ると、TURNサーバーが相手にデータを送ってくれるから

TURNサーバーは暗号化されたデータを中継するだけなので、データの中身を見ることはできない。なのでデータがTURNサーバーのところで見られてしまうんじゃないかという心配は不要

さしもんさしもん

仕組みについてよくまとまってる
https://zenn.dev/faucon/articles/085ab8e420b432

FlutterでwebRTCを実装す場合の流れ(参照: https://www.100ms.live/blog/flutter-webrtc#how-webrtc-works)

This local data then needs to be shared with the remote user. Following this, the steps below are executed to initiate a connection:

  • The local user creates an offer which is essentially an SDP session description.
  • This offer is stringified and sent over to a remote user.
  • The remote user sets its remote description to the obtained offer and sends back an answer.
  • The answer is used to set a remote description of the local user.
  • Connection is established between the local and the remote user.

よくわかってないのはanswerをタップするとcandidateの情報が取得できるのかな?
その取得したcandidate情報をlocalユーザーのtextFieldにペーストしてsetCandidateなら、これはおそらくlocalの相手としてremoteが設定される感じになるからわかる
→ コードを見た感じそれっぽそう

さしもんさしもん

UDPソケット通信

さしもんさしもん

UDPソケット通信とは

UDP(User Datagram Protocol)ソケット通信

UDPとは: インターネット上でデータを送受信するための一つの方法で、UDPはTCP(transmission Control Protocol)と並ぶ、主要なトランスポート層プロトコルの一つ

ソケットとは: ネットワーク通信を行うためのエンドポイントのこと。エンドポイントとも呼ばれる。通信を行うためのインターフェースを提供する。

UDPソケットとは: UDPプロトコルをを使用してデータを送受信するためのソケット。プログラミング言語によっては、このUDPソケットを使って簡単にネットワーク通信のプログラムを作成できる

ソケット通信とは: ネットワーク上の異なるコンピュータやプロセス間でのデータの送受信を行う手法の一つ。TCPやUDPといったトランスポート層のプロトコルを使用して、データのやり取りが行われる

UDPソケット通信はUDPプロトコルの「速度が速い」「100%の信頼性があるわけではない」という特性上、速度が必要だけれども、100%の信頼性が必要でない場合に適している

UDPの特徴

コネクションレスなプロトコルなので、データの送受信がとてもシンプル
そのほかTCPと比べてUDPは以下のような特徴がある

  • 速度が速い: UDPは、データを送る前に接続の確率(ハンドシェイク)が不要。そのため低霊天使でデータを遅れる
  • 信頼性が低い: UDPは、データの到達を保証しないため、データが失われる可能性がある
  • 順序が保証されない: 送られたデータが目的に到達する順序は保証されない
  • オーバーヘッドが少ない: ヘッダ情報が少ないため、データ転送に必要なバンドウィズドが少なくすむ

ユースケース

速度が速いという特性上、高速な反応の求められるアプリケーション、例えばリアルタイムなストリーミングやゲーム、VoIP(Voice over Ip)などで利用される

結論

以上の通りUDPソケット通信とは速度が速いけど信頼性が低いという特性もつUDPプロトコル上で行われるソケット通信のこと

さしもんさしもん

ソケットについてもう少し...

ソケットとは

ソケットは通信エンドポイントを表現するプログラミングの抽象概念
ネットワーク上の2つの異なるコンピュータが通信するためには、それぞれのソケットが必要です。ソケットは特定のIPアドレスとポート番号にバインド(紐付け)されます。

もっとわかりやすく

ソケットというのは、コンピュータ同士がネットワークを通じてデータをやり取りする際の「通信の窓口」みたいなもの。例えば、あなたがインターネット上のWebサイトにアクセスする際、あなたのコンピュータとWebサーバーはそれぞれソケットと呼ばれるこの「窓口」を開いて通信を行います。

特定のソケットは、IPアドレスとポート番号という二つの要素で一意に識別されます。

  • IPアドレス: これはコンピュータのネットワーク上での「住所」のようなものです。
  • ポート番号: これはその「住所」内での「部屋番号」のようなものです。
    この2つを組み合わせることで、ネットワーク上の特定のコンピュータの特定のアプリケーションとデータをやり取りすることができます。

たとえば、あるWebサーバーがIPアドレス「192.168.1.1」、ポート「80」で動いていたとします。この場合、そのサーバーへアクセスする際のソケットは「192.168.1.1:80」という形で表現されます。

このように、ソケットはネットワーク上でデータを送受信する基本的な仕組みの一つです。それがソケットです。

そのほかの通信手法とその特徴

通信手法 説明 プロセスの違い
ソケット通信 (TCP/UDP) ネットワーク層で動作し、低レベルのデータ送受信が可能。 - 初期設定: ソケットを明示的に開き、バインド(サーバー)や接続(クライアント)を行います。
- データ転送: 通常、バイトストリームまたはデータグラムとして生のデータを送受信します。
- 手動制御: 通常、エラーハンドリング、再送、接続の切断などを手動で行う場合が多い。
- 低レベル: アプリケーションプロトコルを自分で定義する場合が多く、制御が柔軟です。
HTTP/HTTPS Webブラウザとサーバー間でよく用いられる。テキストやメディアを転送する際に主に使用される。 - 初期設定: 通常、URLを指定してリクエストを送るだけで、裏側でソケットが扱われます。
- データ転送: リクエストとレスポンスが高レベルのオブジェクトとして扱われます。
- 自動制御: 再接続、リダイレクト、エラーハンドリングなどが自動で行われる場合が多い。
- 高レベル: HTTPメソッド(GET, POSTなど)とステータスコード(200 OK, 404 Not Foundなど)が定義されています。
Bluetooth モバイルデバイスやパソコン、周辺機器との通信に使われる。距離は数メートルから数十メートル。 - 初期設定: デバイスの探索とペアリングが必要です。
- データ転送: 通常、専用のプロファイル(HFP, A2DPなど)を用いて高レベルのデータ交換を行います。
- 自動制御: 再接続やエラーハンドリングは多くの場合自動です。
- 中-高レベル: 用途に応じたプロファイルが存在し、それに基づいて通信を行います。
NFC 非常に短い距離(数センチ)で動作し、主に決済やID認証に使用される。 - 初期設定: 物理的な接触(タップ)が必要です。
- データ転送: 非常に短いデータやコマンドが交換されます。
- 自動制御: 通常、エラーハンドリングや再送はほとんど必要ありません。
- 高レベル: NFCタグの種類やデータフォーマットが高度に規定されています。

ちなみに ターミナルでTCP/UDP(osc)ネットワーク通信(Mac) を参考にして発信機ターミナルから受信機アプリ(シミュレーター)にデータ送れた

$ echo "hoge" | nc -u 127.0.0.1 8888

逆に発信機アプリ(シミュレーター)から受信機ターミナルへの送信して受信側でレスポンス返すのも以下でできた

$ (echo "HTTP/1.0 200 Ok"; echo; echo "Hello World") | nc -l 8080(任意のport番号)

だけど while true; do (echo "HTTP/1.0 200 Ok"; echo; echo "Hello World") | nc -l 8080; done を実行しても受信するたびに連続でレスポンスを返すのはできなかったからpythonでプログラムをpyファイルに書いてローカルサーバー(受信機)立てて対応した

以下をターミナルで python3 ファイル名で実行

import socket

HOST = "127.0.0.1"
PORT = 8889

# UDPソケットの作成
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((HOST, PORT))

 # print(f"Listening at {HOST}:{PORT}")

while True:
    # データの受信とレスポンスの送信
    data, addr = sock.recvfrom(1024)
    # print(f"Received message: {data.decode('utf-8')} from {addr}")
    print(f"Received message: {data.decode('utf-8')}")
    response = b"HTTP/1.0 200 Ok\n\nHello World"
    sock.sendto(response, addr)

さしもんさしもん

UDPソケットつシンを行うためのプロセス

UDPソケット通信を行う基本的なプロセスは以下の通り

サーバー側

  1. ソケットの作成: UDPソケットを作成
  2. ポートのバインド: 特定のIPアドレスとポート番号にソケットをバインドする。このポートは、クライアントがデータを送信する際の目的地となる
  3. データの受信: バインドが完了したら、サーバーはデータを受信する準備ができる。なのでUDPでは受信待ちの状態になる
  4. データの処理: データを受信すると、サーバーはそのデータに対してなんらかの処理を行う。これはアプリによって異なる
  5. ソケットのクローズ:通信が終了したら、ソケット閉じる。ただしUDPはコネクションレスなので必ずしも閉じる必要はない

クライアント側

  1. ソケットの作成: クライアントもサーバー同様UDPソケットを作成する
  2. データの送信: サーバーのIPアドレスとポート番号を指定して、データを送信します
  3. データの受信: 必要に応じて、サーバーからのデータを受信する
  4. ソケットのクローズ:通信が終了したらソケットを閉じる
さしもんさしもん

Datagramとは

  • 通信ネットワークにおける送受信単位となる、port番号やアドレスなどの情報を持つデータの小さなまとまり
  • ネットワーク層やトランスポート層の一部のプロトコルにおける送受信単位を指すことが多い
  • UDPでも使用されるデータパケットの一種
さしもんさしもん

非同期処理はプラットフォームや技術によって異なる

*Swiftだけを触ってた時は勝手にスレッド切り替えてくれてサブスレッドで処理。結果メインスレッドを邪魔なしない。という印象だったけどDartを触っていたら少し違うようで気になった
*ただし基本的な目的は、メインスレッドやメインプロセスがブロックされることなく、並行して他のタスクを実行できるようにすること

さしもんさしもん

非同期処理の種類

  • スレッドベースの非同期処理
    新しいスレッドを生成し、そのスレッド上でタスクを実行するものです。JavaのThreadやC++のstd::threadなどが該当
    ex) swift
  • イベントループベースの非同期処理
    イベントループを使用して、非同期タスクをキューに入れて順に実行する方法
    この方法では、新しいスレッドを生成しないことも多い
    ex) dart
さしもんさしもん

イベントループとは

名前の通り「ループ」で、そのループの中で「イベント」や「タスク」を順に処理していく仕組み
以下の手順で動作する:

  1. イベントやタスクがキューに入れられる。
  2. イベントループはキューからイベントやタスクを一つ取り出す。
  3. 取り出したイベントやタスクを実行する。
  4. 実行が終了したら、再度キューにタスクがあるか確認する。
  5. タスクがあれば2に戻る。なければ終了。

このような流れを使って、例えばI/O操作(ファイル読み込みやネットワーク通信など)を非同期で実行する場合、実際のI/O操作が終了するまで他のタスクを実行することができる。そしてI/O操作が終了した時点で、その結果に関連するコールバックやタスクがキューに追加され、それが順に実行されるという流れになる

これを読むと改めて以下の例もよくわかる

まず前提として、並行処理(Concurrency)と並列処理(Parallelism)は異なります。
ほとんどのFlutterアプリケーションでは、Main Isolateと呼ばれる1つのスレッド上でほとんどの処理が実行されます。Main Isolateは独自のCPUとメモリヒープを確保しており、その中で各イベントを回すイベントサイクル(ループ)形式を取っています。

Concurrency in Dart
ここで、1つのアプリをAさんという一人の人間に置き換えて凄く簡単に例えます。
Aさんは仕事をしています。基本的にAさんは一つの事しか出来ません。しかし、キーボードを打ちながらPCの時計をちらっと見たり、後輩にある仕事の依頼をしていて待っている間に別の作業は出来ます。ですが、キーボードを打ちながら昼食を買いに行くことは出来ませんし、着替えながら何か考え事をするというのもの出来ません。
PCの時計をチラッと見たり、後輩へ依頼した仕事の待ち時間を他の作業で消費するというのは、FutureやStreamにあたる並行処理(Concurrency)です。そして、キーボードを打ちながら昼食を買いに行く、着替えながら何か考え事をするというのは並列処理(Parallelism)にあたります。

参照: https://zenn.dev/urasan/articles/f6613470658de1#dartにおけるconcurrency-と-parallelism

自分なりの理解をかくと、イベントループを使った非同期処理というのは、「処理が終わるのを待つ時間」を他の処理で有効に使う。
言い方を変えれば、非同期処理をする際には待ち時間がしばしば発生する(例:ネットワークからデータを取得する時間、ディスクからファイルを読む時間など)。この待ち時間を、他の処理(例:ユーザーの入力を受け付ける、画面の描画を更新するなど)に使うというやり方
例えば、awaitキーワードを使ったDartの非同期処理では、awaitの後の処理が終わるまでの間に他のイベント(ユーザーからの入力、画面描画の更新など)が処理される

Future<void> fetchData() async {
  // この処理が時間がかかるとして...
  var data = await fetchSomeData(); 

  // ...この間に他のイベント(ユーザー入力、画面の描画更新など)が処理されます

  // データが取得できたらUIを更新
  updateUI(data); 
}

もっと砕けた言い方をすれば「待つ」時間を有効に使うことで、シングルスレッドでありながらもUIを滑らかに保つというやり方。非同期処理をしている待ち時間でメインスレッドの処理ができるってこと。うまくスケジューリングして空き時間をうまく利用することでUIを滑らかにしてる

  1. ユーザーがボタンをクリックして、データのダウンロードをリクエストする。
  2. 非同期関数が呼び出され、データのダウンロードが開始される。
  3. ダウンロードが完了するまでの「待ち時間」に、メインスレッドは他のイベント(ユーザーが別のボタンをクリックする、スクロールするなど)を処理できる。
  4. ダウンロードが完了したら、非同期関数が再びアクティブになり、UIを更新。

このように、非同期処理を使うことで、待ち時間を有効に活用してUIのレスポンスを高く保つことができる

イベントループは以下のようなステップで動作

  1. イベント(ユーザー入力、タイマー、IO操作の完了通知など)がキューに追加される。
  2. イベントループはキューからイベントを一つずつ取り出し、それに対応するコールバック関数(またはプロミスの then メソッド、Dartの場合は Future の完了ハンドラ等)を呼び出す。
  3. コールバック関数が非同期処理を開始した場合、その「終了通知」がキューに追加される。
  4. 非同期処理が終了すると、「終了通知」がキューから取り出され、次に行うべき処理(例:UIの更新)が行われる。

この一連の流れによって、イベントループはシングルスレッドの中で非同期処理とその他のタスク(UI更新、ユーザー入力の処理など)を効率よく行うことができる。結果として、メインスレッドが「ブロック」されることなく、スムーズなユーザー体験を提供できる。

考察

Dartにおける非同期処理はスキマ時間をうまくスケジューリングしたやり方のようなので
非同期処理の中身が単純な処理だとしてもかなり高頻度で行われるような処理の場合は
それによって生成される「終了通知」やコールバックがイベントキューに積まれていくことになるので
キューが詰まってしまうためスキマ時間が短くなり、UIの処理が遅れる可能性がある
なのでそのような場合はIsolateなどを使ってサブスレッドで実行されるようにした方がいいかもしれない

さしもんさしもん

恥ずかしながら

buffer

バッファ。またはバッファリングとも呼ばれる
データを一時的に保存するために使用されるメモリ領域のこと
データ処理やデータ転送の間にデータの流れを管理する役割

さしもんさしもん

ChatGPTに聞いてみた

プログラミングとコンピューターサイエンスのコンテキストで、バッファは以下のようないくつかの異なる目的で使用されます:

IO(入出力)の効率を高める:

データを小さい塊で頻繁に読み書きするよりも、大きい塊で読み書きする方が効率的な場合があります。バッファリングを使用すると、データを一定量集めてから一度に書き込む(または送信する)ことが可能になります。

デバイスの速度差を吸収する:

異なる速度で動作するデバイス間でデータを転送する場合、バッファは速度差を吸収し、データの欠損を防ぐ役割を果たします。

データの安全性を確保する:

バッファオーバーフローというセキュリティ上の問題があります。これはバッファに割り当てられたメモリ領域を超えてデータを書き込むことで発生します。この問題を避けるために、プログラムはバッファに書き込むデータの量を適切に制御する必要があります。

データの変換や処理を支援する:

バッファはデータ変換や処理の間にデータを一時的に保存するためにも使用されます。たとえば、ネットワークを通じて受信したデータを解析し、適切なフォーマットに変換する前に一時的にデータをバッファに保存します。

バッファリングがデータの処理や転送を助ける一方で、その管理も重要であり、不適切なバッファの管理はパフォーマンスの低下やセキュリティの問題を引き起こす可能性があります

さしもんさしもん

恥ずかしながら

ブロードバンド回線

特定の回線を指す言葉ではなく、高速で広帯域の通信が可能な回線の総称として使われる
具体的には、以下のような回線技術がブロードバンドに分類されます:

  • DSL (Digital Subscriber Line): 既存の電話回線を高速化したもの。ADSLやVDSLなどの派生技術がある
  • 光ファイバー: 光の信号を利用して高速通信を実現する回線。
  • ケーブルインターネット: ケーブルテレビの回線を利用したインターネット接続。
  • 衛星インターネット: 衛星を利用した高速通信。
  • モバイルネットワーク (特に4G以降): 携帯電話の通信ネットワークを利用した高速インターネット接続。
さしもんさしもん

BFFとは

さしもんさしもん

BFF とは、Backend For Frontendの略称で、「フロントエンドとバックエンドの中間に配置され双方の複雑な処理を緩和させる責務を持つアーキテクチャ設計パターン」のことです。これだけだと分かりづらいので簡単にまとめると、「バックエンドの API から取得したデータをフロントエンド向けに加工するフロントエンド専用のサーバー(API Gateway)」です。

https://zenn.dev/overflow_offers/articles/20220418-what-is-bff-architecture

さしもんさしもん

上の説明で簡単にいうとBFFがどういうものなのかはわかったけどもう少し詳しく概念を理解したいから別の記事を読んでみた
https://techblog.zozo.com/entry/zozo-aggregation-api-bff
https://atmarkit.itmedia.co.jp/ait/articles/1803/12/news012.html
https://qiita.com/souhei-etou/items/d5de99bb8cba1c59d393
https://tsd.mitsue.co.jp/blog/2021-10-28-event-report-aws-innovate-modern-app-edition-bff/

メリット

  • クライアント<->バックエンドの時に呼び出すAPIの本数が多くてパフォーマンスに問題があれば、BFFを立ててクライアントの呼び出しでBFFはある程度まとめてバックエンドのAPIを呼び出してその結果をまとめて返せる
  • モバイル、webなどのクライアントが多数ある場合にそれぞれ向けにBFFを建てることで最適なAPIレスポンスをそれぞれに対して返すことができる
  • クライアント側GraphQL、バックエンドRestfulみたいなこともできる
    とかかな

より詳細な内容

https://samnewman.io/patterns/architectural/bff/

さしもんさしもん

恥ずかしながら...

API サーバーは同一のクライアントからの大量のリクエストが短時間であった時にリクエストを受け付けなくなることが多いです。
したがって、アプリケーションを組む側としては、一度に大量のリクエストを送らなくても済むような設計にすべきです。もしまとめてデータをとって動かすようなアプリを作りたいのなら、ストリームなり一括取得なりの API をサーバー側に用意すべきです。しかしこれは API サーバーの設計も自ら行なっている場合の選択肢であり、こういった善意の下に使用させていただく API にそれを求めるべきではありません。そこは API でできることの範疇で考えるのが礼儀です。

さしもんさしもん

恥ずかしながらわかってなかったから調べたけど似たような人がいた
https://x.com/the_uhooi/status/1461152126076215296?s=20

Formatter: コード整形
Linter: 静的解析

俺もこの理解だったけどSwiftlintは修正もやってくれないっけ?って思って調べたのがきっかけでやっぱりそうなのかも

Swift だと SwiftLint がまさにそうです。
Formatter と Linter の境界がわからなくなっているのはそれが理由かもしれませんね…