[Swift] サーバ通信
- あるURLにアクセスした時、サーバからどういうデータが返されるかは、アプリサイドは受け取るまでわからない。
- サーバーと通信するためには、いくつかのプロトコルがある。
- 色々あるが http(s) でやるのが主流
- ざっくり、何をしているのかというと、http(s)リクエストを送って、レスポンスを返してもらう という感じのことをしている。ここは、なるべくシンプルに捉えるようにすること。
- サーバ通信には 同期通信 と 非同期通信 がある。
- 同期通信は、サーバのレスポンスが帰ってくるまで後続処理を待つ(融通が効かないイメージ)
- 非同期は、レスポンスを待たずに後続処理を行い、帰ってきた時点で割り込み処理を行い、裁く。
- URLSessionというFoundationのクラスを使ってHTTP通信を行う時は、自動的に非同期になる
- これがGitHubのAPI
- q=の後に何か入れてブラウザで叩いてみると、下記のようなものが返ってくる。
- 下記はSwiftで受け取るコード
func searchGithubUser(query: String) {
//Githubのユーザー名検索結果を表示する
}
- URLRequestというclassを使って、httpリクエストをつくる。これは全部非同期でよしなに処理してくれるclass。
- これはhttpプロトコルで定められた形なので、好きに改変できない。
- リクエストの実態は下記。
GET / HTTP/1.1
Host: qiita.com
User-Agent: curl/7.54.0
Accept: */*
- 全て正しく記述できるのなら、別にclassを使わずともリクエストすることもできる。
- とはいえ生成するためのclassを利用した方が、安全であるし、複雑なリクエストにも対応できるので、積極的に使うようにする。
func searchGithubUser(query: String) {
let url = URL(string: "https://api.github.com/search/users?q=" + query)!
let request = URLRequest(url: url)
}
- URLRequestというクラスは、urlオブジェクトを渡すと、上記のhttpリクエストを生成してくれる。
- 上記コードにおいては、URL変換でforceUnwrappしているため、query部分に全角文字指定されるとクラッシュしてしまう点に留意する。
- URLRequestはデフォルトではGETメソッドになっている。
- いまいちhttpレスポンスとか言われてもピンとこないのでもうちょっと調べた。
- httpはアプリケーション層(具体的なシステムやサービスに必要な機能を実装するための層)におけるプロトコル。
アプリケーション層は最上位の階層で、利用者が操作するソフトウェアが提供する具体的な機能についての仕様や通信手順、データ形式などを定めている。
https://e-words.jp/w/アプリケーション層.html#:~:text=アプリケーション層とは、通信,実装するための層。
curl --http1.1 --get -v https://qiita.com
リクエストを送る
レスポンス
- 1行目の
GET / HTTP/1.1
はリクエストラインと呼ばれる部分 - GETがHTTPメソッド、/がリクエスト先のURIを表している。/
- /HTTP/がプロトコルのバージョン
- 2行目以降をヘッダーと呼ぶ。
- ヘッダーは、リクエストを送信したクライアントに関する追加情報(Host: qiita.com
User-Agent: curl/7.54.0
Accept: */*のこと)をサーバに送信するために存在している。 - ヘッダーは色々種類があり、上記のものはあくまでも一例。
-
ヘッダーの内容をもう少し細かにみていく。
-
Host ではドメイン名と任意でサーバーが開いているTCP Port番号を指定することができる。
-
TCP Portは、TCPプロトコルに従っているポート(ネットワークとパソコンを仲介している)。
-
TCPは送ったデータが相手に届いたか、その都度確認しながら通信する。
https://wa3.i-3-i.info/word18121.html -
バーチャルホストという仕組みが大きく関係しているらしい。
バーチャルホストとは、一台のサーバで複数のホスト名(ドメイン名)によるサービスを提供すること。Webサーバやメールサーバを複数ドメインで共用したい場合に用いられる。
https://e-words.jp/w/バーチャルホスト.html#:~:text=バーチャルホストとは、一,したい場合に用いられる。
- ちなみにサーバは「役割としてのサーバ」と「機器としてのサーバ」の2パターンあるっぽい。
- 役割としてのサーバ = サービスや機能を提供する役割のコンピュータ。
- Webブラウザ(ソフト)を操作すると、ホームページの置いてあるコンピュータに対してリクエストが送られる。ホームページの置いてあるコンピュータはすなわちサーバのこと。
https://wa3.i-3-i.info/word144.html - IPアドレスはコンピュータに割り当てられる住所
https://wa3.i-3-i.info/word172.html - バーチャルホストのイメージとしては、たくさんのIPアドレスを持っていて、それぞれに異なるドメイン名を対応づけている、あるいは一つのIPアドレスにたくさんのドメイン名を割り当てて、サーバー上でそれぞれのドメインに対応したサービスを提供するものみたいな感じらしい。
- 今回問題となる「Host」は、後者の、1つのIPアドレスに複数のドメイン名を割り当てて、対応したサービスを提供する方式(名前方式)で大事な役割を担っている。
- その役割とは、外部からのアクセスが、どのバーシャルホストに向けたものかを区別するというものである。上記を実現するために指定されるものが、すなわちヘッダーの「Host:」で示しているものである。
- User-Agent ではリクエストを送信したクライアント側(のコンピュータがリクエストを送るにあたり使用したツール?)の情報を入力することができる。
- 情報 = アプリケーションタイプ, OS, ソフトウェアベンダー, ソフトウェアのバーションなど。
- 上記コードではcurlのバージョン7.77.0で送信されたものであることを示している。
curlはURLシンタックスを用いてファイルを送信または受信するコマンドラインツールである。curlはlibcurlを使うため、幅広いインターネットプロトコルをサポートする。
libcurlとはフリーで使いやすいクライアントサイドURL転送ライブラリであり、2013年10月現在、DICT、FILE、FTP、FTPS、GOPHER、HTTP、HTTPS、LDAP、LDAPS、SCP、SFTP、TELNET、TFTPのスキームをサポートしている。
curlはデフォルトでは、取得した出力をシステムによって特定された標準出力に表示するようになっている(通常はターミナル)。 したがってcurl www.example.com
のコマンドを実行すると、ほとんどのシステムではwww.example.comのソースコードがターミナルに表示される。
https://ja.wikipedia.org/wiki/CURL
- Acceptはクライアントが処理することができるcontent-typeをサーバーに伝えるために使用されている。
- ここの
content-type
とは、ボディに含まれているデータの種類のこと。 - 今回は
Accept: */*
と改行コードが入るのみで終わっているが、httpメソッドがGETではなくPOSTだった場合はボディが含まれる。
POST / HTTP/1.1
Host: localhost:18888
Accept: */*
Content-Length: 46
Content-Type: application/x-www-form-urlencoded
User-Agent: curl/7.54.0
title=studying about HTTP&author=koheiyamayama
- リクエストについて記述した。次はレスポンスを読み解く。
- 3つの部品で構成されている。ステータスライン, レスポンスヘッダ(ヘッダ), レスポンスボディ.
- 1行目はステータスラインという。プロトコルのバージョン、数字でステータスコードとそれに付随するテキスト
- HTTP1.1はプロトコルのバージョン、200はステータスコード、OKはそれに付随するテキスト。
- ステータスコードはレスポンスを理解しようとした結果を示す3桁の整数。
- ステータスコード一覧
- 2行目以降はヘッダー。リクエストと同じでレスポンスヘッダーという。
- ステータスラインに書ききれないレスポンスの情報が色々と書いてある。
- Content-Typeレスポンスでは、リクエストに対して返したボディがどのcontentTypeなのかをクライアントに伝えている。
- 例えるならば、人間が拡張子を見てファイルの判別を行うことに似ている。ブラウザはcontentTypeで判別を行う。
Content-Type: text/html; charset=utf-8
- 話を戻す。
-
let request = URLRequest(url: url)
で、上述のhttpリクエストを勝手につくってくれる。 - POSTにしたいときは
request.httpMethod = "POST"
などと指定する必要がある。 - インスタンスを作成するにあたり設定情報を渡す必要がある。
- URLSession.sharedを使うと設定情報なしでセッションを使うことができる。
- このように、複雑なデータを扱いやすくラップしてくれるクラスをラッパークラスをいうらしい。
- リクエストができたので、今度はサーバに投げる必要がある。
- サーバに投げる際はURLSessionクラスを利用する。
func searchGithubUser(query: String) {
let url = URL(string: "https://api.github.com/search/users?q=" + query)!
let request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
//ここにデータ受信後の処理を書く
}
task.resume()
}
//上記処理「let task = URLSession.shared.」について、やっていることは下記と同じ。
var config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session(with: request) { (data, response, error) in
(以下略)
- URLSessionのインスタンスを作成する際に設定情報を流す必要(セッション)がある。
- 「URLSession.shared」までで一つのインスタンス
Singleton パターンとは、そのクラスのインスタンスが1つしか生成されないことを保証するデザインパターンのことである。
https://ja.wikipedia.org/wiki/Singleton_パターン
shared
The shared singleton session object.
class var shared: URLSession { get }
https://developer.apple.com/documentation/foundation/urlsession/1409000-shared
-
dataTask(with: request)
は、URLSessionが持っているメソッドのこと。 - 正確には
dataTask(with: request) { (data, response, error) in
//ここにデータ受信後の処理を書く
}
- までが1メソッド。
func dataTask(with request: URLRequest,
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
- 1つめの引数にHTTPリクエストオブジェクト
- 2つめの引数に「completionHandler」というクロージャをとる。
- 戻り値にURLSessionDataTaskオブジェクトを返すメソッド。
- completionHandlerは通信が終了した後の完了処理をするためのクロージャ.
- 話について行けていないので一旦調べ物。
- まずhttpなどURLから特定されるリソースを非同期で提供する/されるシステムのことを、URLローディングシステムというらしい。
- Swiftにおいては、これを使うためにURLSessionとそれに関連するクラスを使用するらしい。
- URLSessionはネットワーク上のデーター転送処理群(=URLSessionTask)をまとめるclass
- 1つのSessionで繰り返しURLSessionTaskの作成が可能。
- Sessionというのは、一連のインタラクティブな操作のこと。対話。
- URLSessionTaskはURLSessionの下位概念。
- URLから特定されるリソースを実際に取得し、アプリにデータを返却したり、リモートサーバからファイルのダウンロードやアップロードを行う。
- URLSessionConfigurationはURLSessionの設定を提供するclass。上位概念。
- タイムアウト値、キャッシュやクッキーの扱い方、端末回線での接続の許可などが変更できる。
IPプロトコル(ネットワーク層)
- OSI参照モデルで言うところのネットワーク層で使われているプロトコル。
- 地球の裏側のコンピュータとも通信ができるのはこの子のおかげ。
- IPアドレスは住所的なもので、最終到達地点を示す。
- コネクションレス型で、上位階層から指示されたらすぐにIPパケットに詰めて発送する。
- そのため、コネクション型の通信を行いたい場合はより上位層で処理をいい感じにしてあげる必要がある。
- ただ、この子はうまくパケットを届けられなかったとしても再送はしないので、そう言う意味でちょっと不安のある子でもある。
- ICMPIPパケットの配送中になんらかの異常が生じた場合、送信元に異常を知らせる仕組み。
- ARPはパケットの送り先の物理アドレス(MACアドレス)を取得するプロトコル。
- ちなみに、ネットワーク層のプロトコルがIPのように到達性が保証されていないものである場合、「正しくデータを届ける処理」は、上位階層のトランスポート層に託されている
TCP (データリンク層)
- コネクション型(対話するイメージ。)
- ちゃんと届いているかどうか管理してくれるいい子。
- 難点として制御のためのパケット切り替えが多いため、ちょっとのことで使うには無駄が多い。
- また、ネットワーク利用効率を向上させる仕組みが色々組み込まれている関係で、ビデオや音声のような一定感覚で定められた量のデータを送信するような処理には向いていない。
UDP (データリンク層)
- コレクションレス。
- TCPのように、データがちゃんと届いているかどうかなど密な管理はしてくれない。
- だから、この子を使う場合はより上位のアプリケーション層においてちゃんと管理してくれる子を入れておく必要がある。
- 反面、データ量が少なかったり、ブロードキャスト、マルチキャストの送信、メディア再生などには向いている(一定間隔でデータの受け渡しができる。)
ネットワーク層とデータリンク層の関係
- 旅行の日程表がネットワーク層だとすれば、データリンク層は切符や旅行券。
- どこからどういう経路で目的地に行くのかの管理はネットワーク層。
- 対するデータリンク層は特定の1区間のみ補償している。
- また、ネットワーク層にはデータリンク層の性質を抽象化する役割もある。
- 物理層、データリンク層で利用される機器においては、IPアドレスを有する必要はない。
- TCP/IPにおいては、ネットワークの1区間をIPパケットが飛ぶことをホップという。
- 1ホップは送信元MACアドレスと宛先MACアドレスを利用し、フレームが伝達される区間。
- IPのパケット転送はルータに到着する都度宛先IPアドレスが調べられる。