昨天我們講解了 Connection Migration 的概念,今天就來帶著讀者簡單看一下對應的 Source code 實現在哪個 function,讓有興趣的讀者可以繼續往下 trace 細節,大概講一下 ngtcp2 中哪幾個 function 負責做 Connection migration,就可以往下研究了(如果研究出心得記得交流一下)
在 ngtcp2 中 Client 要執行 Connection migration 可以呼叫 ngtcp2_conn_initiate_immediate_migration 或者 ngtcp2_conn_initiate_migration,ngtcp2_conn_initiate_immediate_migration
遷移前不會執行 path validation, 無法確認 reachability,ngtcp2_conn_initiate_migration
在執行前會執行 path validation 的動作確保 reachability。
int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_tstamp ts) {
int rv;
ngtcp2_dcid *dcid;
ngtcp2_duration pto, initial_pto, timeout;
ngtcp2_pv *pv;
assert(!conn->server);
conn->log.last_ts = ts;
conn->qlog.last_ts = ts;
rv = conn_initiate_migration_precheck(conn, &path->local);
if (rv != 0) {
return rv;
}
if (conn->pv) {
rv = conn_abort_pv(conn, ts);
if (rv != 0) {
return rv;
}
}
dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
ngtcp2_dcid_set_path(dcid, path);
pto = conn_compute_pto(conn, &conn->pktns);
initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
timeout = 3 * ngtcp2_max(pto, initial_pto);
rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
conn->mem);
if (rv != 0) {
return rv;
}
ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
conn->pv = pv;
return conn_call_activate_dcid(conn, &pv->dcid);
}
int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn,
const ngtcp2_path *path,
ngtcp2_tstamp ts) {
int rv;
ngtcp2_dcid *dcid;
ngtcp2_duration pto, initial_pto, timeout;
ngtcp2_pv *pv;
assert(!conn->server);
conn->log.last_ts = ts;
conn->qlog.last_ts = ts;
rv = conn_initiate_migration_precheck(conn, &path->local);
if (rv != 0) {
return rv;
}
ngtcp2_conn_stop_pmtud(conn);
if (conn->pv) {
rv = conn_abort_pv(conn, ts);
if (rv != 0) {
return rv;
}
}
rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
if (rv != 0) {
return rv;
}
dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
ngtcp2_dcid_set_path(dcid, path);
ngtcp2_dcid_copy(&conn->dcid.current, dcid);
ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
conn_reset_congestion_state(conn, ts);
conn_reset_ecn_validation_state(conn);
pto = conn_compute_pto(conn, &conn->pktns);
initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
timeout = 3 * ngtcp2_max(pto, initial_pto);
/* TODO It might be better to add a new flag which indicates that a
connection should be closed if this path validation failed. The
current design allows an application to continue, by migrating
into yet another path. */
rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
conn->mem);
if (rv != 0) {
return rv;
}
conn->pv = pv;
return conn_call_activate_dcid(conn, &conn->dcid.current);
}
在程式碼片段中常看到關於 ngtcp2_pv
這個 struct 是專門負責存放 path validation 相關資料的,詳細請看 ngtcp2_pv.h
, ngtcp2_pv.c
這兩個檔案。