事件迴圈
********

**原始碼：** Lib/asyncio/events.py、Lib/asyncio/base_events.py

======================================================================

-[ 前言 ]-

事件迴圈是每個 asyncio 應用程式的核心。事件迴圈執行非同步任務和回呼、
執行網路 IO 操作並啟動子行程。

應用程式開發人員通常應使用高階的 asyncio 函式，例如 "asyncio.run()"，
並且很少需要參照事件迴圈物件或呼叫其方法。本節主要針對那些需要更細粒度
控制事件迴圈行為的低階程式碼、函式庫和框架的作者。

-[ 取得事件迴圈 ]-

以下的低階函式可用於取得、設置或建立事件迴圈：

asyncio.get_running_loop()

   在當前作業系統執行緒中回傳正在運行的事件迴圈。

   如果沒有運行的事件迴圈，則引發 "RuntimeError"。

   此函式只能從協程或回呼函式中呼叫。

   在 3.7 版被加入.

asyncio.get_event_loop()

   取得目前的事件迴圈。

   當從協程或回呼函式呼叫此函式（例如，使用 call_soon 或類似的 API 於
   排程呼叫），此函式將永遠回傳正在運行的事件迴圈。

   如果沒有設定正在運行的事件迴圈，該函式將回傳
   "get_event_loop_policy().get_event_loop()" 呼叫的結果。

   由於此函式具有相當複雜的行為（尤其是在使用自訂事件迴圈策略時），在
   協程和回呼函式中，建議使用 "get_running_loop()" 函式，而不是
   "get_event_loop()"。

   如上所述，可以考慮使用高階的 "asyncio.run()" 函式，而不是使用這些較
   低階的函式手動建立和關閉事件迴圈。

   在 3.12 版之後被棄用: 如果沒有當前事件迴圈，則會發出棄用警告。在未
   來的某個 Python 發行版中，這將變成錯誤。

asyncio.set_event_loop(loop)

   將 *loop* 設置為當前 OS 執行緒的當前事件迴圈。

asyncio.new_event_loop()

   建立並回傳新的事件迴圈物件。

請注意 "get_event_loop()"、"set_event_loop()" 和 "new_event_loop()" 函
式的行為可以透過設定自訂事件迴圈策略進行調整。

-[ 目錄 ]-

本頁文件包含以下章節：

* 事件迴圈方法章節是事件迴圈 API 們的參照文件；

* 回呼處理章節記錄了從排程方法（如 "loop.call_soon()" 和
  "loop.call_later()"）回傳的 "Handle" 和 "TimerHandle" 實例；

* Server 物件章節記錄了從事件迴圈方法（如 "loop.create_server()"）回傳
  的資料型別；

* 事件迴圈實作章節記錄了 "SelectorEventLoop" 和 "ProactorEventLoop" 類
  別；

* 範例章節展示了如何使用一些事件迴圈 API。


事件迴圈方法
============

事件迴圈提供以下**低階** API：

* 啟動和停止迴圈

* 排程回呼函式

* 排程延遲的回呼函式

* 建立 Futures 和 Tasks

* 打開網路連線

* 建立網路伺服器

* 傳輸檔案

* TLS 升級

* 監視檔案描述器

* 直接使用 socket 物件

* DNS

* 使用管道

* Unix 訊號

* 在執行緒池或行程池中執行程式碼

* 錯誤處理 API

* 啟用除錯模式

* 運行子行程


啟動和停止迴圈
--------------

loop.run_until_complete(future)

   運行直到 *future* （一個 "Future" 實例）完成。

   如果引數是協程物件，則它將被隱式排程為 "asyncio.Task" 運行。

   回傳 Future 的結果或引發其例外。

loop.run_forever()

   運行事件迴圈直到 "stop()" 被呼叫。

   如果在呼叫 "run_forever()" 之前呼叫 "stop()"，則迴圈將使用超時為零
   的方式輪詢 I/O 選擇器，運行所有回應 I/O 事件（以及已經排程的事件）
   的回呼函數，然後退出。

   如果在 "run_forever()" 運行時呼叫 "stop()"，則迴圈將運行當前批次的
   回呼函式，然後退出。請注意，由回呼函式排程的新回呼在此情況下不會運
   行；而是在下次呼叫 "run_forever()" 或 "run_until_complete()" 時運行
   。

loop.stop()

   停止事件迴圈。

loop.is_running()

   如果事件迴圈當前正在運行，則回傳 "True"。

loop.is_closed()

   如果事件迴圈已關閉，則回傳 "True"。

loop.close()

   關閉事件迴圈。

   不得於迴圈運行中呼叫此函式。將丟棄任何待處理的回呼。

   此方法清除所有佇列並關閉執行器，但不等待執行器完成。

   此方法是冪等且不可逆的。在事件迴圈關閉後不應呼叫其他方法。

async loop.shutdown_asyncgens()

   排程所有當前打開的*非同步產生器*物件使用 "aclose()" 呼叫來關閉。呼
   叫此方法後，如果疊代新的非同步產生器，事件迴圈將發出警告。應該使用
   此方法可靠地完成所有已排程的非同步產生器。

   請注意，使用 "asyncio.run()" 時不需要呼叫此函式。

   範例：

      try:
          loop.run_forever()
      finally:
          loop.run_until_complete(loop.shutdown_asyncgens())
          loop.close()

   在 3.6 版被加入.

async loop.shutdown_default_executor(timeout=None)

   排程預設執行器的關閉，並等待它加入 "ThreadPoolExecutor" 中的所有執
   行緒。一旦呼叫了此方法，使用預設執行器與 "loop.run_in_executor()"
   將引發 "RuntimeError"。

   *timeout* 參數指定執行器完成加入所需的時間（以 "float" 秒為單位）。
   預設情況下為 "None"，不會限制執行器所花費的時間。

   如果達到 *timeout*，將發出 "RuntimeWarning" 警告，預設執行器將立即
   終止，不等待其執行緒完成加入。

   備註:

     使用 "asyncio.run()" 時請勿呼叫此方法，因為後者會自動處理預設執行
     器的關閉。

   在 3.9 版被加入.

   在 3.12 版的變更: 加入 *timeout* 參數。


排程回呼函式
------------

loop.call_soon(callback, *args, context=None)

   在事件迴圈的下一次疊代中排程以 *args* 引數呼叫 *callback*
   *callback*。

   回傳 "asyncio.Handle" 的實例，稍後可以用於取消回呼函式。

   回呼函式按照其註冊的順序呼叫。每個回呼函式將被呼叫恰好一次。

   選用的僅限關鍵字引數 *context* 指定了要給 *callback* 執行的自定義
   "contextvars.Context"。當未提供 *context* 時，回呼函式使用當前情境
   。

   與 "call_soon_threadsafe()" 不同，此方法不是執行緒安全的。

loop.call_soon_threadsafe(callback, *args, context=None)

   這是 "call_soon()" 的執行緒安全變體。當從另一個執行緒排程回呼函式時
   ，*必須*使用此函式，因為 "call_soon()" 不是執行緒安全的。

   This function is safe to be called from a reentrant context or
   signal handler, however, it is not safe or fruitful to use the
   returned handle in such contexts.

   如果在已關閉的迴圈上呼叫，則引發 "RuntimeError"。在主應用程式關閉時
   ，這可能發生在次要執行緒上。

   請參閱文件的並行和多執行緒部分。

   在 3.7 版的變更: 新增了 *context* 僅限關鍵字參數。詳細資訊請參閱
   **PEP 567**。

備註:

  大多數 "asyncio" 排程函式不允許傳遞關鍵字引數。要傳遞關鍵字引數，請
  使用 "functools.partial()"：

     # will schedule "print("Hello", flush=True)"
     loop.call_soon(
         functools.partial(print, "Hello", flush=True))

  通常使用 partial 物件比使用 lambda 更方便，因為 asyncio 可以在除錯和
  錯誤訊息中更好地呈現 partial 物件。


排程延遲的回呼函式
------------------

事件迴圈提供為回呼函式排程在將來某個時間點才呼叫的機制。事件迴圈使用了
單調時鐘來追蹤時間。

loop.call_later(delay, callback, *args, context=None)

   排程 *callback* 在給定的 *delay* 秒數後呼叫（可以是整數或浮點數）。

   回傳 "asyncio.TimerHandle" 的實例，可用於取消回呼函式。

   *callback* 將只被呼叫恰好一次。如果有兩個回呼函式被排程在完全相同的
   時間，則其呼叫順序是不定的。

   可選的位置引數 *args* 將在呼叫回呼函式時傳遞。如果要使用關鍵字引數
   呼叫回呼函數，請使用 "functools.partial()"。

   可選的僅限關鍵字 *context* 引數允許為 *callback* 指定自定義的
   "contextvars.Context" 以提供運行。當未提供 *context* 時，將使用當前
   情境。

   在 3.7 版的變更: 新增了 *context* 僅限關鍵字參數。詳細資訊請參閱
   **PEP 567**。

   在 3.8 版的變更: 在 Python 3.7 及更早版本中，使用預設事件迴圈實作時
   ，*delay* 不能超過一天。這在 Python 3.8 中已經修復。

loop.call_at(when, callback, *args, context=None)

   排程 *callback* 在給定的絕對時間戳 *when* （整數或浮點數）處呼叫，
   使用與 "loop.time()" 相同的時間參照。

   此方法的行為與 "call_later()" 相同。

   回傳 "asyncio.TimerHandle" 的實例，可用於取消回呼函式。

   在 3.7 版的變更: 新增了 *context* 僅限關鍵字參數。詳細資訊請參閱
   **PEP 567**。

   在 3.8 版的變更: 在 Python 3.7 及更早版本中，使用預設事件迴圈實作時
   ，*when* 和當前時間之間的差值不能超過一天。這在 Python 3.8 中已經修
   復。

loop.time()

   根據事件迴圈的內部單調時鐘，回傳當前時間，以 "float" 值表示。

備註:

  在 3.8 版的變更: 在 Python 3.7 及更早版本中，超時（相對 *delay* 或絕
  對 *when*）不應超過一天。這在 Python 3.8 中已經修復。

也參考: 函式 "asyncio.sleep()"。


建立 Futures 和 Tasks
---------------------

loop.create_future()

   建立附加到事件迴圈的 "asyncio.Future" 物件。

   這是在 asyncio 中建立 Futures 的首選方式。這允許第三方事件迴圈提供
   Future 物件的替代實作（具有更好的性能或儀器計測表現）。

   在 3.5.2 版被加入.

loop.create_task(coro, *, name=None, context=None)

   排程執行協程 *coro*。回傳 "Task" 物件。

   第三方事件迴圈可以使用其自己的 "Task" 子類別以實現互操作性（
   interoperability）。在這種情況下，結果類型是 "Task" 的子類別。

   如果提供了 *name* 引數且不為 "None"，則將其設置為任務的名稱，使用
   "Task.set_name()"。

   可選的僅限關鍵字 *context* 引數允許為 *coro* 指定自定義的
   "contextvars.Context" 以提供運行。當未提供 *context* 時，將建立當前
   情境的副本。

   在 3.8 版的變更: 加入 *name* 參數。

   在 3.11 版的變更: 加入 *context* 參數。

loop.set_task_factory(factory)

   設置將由 "loop.create_task()" 使用的任務工廠。

   如果 *factory* 為 "None"，將設置預設的任務工廠。否則，*factory* 必
   須是一個具有匹配簽名 "(loop, coro, context=None)" 的 *callable*，其
   中 *loop* 是有效事件迴圈的參照，*coro* 是一個協程物件。該可呼叫物件
   必須回傳一個與 "asyncio.Future" 相容的物件。

loop.get_task_factory()

   回傳任務工廠，如果使用預設任務工廠則回傳 "None"。


打開網路連線
------------

async loop.create_connection(protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, happy_eyeballs_delay=None, interleave=None, all_errors=False)

   打開以 *host* 和 *port* 指定之給定地址的串流傳輸連線。

   根據 *host*（或提供的 *family* 引數）的情況，socket 家族可以是
   "AF_INET" 或 "AF_INET6"。

   Socket 類型將為 "SOCK_STREAM"。

   *protocol_factory* 必須是一個回傳 asyncio protocol 實作的可呼叫函式
   。

   此方法將嘗試在背景建立連線。成功時，它將回傳一對 "(transport,
   protocol)"。

   底層操作的時間軸簡介如下：

   1. 建立連線並為其建立傳輸。

   2. *protocol_factory* 在無引數的情況下被呼叫，並且預計回傳一個 協定
      實例。

   3. 通過呼叫其 "connection_made()" 方法，將協議實例與傳輸連線在一起
      。

   4. 成功時回傳一個 "(transport, protocol)" 元組。

   建立的傳輸是一個依賴實作的雙向串流。

   其他引數：

   * 若有給定 *ssl* 且非 false，將建立 SSL/TLS 傳輸（預設建立普通 TCP
     傳輸）。如果 *ssl* 為 "ssl.SSLContext" 物件，則使用該情境來建立傳
     輸；如果 *ssl* 為 "True"，則使用 "ssl.create_default_context()"
     回傳的預設情境。

     也參考: SSL/TLS 安全考量

   * *server_hostname* 設置或覆蓋目標伺服器憑證將匹配的主機名稱。僅在
     *ssl* 不為 "None" 時傳遞。預設情況下，將使用 *host* 引數的值。如
     果 *host* 為空，則沒有預設值，必須傳遞 *server_hostname* 的值。若
     *server_hostname* 為空字串，將停用主機名稱匹配（這是一個嚴重的安
     全風險，可能導致中間人攻擊）。

   * *family*、*proto*、*flags* 是可選的位址家族、協議和旗標，用於傳遞
     至 getaddrinfo() 進行 *host* 解析。若有給定這些應該都是相應
     "socket" 模組常數的整數。

   * 若有給定，*happy_eyeballs_delay* 會啟用此連線的 Happy Eyeballs。
     它應該是一個浮點數，表示等待連線嘗試完成的秒數，然後在並行啟動下
     一次嘗試。這是 **RFC 8305** 中定義的「連線嘗試延遲」。RFC 建議的
     合理預設值為 "0.25" 秒（250 毫秒）。

   * *interleave* 控制主機名稱解析為多個 IP 位址時的地址重新排序。若為
     "0" 或未指定，將不執行重排序，並按 "getaddrinfo()" 回傳的順序嘗試
     位址。如果指定正整數，則按地址家族交錯排列，給定的整數直譯為
     **RFC 8305** 中定義的「首個地址家族計數」。如果未指定
     *happy_eyeballs_delay*，則預設值為 "0"，如果指定則為 "1"。

   * 若有給定 *sock* 則其應為已存在且已連線的 "socket.socket" 物件，可
     供傳輸使用。如果提供了 *sock*，則不應指定 *host*、*port*、
     *family*、*proto*、*flags*、*happy_eyeballs_delay*、*interleave*
     和 *local_addr* 中的任何一項。

     備註:

       引數 *sock* 將 socket 所有權轉移給所建立的傳輸 socket，請呼叫傳
       輸的 "close()" 方法。

   * 若有給定 *local_addr* 則其為一個 "(local_host, local_port)" 元組
     ，用於在本地綁定 socket。將使用 "getaddrinfo()" 查找 *local_host*
     和 *local_port*，方式類似於 *host* 和 *port*。

   * *ssl_handshake_timeout* （對於 TLS 連線）是等待 TLS 交握的時間，
     以秒為單位，在那之前若未完成則會中斷連線。如果為 "None" （預設值
     ），則會等待 "60.0" 秒。

   * *ssl_shutdown_timeout* 是等待 SSL 關閉完成以前中斷連線的時間，以
     秒為單位。如果為 "None" （預設值），則會等待 "30.0" 秒。

   * *all_errors* 決定在無法建立連線時會引發哪些例外。預設情況下，只會
     引發單一 "Exception"：如果只有一個例外或所有錯誤訊息相同，則引發
     第一個例外，否則引發包含所有錯誤訊息的單一 "OSError"。當
     "all_errors" 為 "True" 時，將引發包含所有例外的 "ExceptionGroup"
     （即使只有一個例外）。

   在 3.5 版的變更: 新增 "ProactorEventLoop" 中的 SSL/TLS 支援。

   在 3.6 版的變更: 所有 TCP 連線都預設有 socket.TCP_NODELAY socket 選
   項。

   在 3.7 版的變更: 增加 *ssl_handshake_timeout* 參數。

   在 3.8 版的變更: 加入 *happy_eyeballs_delay* 和 *interleave* 參數。
   Happy Eyeballs 演算法：雙協定堆疊主機 (Dual-Stack Hosts) 的成功。當
   伺服器的 IPv4 路徑和協議運作正常，但伺服器的 IPv6 路徑和協議不運作
   時，雙棧用戶端應用程式會比僅具 IPv4 的用戶端體驗到顯著的連線延遲。
   這是不希望的，因為這會導致雙棧用戶端的使用者體驗變差。本文件具體說
   明了減少此用戶可見延遲的演算法要求並提供了一種演算法。更多資訊請見
   ： https://datatracker.ietf.org/doc/html/rfc6555

   在 3.11 版的變更: 增加 *ssl_shutdown_timeout* 參數。

   在 3.12 版的變更: 已新增 *all_errors*。

   也參考:

     函式 "open_connection()" 是高階的替代 API。它回傳一對
     ("StreamReader", "StreamWriter") 可直接在 async/await 程式碼中使
     用。

async loop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_port=None, allow_broadcast=None, sock=None)

   建立一個資料報連線。

   Socket 家族可以是 "AF_INET"、"AF_INET6" 或 "AF_UNIX"，視乎 *host*（
   或提供的 *family* 引數）而定。

   Socket 類型將為 "SOCK_DGRAM"。

   *protocol_factory* 必須是可呼叫的函式，回傳 protocol 實作。

   成功時回傳 "(transport, protocol)" 元組。

   其他引數：

   * *local_addr*，如果提供，是一個 "(local_host, local_port)" 元組，
     用於在本地綁定 socket。*local_host* 和 *local_port* 使用
     "getaddrinfo()" 來查找。

   * *remote_addr*，如果提供，是一個 "(remote_host, remote_port)" 元組
     ，用於將 socket 連線到遠端位址。 *remote_host* 和 *remote_port*
     使用 "getaddrinfo()" 來查找。

   * *family*、*proto* 和 *flags* 是用於傳遞給 "getaddrinfo()" 以解析
     *host* 的可選地址家族、協定和旗標。如果提供，這些應該都是來自相應
     的 "socket" 模組常數的整數。

   * *reuse_port* 告訴核心允許將此端點綁定到與其他現有端點相同的埠，只
     要它們在建立時都設定了此旗標。此選項不受 Windows 和某些 Unix 系統
     支援。如果未定義 "SO_REUSEPORT" 常數，則不支援此功能。

   * *allow_broadcast* 告訴核心允許此端點向廣播位址發送訊息。

   * *sock* 可以選擇性地指定，以使用預先存在且已連線的 "socket.socket"
     物件供傳輸使用。如果指定，*local_addr* 和 *remote_addr* 應省略（
     必須是 "None"）。

     備註:

       引數 *sock* 將 socket 所有權轉移給所建立的傳輸 socket，請呼叫傳
       輸的 "close()" 方法。

   請參閱 UDP 回應用戶端協議 和 UDP 回應伺服器協議 範例。

   在 3.4.4 版的變更: 新增 *family*、*proto*、*flags*、*reuse_address*
   、*reuse_port*、*allow_broadcast* 和 *sock* 參數。

   在 3.8 版的變更: 新增對於 Windows 的支援。

   在 3.8.1 版的變更: 不再支援 *reuse_address* 參數，因為使用
   "SO_REUSEADDR" 對於 UDP 存有重大的安全疑慮。明確傳遞
   "reuse_address=True" 將引發例外。當具有不同 UID 的多個行程使用
   "SO_REUSEADDR" 將 socket 分配給相同的 UDP socket 地址時，傳入的封包
   可能會在 socket 之間隨機分佈。對於有支援的平台，*reuse_port* 可以用
   作類似功能的替代方案。使用 *reuse_port*，將改為使用 "SO_REUSEPORT"
   ，該選項明確禁止具有不同 UID 的行程將 socket 分配給相同的 socket 地
   址。

   在 3.11 版的變更: 自 Python 3.9.0、3.8.1、3.7.6 和 3.6.10 起，已完
   全移除 *reuse_address* 參數。

async loop.create_unix_connection(protocol_factory, path=None, *, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None)

   建立一個 Unix 連線。

   Socket 家族將為 "AF_UNIX"；socket 類型將為 "SOCK_STREAM"。

   成功時回傳 "(transport, protocol)" 元組。

   *path* 是 Unix 域 socket 的名稱，除非指定 *sock* 參數，否則為必填。
   支援抽象 Unix sockets、"str"、"bytes" 和 "Path" 路徑。

   有關此方法的引數資訊，請參閱 "loop.create_connection()" 方法的文件
   。

   Availability: Unix.

   在 3.7 版的變更: 新增 *ssl_handshake_timeout* 參數。*path* 參數現在
   可以是 *path-like object*。

   在 3.11 版的變更: 增加 *ssl_shutdown_timeout* 參數。


建立網路伺服器
--------------

async loop.create_server(protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, start_serving=True)

   建立一個 TCP 伺服器（socket 類型 "SOCK_STREAM"），監聽 *host* 位址
   的 *port*。

   回傳一個 "Server" 物件。

   引數：

   * *protocol_factory* 必須是可呼叫的函式，回傳 protocol 實作。

   * 可以將 *host* 參數設為幾種類型，以確定伺服器將監聽的位置：

     * 如果 *host* 是字串，則 TCP 伺服器綁定到由 *host* 指定的單個網路
       介面。

     * 如果 *host* 是字串序列，則 TCP 伺服器綁定到序列指定的所有網路介
       面。

     * 若 *host* 是空字串或 "None"，則所有介面都被假定並回傳多個
       socket 的清單（可能一個用於 IPv4，另一個用於 IPv6）。

   * 可以設定 *port* 參數以指定伺服器應該監聽的埠。如果是 "0" 或
     "None"（預設值），將隨機選擇一個未使用的埠（請注意，如果 *host*
     解析為多個網路介面，將為每個介面隨機選擇不同的隨機埠）。

   * *family* 可以設定為 "socket.AF_INET" 或 "AF_INET6" 以強制使用
     IPv4 或 IPv6。如果未設定，*family* 將從主機名稱決定（預設為
     "AF_UNSPEC"）。

   * *flags* 是 "getaddrinfo()" 的位元遮罩。

   * 可以可選地指定 *sock* 以使用現有的 socket 物件。如果指定了，
     *host* 和 *port* 不能指定。

     備註:

       *sock* 引數將 socket 的所有權轉移給建立的伺服器。要關閉 socket
       ，請呼叫伺服器的 "close()" 方法。

   * *backlog* 是傳遞給 "listen()" 的最大佇列連線數（預設為 100）。

   * *ssl* 可以設定為 "SSLContext" 實例以在接受的連線上啟用 TLS。

   * *reuse_address* 告訴內核重用 "TIME_WAIT" 狀態下的本地 socket，而
     不等待其自然超時過期。如果未指定，在 Unix 上將自動設置為 "True"。

   * *reuse_port* 告訴內核允許此端點繫結到與其他現有端點繫結的相同埠，
     只要它們在建立時都設置了此旗標。此選項在旗標 Windows 上不受支援。

   * （對於 TLS 伺服器）*ssl_handshake_timeout* 是在中斷連線之前等待
     TLS 握手完成的時間（以秒為單位）。如果為 "None"（預設），則為
     "60.0" 秒。

   * *ssl_shutdown_timeout* 是等待 SSL 關閉完成以前中斷連線的時間，以
     秒為單位。如果為 "None" （預設值），則會等待 "30.0" 秒。

   * 將 *start_serving* 設置為 "True"（預設）將使建立的伺服器立即開始
     接受連接。當設置為 "False" 時，用戶應該等待
     "Server.start_serving()" 或 "Server.serve_forever()" 來使伺服器開
     始接受連線。

   在 3.5 版的變更: 新增 "ProactorEventLoop" 中的 SSL/TLS 支援。

   在 3.5.1 版的變更: *host* 參數可以是字串序列。

   在 3.6 版的變更: 新增 *ssl_handshake_timeout* 與 *start_serving* 參
   數。所有 TCP 連線都預設有 socket.TCP_NODELAY socket 選項。

   在 3.11 版的變更: 增加 *ssl_shutdown_timeout* 參數。

   也參考:

     "start_server()" 函式是一個更高階的替代 API，它回傳一對
     "StreamReader" 和 "StreamWriter"，可以在 async/await 程式碼中使用
     。

async loop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, start_serving=True)

   類似 "loop.create_server()"，但適用於 "AF_UNIX" socket 家族。

   *path* 是 Unix 域 socket 的名稱，除非提供了 *sock* 引數，否則必須給
   定。支援抽象 Unix sockets、"str"、"bytes" 和 "Path" 路徑。

   有關此方法的引數資訊，請參閱 "loop.create_server()" 方法的文件。

   Availability: Unix.

   在 3.7 版的變更: 新增 *ssl_handshake_timeout* 與 *start_serving* 參
   數。*path* 參數現在可為一個 "Path" 物件。

   在 3.11 版的變更: 增加 *ssl_shutdown_timeout* 參數。

async loop.connect_accepted_socket(protocol_factory, sock, *, ssl=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None)

   將已接受的連線包裝成傳輸層/協議對。

   此方法可以由在 asyncio 外接受連線但使用 asyncio 處理連線的伺服器使
   用。

   參數：

   * *protocol_factory* 必須是可呼叫的函式，回傳 protocol 實作。

   * *sock* 是從 "socket.accept" 回傳的預先存在的 socket 物件。

     備註:

       引數 *sock* 將 socket 所有權轉移給所建立的傳輸 socket，請呼叫傳
       輸的 "close()" 方法。

   * *ssl* 可以設置為 "SSLContext" 以在已接受的連線上啟用 SSL。

   * （對於 SSL 連線）*ssl_handshake_timeout* 是在中斷連線之前等待 SSL
     握手完成的時間（以秒為單位）。如果為 "None"（預設），則為 "60.0"
     秒。

   * *ssl_shutdown_timeout* 是等待 SSL 關閉完成以前中斷連線的時間，以
     秒為單位。如果為 "None" （預設值），則會等待 "30.0" 秒。

   回傳 "(transport, protocol)" 對。

   在 3.5.3 版被加入.

   在 3.7 版的變更: 增加 *ssl_handshake_timeout* 參數。

   在 3.11 版的變更: 增加 *ssl_shutdown_timeout* 參數。


傳輸檔案
--------

async loop.sendfile(transport, file, offset=0, count=None, *, fallback=True)

   通過 *transport* 發送 *file*。回傳發送的總位元組數。

   如果可用，該方法使用高性能 "os.sendfile()"。

   *file* 必須是以二進位模式打開的常規檔案物件。

   *offset* 告訴從哪裡開始讀取檔案。如果指定了，*count* 是要傳輸的總位
   元組數，而不是發送檔案直到達到 EOF。即使此方法引發錯誤時，檔案位置
   也始終更新，可以使用 "file.tell()" 取得實際發送的位元組數。

   將 *fallback* 設置為 "True" 會使 asyncio 在平台不支援 sendfile 系統
   呼叫時（例如 Windows 或 Unix 上的 SSL socket）手動讀取和發送檔案。

   如果系統不支援 *sendfile* 系統呼叫且 *fallback* 為 "False"，則引發
   "SendfileNotAvailableError"。

   在 3.7 版被加入.


TLS 升級
--------

async loop.start_tls(transport, protocol, sslcontext, *, server_side=False, server_hostname=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None)

   將基於傳輸的現有連線升級到 TLS。

   建立 TLS 編解碼器實例並在 *transport* 和 *protocol* 之間插入它。編
   解碼器既實作了對於 *transport* 的協議，也實作了對於 *protocol* 的傳
   輸。

   回傳建立的雙介面實例。在 *await* 後，*protocol* 必須停止使用原始的
   *transport*，僅與回傳的物件通信，因為編碼器快取了 *protocol* 端的資
   料，並且與 *transport* 間歇性地交換額外的 TLS session 封包。

   在某些情況下（例如傳入的傳輸已經關閉），此函式可能回傳 "None"。

   參數：

   * *transport* 和 *protocol* 實例，由像 "create_server()" 和
     "create_connection()" 等方法回傳。

   * *sslcontext*：配置好的 "SSLContext" 實例。

   * 當升級伺服器端連線時（像由 "create_server()" 建立的那樣）傳遞
     "True"。

   * *server_hostname*：設置或覆蓋將用於匹配目標伺服器憑證的主機名。

   * *ssl_handshake_timeout* （對於 TLS 連線）是等待 TLS 交握的時間，
     以秒為單位，在那之前若未完成則會中斷連線。如果為 "None" （預設值
     ），則會等待 "60.0" 秒。

   * *ssl_shutdown_timeout* 是等待 SSL 關閉完成以前中斷連線的時間，以
     秒為單位。如果為 "None" （預設值），則會等待 "30.0" 秒。

   在 3.7 版被加入.

   在 3.11 版的變更: 增加 *ssl_shutdown_timeout* 參數。


監視檔案描述器
--------------

loop.add_reader(fd, callback, *args)

   開始監視 *fd* 檔案描述器的讀取可用性，一但 *fd* 可讀取，使用指定引
   數呼叫 *callback*。

   Any preexisting callback registered for *fd* is cancelled and
   replaced by *callback*.

loop.remove_reader(fd)

   停止監視 *fd* 檔案描述器的讀取可用性。如果 *fd* 之前正在監視讀取，
   則回傳 "True"。

loop.add_writer(fd, callback, *args)

   開始監視 *fd* 檔案描述器的寫入可用性，一旦 *fd* 可寫入，使用指定引
   數呼叫 *callback*。

   Any preexisting callback registered for *fd* is cancelled and
   replaced by *callback*.

   使用 "functools.partial()" 向 *callback* 傳送關鍵字引數。

loop.remove_writer(fd)

   停止監視 *fd* 檔案描述器的寫入可用性。如果 *fd* 之前正在監視寫入，
   則回傳 "True"。

另請參閱平台支援部分以了解這些方法的一些限制。


直接使用 socket 物件
--------------------

一般情況下，使用基於傳輸的 API（如 "loop.create_connection()" 和
"loop.create_server()"）的協議實作比直接使用 socket 的實作更快。然而在
某些情況下性能不是關鍵，直接使用 "socket" 物件更方便。

async loop.sock_recv(sock, nbytes)

   從 *sock* 接收最多 *nbytes*。"socket.recv()" 的非同步版本。

   將接收到的資料作為 bytes 物件回傳。

   *sock* 必須是非阻塞 socket。

   在 3.7 版的變更: 儘管此方法一直記錄為協程方法，但 Python 3.7 之前的
   版本回傳 "Future"。自 Python 3.7 起，這是 "async def" 方法。

async loop.sock_recv_into(sock, buf)

   從 *sock* 接收資料到 *buf* 緩衝區。仿照阻塞 "socket.recv_into()" 方
   法。

   回傳寫入緩衝區位元組的數目。

   *sock* 必須是非阻塞 socket。

   在 3.7 版被加入.

async loop.sock_recvfrom(sock, bufsize)

   從 *sock* 接收最多 *bufsize* 大小的資料單元。"socket.recvfrom()" 的
   非同步版本。

   回傳一個元組 (received data, remote address)。

   *sock* 必須是非阻塞 socket。

   在 3.11 版被加入.

async loop.sock_recvfrom_into(sock, buf, nbytes=0)

   從 *sock* 接收最多 *nbytes* 大小的資料單元到 *buf*。
   "socket.recvfrom_into()" 的非同步版本。

   回傳一個元組 (number of bytes received, remote address)。

   *sock* 必須是非阻塞 socket。

   在 3.11 版被加入.

async loop.sock_sendall(sock, data)

   將 *data* 發送到 *sock* socket。"socket.sendall()" 的非同步版本。

   此方法將繼續發送到 socket，直到 *data* 中的所有資料都已發送或發生錯
   誤。成功時回傳 "None"。錯誤時引發例外。此外，沒有辦法確定接收端成功
   處理了多少資料（如果有的話）。

   *sock* 必須是非阻塞 socket。

   在 3.7 版的變更: 儘管該方法一直被記錄為協程方法，但在 Python 3.7 之
   前它回傳 "Future"。從 Python 3.7 開始，這是一個 "async def" 方法。

async loop.sock_sendto(sock, data, address)

   從 *sock* 向 *address* 發送一個資料單元。"socket.sendto()" 的非同步
   版本。

   回傳發送的位元組數。

   *sock* 必須是非阻塞 socket。

   在 3.11 版被加入.

async loop.sock_connect(sock, address)

   將 *sock* 連線到位於 *address* 的遠端 socket。

   "socket.connect()" 的非同步版本。

   *sock* 必須是非阻塞 socket。

   在 3.5.2 版的變更: 不再需要解析 "address"。"sock_connect" 將嘗試透
   過呼叫 "socket.inet_pton()" 檢查 *address* 是否已解析。如果沒有，將
   使用 "loop.getaddrinfo()" 解析 *address*。

   也參考: "loop.create_connection()" 和 "asyncio.open_connection()"。

async loop.sock_accept(sock)

   接受一個連線。模擬阻塞的 "socket.accept()" 方法。

   Socket 必須繫結到一個地址並偵聽連線。回傳值是一個 "(conn, address)"
   對，其中 *conn* 是一個 *新* socket 物件，可在連線上發送和接收資料，
   *address* 是連接另一端對應的 socket 地址。

   *sock* 必須是非阻塞 socket。

   在 3.7 版的變更: 儘管該方法一直被記錄為協程方法，但在 Python 3.7 之
   前它回傳 "Future"。從 Python 3.7 開始，這是一個 "async def" 方法。

   也參考: "loop.create_server()" 和 "start_server()"。

async loop.sock_sendfile(sock, file, offset=0, count=None, *, fallback=True)

   如果可行，使用高性能 "os.sendfile" 發送檔案。回傳發送的總位元組數。

   "socket.sendfile()" 的非同步版本。

   *sock* 必須是非阻塞的 "socket.SOCK_STREAM" "socket"。

   *file* 必須是以二進位模式打開的常規檔案物件。

   *offset* 告訴從哪裡開始讀取檔案。如果指定了，*count* 是要傳輸的總位
   元組數，而不是發送檔案直到達到 EOF。即使此方法引發錯誤時，檔案位置
   也始終更新，可以使用 "file.tell()" 取得實際發送的位元組數。

   當設置為 "True" 時，*fallback* 使 asyncio 在平台不支援 sendfile 系
   統呼叫時（例如 Windows 或 Unix 上的 SSL socket）手動讀取和發送檔案
   。

   如果系統不支援 *sendfile* 系統呼叫且 *fallback* 為 "False"，引發
   "SendfileNotAvailableError"。

   *sock* 必須是非阻塞 socket。

   在 3.7 版被加入.


DNS
---

async loop.getaddrinfo(host, port, *, family=0, type=0, proto=0, flags=0)

   "socket.getaddrinfo()" 的非同步版本。

async loop.getnameinfo(sockaddr, flags=0)

   "socket.getnameinfo()" 的非同步版本。

備註:

  Both *getaddrinfo* and *getnameinfo* internally utilize their
  synchronous versions through the loop's default thread pool
  executor. When this executor is saturated, these methods may
  experience delays, which higher-level networking libraries may
  report as increased timeouts. To mitigate this, consider using a
  custom executor for other user tasks, or setting a default executor
  with a larger number of workers.

在 3.7 版的變更: *getaddrinfo* 和 *getnameinfo* 方法一直被記錄為回傳協
程，但在 Python 3.7 之前它們實際上回傳 "asyncio.Future" 物件。從
Python 3.7 開始，兩個方法都是協程。


使用管道
--------

async loop.connect_read_pipe(protocol_factory, pipe)

   在事件迴圈中註冊 *pipe* 的讀取端。

   *protocol_factory* 必須是一個回傳 asyncio protocol 實作的可呼叫函式
   。

   *pipe* 是 *類檔案物件*。

   回傳 "(transport, protocol)" 對，其中 *transport* 支援
   "ReadTransport" 介面，*protocol* 是由 *protocol_factory* 實例化的物
   件。

   使用 "SelectorEventLoop" 事件迴圈時，*pipe* 設置為非阻塞模式。

async loop.connect_write_pipe(protocol_factory, pipe)

   在事件迴圈中註冊 *pipe* 的寫入端。

   *protocol_factory* 必須是一個回傳 asyncio protocol 實作的可呼叫函式
   。

   *pipe* 是 *file-like object*。

   回傳 "(transport, protocol)" 對，其中 *transport* 支援
   "WriteTransport" 介面，*protocol* 是由 *protocol_factory* 實例化的
   物件。

   使用 "SelectorEventLoop" 事件迴圈時，*pipe* 設置為非阻塞模式。

備註:

  "SelectorEventLoop" 在 Windows 上不支援上述方法。對於 Windows 請使用
  "ProactorEventLoop"。

也參考: "loop.subprocess_exec()" 和 "loop.subprocess_shell()" 方法。


Unix 訊號
---------

loop.add_signal_handler(signum, callback, *args)

   將 *callback* 設置為 *signum* 訊號的處理程式。

   該回呼將由 *loop* 呼叫，與該事件迴圈的其他排隊回呼和可運行的協程一
   起。與使用 "signal.signal()" 註冊的訊號處理程式不同，使用此函式註冊
   的回呼允許與事件迴圈進行互動。

   如果訊號號無效或不可捕獲，引發 "ValueError"。如果設定處理程序有問題
   ，拋出 "RuntimeError"。

   使用 "functools.partial()" 向 *callback* 傳送關鍵字引數。

   像 "signal.signal()" 一樣，此函式必須在主執行緒中呼叫。

loop.remove_signal_handler(sig)

   移除 *sig* 訊號的處理程式。

   如果訊號處理程式被移除，回傳 "True"；如果給定訊號沒有設置處理程式，
   回傳 "False"。

   Availability: Unix.

也參考: "signal" 模組。


在執行緒池或行程池中執行程式碼
------------------------------

awaitable loop.run_in_executor(executor, func, *args)

   安排在指定的執行器中呼叫 *func*。

   The *executor* argument should be an "concurrent.futures.Executor"
   instance. The default executor is used if *executor* is "None". The
   default executor can be set by "loop.set_default_executor()",
   otherwise, a "concurrent.futures.ThreadPoolExecutor" will be lazy-
   initialized and used by "run_in_executor()" if needed.

   範例：

      import asyncio
      import concurrent.futures

      def blocking_io():
          # File operations (such as logging) can block the
          # event loop: run them in a thread pool.
          with open('/dev/urandom', 'rb') as f:
              return f.read(100)

      def cpu_bound():
          # CPU-bound operations will block the event loop:
          # in general it is preferable to run them in a
          # process pool.
          return sum(i * i for i in range(10 ** 7))

      async def main():
          loop = asyncio.get_running_loop()

          ## Options:

          # 1. Run in the default loop's executor:
          result = await loop.run_in_executor(
              None, blocking_io)
          print('default thread pool', result)

          # 2. Run in a custom thread pool:
          with concurrent.futures.ThreadPoolExecutor() as pool:
              result = await loop.run_in_executor(
                  pool, blocking_io)
              print('custom thread pool', result)

          # 3. Run in a custom process pool:
          with concurrent.futures.ProcessPoolExecutor() as pool:
              result = await loop.run_in_executor(
                  pool, cpu_bound)
              print('custom process pool', result)

      if __name__ == '__main__':
          asyncio.run(main())

   請注意，由於 "multiprocessing"（由 "ProcessPoolExecutor" 使用）的特
   殊性，選項 3 需要進入點保護（"if __name__ == '__main__'"）。請參閱
   主模組的安全引入。

   此方法回傳 "asyncio.Future" 物件。

   使用 "functools.partial()" 將來關鍵字引數傳遞給 *func*。

   在 3.5.3 版的變更: "loop.run_in_executor()" 不再配置它建立的執行緒
   池執行器的 "max_workers"，而是讓執行緒池執行器（
   "ThreadPoolExecutor"）設定預設值。

loop.set_default_executor(executor)

   將 *executor* 設置為 "run_in_executor()" 使用的預設執行器。
   *executor* 必須是 "ThreadPoolExecutor" 的實例。

   在 3.11 版的變更: *executor* 必須是 "ThreadPoolExecutor" 的實例。


錯誤處理 API
------------

允許自定義事件迴圈中的例外處理方式。

loop.set_exception_handler(handler)

   將 *handler* 設定為新的事件迴圈例外處理程式。

   如果 *handler* 是 "None"，則將設置預設例外處理程式。否則，*handler*
   必須是一個可呼叫物件，簽名匹配 "(loop, context)"，其中 "loop" 是參
   照活躍事件迴圈的，"context" 是包含例外詳細資訊的 "dict" 物件（有關
   情境的詳細資訊，請參閱 "call_exception_handler()" 文件）。

   如果代表 "Task" 或 "Handle" 呼叫處理程式，它將在該任務或回呼處理程
   式的 "contextvars.Context" 中運行。

   在 3.12 版的變更: 處理程式可能在引發例外的任務或處理程式的
   "Context" 中被呼叫。

loop.get_exception_handler()

   回傳當前的例外處理程式，如果未設置自定義例外處理程式，則回傳 "None"
   。

   在 3.5.2 版被加入.

loop.default_exception_handler(context)

   預設例外處理程式。

   當發生例外且未設置例外處理程式時呼叫此函式。自定義例外處理程式可以
   呼叫此函式以轉由預設處理程式處理。

   *context* 參數與 "call_exception_handler()" 中的意思相同。

loop.call_exception_handler(context)

   呼叫當前事件迴圈例外處理程式。

   *context* 是一個包含以下鍵的 "dict" 物件（未來的 Python 版本中可能
   會引入新的鍵）:

   * 'message'：錯誤訊息；

   * 'exception'（可選）：例外物件；

   * 'future'（可選）： "asyncio.Future" 實例；

   * 'task'（可選）： "asyncio.Task" 實例；

   * 'handle'（可選）： "asyncio.Handle" 實例；

   * 'protocol'（可選）： Protocol 實例；

   * 'transport'（可選）： Transport 實例；

   * 'socket'（可選）： "socket.socket" 實例；

   * 'asyncgen'(可選)： 非同步產生器引發
        例外。

   備註:

     此方法不應在子類別事件迴圈中被覆寫。為了自定義例外處理，請使用
     "set_exception_handler()" 方法。


啟用除錯模式
------------

loop.get_debug()

   取得事件迴圈的除錯模式（"bool"）。

   如果環境變數 "PYTHONASYNCIODEBUG" 被設定為非空字串，則預設值為
   "True"，否則為 "False"。

loop.set_debug(enabled: bool)

   設定事件迴圈的除錯模式。

   在 3.7 版的變更: 現在也可以使用新的 Python 開發模式 啟用除錯模式。

loop.slow_callback_duration

   此屬性可用於設定被視為"慢"的最短執行時間（以秒為單位）。啟用偵錯模
   式後，"慢"回呼將被記錄。

   預設值為 100 毫秒

也參考: asyncio 的除錯模式。


運行子行程
----------

本小節描述的方法是低階的。在常規的 async/await 程式碼中，請考慮使用高
階 "asyncio.create_subprocess_shell()" 和
"asyncio.create_subprocess_exec()" 輔助功能而不是。

備註:

  在 Windows 上，預設事件迴圈 "ProactorEventLoop" 支援子行程，而
  "SelectorEventLoop" 不支援。詳細資訊請參見 Windows 上對於子行程的支
  援。

async loop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

   從 *args* 指定的一個或多個字串引數建立子行程。

   *args* 必須是由以下項表示的字串串列：

   * "str"；

   * 或 "bytes"，編碼為 檔案系統編碼。

   第一個字串指定程序可執行檔案，其餘字串指定引數。字串引數一起組成程
   序的 "argv"。

   這與標準函式庫 "subprocess.Popen" 類似，使用 "shell=False" 呼叫並將
   字串串列作為第一個引數傳遞；然而，"Popen" 接受單個字串串列引數，
   *subprocess_exec* 接受多個字串引數。

   *protocol_factory* 必須是回傳 "asyncio.SubprocessProtocol" 子類別的
   可呼叫物件。

   其他參數：

   * *stdin* 可以是以下任意一個：

     * 類檔案物件

     * 現有的檔案描述器（正整數），例如用 "os.pipe()" 建立的

     * "subprocess.PIPE" 常數（預設），它將建立一個新的管道並連線，

     * 值 "None" 將使子行程從此行程繼承檔案描述器

     * "subprocess.DEVNULL" 常數，表示將使用特殊的 "os.devnull" 檔案

   * *stdout* 可以是以下任意一個：

     * 類檔案物件

     * "subprocess.PIPE" 常數（預設），它將建立一個新的管道並連線，

     * 值 "None" 將使子行程從此行程繼承檔案描述器

     * "subprocess.DEVNULL" 常數，表示將使用特殊的 "os.devnull" 檔案

   * *stderr* 可以是以下任意一個：

     * 類檔案物件

     * "subprocess.PIPE" 常數（預設），它將建立一個新的管道並連線，

     * 值 "None" 將使子行程從此行程繼承檔案描述器

     * "subprocess.DEVNULL" 常數，表示將使用特殊的 "os.devnull" 檔案

     * "subprocess.STDOUT" 常數，它將標準錯誤串流連線到行程的標準輸出
       串流

   * 所有其他關鍵字引數都會傳遞給 "subprocess.Popen" 而不進行直譯，但
     *bufsize*、*universal_newlines*、*shell*、*text*、*encoding* 和
     *errors* 除外，這些不應該指定。

     "asyncio" 子行程 API 不支援將串流解碼為文本。可以使用
     "bytes.decode()" 將從串流回傳的位元組轉換為文本。

   如果傳遞給 *stdin*、*stdout* 或 *stderr* 的類檔案物件表示管道，則該
   管道的另一端應該使用 "connect_write_pipe()" 或
   "connect_read_pipe()" 註冊到事件迴圈中。

   有關其他引數的文件，請參閱 "subprocess.Popen" 類別的建構函式。

   回傳 "(transport, protocol)" 對，其中 *transport* 符合
   "asyncio.SubprocessTransport" 基底類別，*protocol* 是由
   *protocol_factory* 實例化的物件。

async loop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

   使用平台的 “shell” 語法從 *cmd* 建立子行程，*cmd* 可以是 "str" 或編
   碼為 檔案系統編碼 的 "bytes" 字串。

   這類似於標準函式庫中的 "subprocess.Popen" 類別，使用 "shell=True"
   呼叫。

   *protocol_factory* 必須是回傳 "SubprocessProtocol" 子類別的可呼叫物
   件。

   有關其餘引數的更多詳細資訊，請參閱 "subprocess_exec()"。

   回傳一對 "(transport, protocol)"，其中 *transport* 符合
   "SubprocessTransport" 基底類別，而 *protocol* 是由
   *protocol_factory* 實例化的物件。

備註:

  由應用程式負責確保適當引用所有空白和特殊字元，以避免 shell 注入風險
  。可以使用 "shlex.quote()" 函式來正確跳脫用於構建 shell 命令的字串中
  的空白和特殊字元。


回呼處理
========

class asyncio.Handle

   由 "loop.call_soon()" 和 "loop.call_soon_threadsafe()" 回傳的回呼包
   裝器。

   get_context()

      回傳與處理相關聯的 "contextvars.Context" 物件。

      在 3.12 版被加入.

   cancel()

      取消回呼。如果回呼已被取消或執行，此方法將不起作用。

   cancelled()

      如果回呼已被取消，回傳 "True"。

      在 3.7 版被加入.

class asyncio.TimerHandle

   由 "loop.call_later()" 和 "loop.call_at()" 回傳的回呼包裝器。

   這個類別是 "Handle" 的子類別。

   when()

      回傳預定的回呼時間，以 "float" 秒為單位。

      時間是一個絕對的時間戳，使用與 "loop.time()" 相同的時間參照。

      在 3.7 版被加入.


Server 物件
===========

Server 物件是由 "loop.create_server()"、"loop.create_unix_server()"、
"start_server()" 和 "start_unix_server()" 函式所建立。

請勿直接實例化 "Server" 類別。

class asyncio.Server

   *Server* 物件是非同步情境管理器。當在 "async with" 陳述中使用時，可
   以保證在完成 "async with" 陳述時，Server 物件將會關閉並停止接受新的
   連線：

      srv = await loop.create_server(...)

      async with srv:
          # some code

      # At this point, srv is closed and no longer accepts new connections.

   在 3.7 版的變更: 自 Python 3.7 起，Server 物件是非同步情境管理器。

   在 3.11 版的變更: 此類別在 Python 3.9.11、3.10.3 和 3.11 中以
   "asyncio.Server" 的形式被公開。

   close()

      停止服務：關閉監聽的 sockets 並將 "sockets" 屬性設為 "None"。

      代表現有傳入用戶端連線的 sockets 仍然保持開啟。

      伺服器以非同步方式關閉；使用 "wait_close()" 協程等待伺服器關閉（
      不再有活躍連線）。

   get_loop()

      回傳與伺服器物件關聯的事件迴圈。

      在 3.7 版被加入.

   async start_serving()

      開始接受連線。

      此方法是幂等的，因此可以在伺服器已經運行時呼叫。

      *start_serving* 僅限關鍵字參數只能在 "loop.create_server()" 和
      "asyncio.start_server()" 中使用，允許建立一個最初不接受連線的
      Server 物件。在這種情況下，可以使用 "Server.start_serving()" 或
      "Server.serve_forever()" 來使 Server 開始接受連線。

      在 3.7 版被加入.

   async serve_forever()

      開始接受連線，直到協程被取消。取消 "serve_forever" 任務會導致伺
      服器關閉。

      如果伺服器已經接受連線，則可以呼叫此方法。每個 *Server* 物件只能
      存在一個 "serve_forever" 任務。

      範例：

         async def client_connected(reader, writer):
             # Communicate with the client with
             # reader/writer streams.  For example:
             await reader.readline()

         async def main(host, port):
             srv = await asyncio.start_server(
                 client_connected, host, port)
             await srv.serve_forever()

         asyncio.run(main('127.0.0.1', 0))

      在 3.7 版被加入.

   is_serving()

      如果伺服器正在接受新連線，則回傳 "True"。

      在 3.7 版被加入.

   async wait_closed()

      等待 "close()" 方法完成且所有活動連線都已結束。

   sockets

      伺服器正在監聽的類似 socket 的物件串列，
      "asyncio.trsock.TransportSocket"。

      在 3.7 版的變更: 在 Python 3.7 之前，"Server.sockets" 曾經直接回
      傳內部伺服器 sockets 的串列。在 3.7 中回傳了該串列的副本。


事件迴圈實作
============

asyncio 內附兩個不同的事件迴圈實作："SelectorEventLoop" 和
"ProactorEventLoop"。

預設情況下，asyncio 配置為在 Unix 上使用 "SelectorEventLoop"，在
Windows 上使用 "ProactorEventLoop"。

class asyncio.SelectorEventLoop

   基於 "selectors" 模組的事件迴圈。

   使用特定平台上最有效的 *selector*。也可以手動配置要使用的確切
   selector 實作：

      import asyncio
      import selectors

      class MyPolicy(asyncio.DefaultEventLoopPolicy):
         def new_event_loop(self):
            selector = selectors.SelectSelector()
            return asyncio.SelectorEventLoop(selector)

      asyncio.set_event_loop_policy(MyPolicy())

   Availability: Unix, Windows.

class asyncio.ProactorEventLoop

   用於 Windows 的事件迴圈，使用"I/O 完成埠"(IOCP)。

   Availability: Windows.

   也參考: MSDN documentation on I/O Completion Ports.

class asyncio.AbstractEventLoop

   為符合 asyncio 標準的事件迴圈的抽象基礎類別。

   事件迴圈方法 部分列出了替代 "AbstractEventLoop" 實作應該定義的所有
   方法。


範例
====

請注意，本節中的所有範例都 **故意** 展示如何使用低階事件迴圈 API，如
"loop.run_forever()" 和 "loop.call_soon()"。現代 asyncio 應用程式很少
需要這種方式撰寫；請考慮使用高階的函式，如 "asyncio.run()"。


使用 call_soon() 的 Hello World 範例
------------------------------------

使用 "loop.call_soon()" 方法排程回呼的範例。回呼會顯示 ""Hello World""
，然後停止事件迴圈：

   import asyncio

   def hello_world(loop):
       """A callback to print 'Hello World' and stop the event loop"""
       print('Hello World')
       loop.stop()

   loop = asyncio.new_event_loop()

   # Schedule a call to hello_world()
   loop.call_soon(hello_world, loop)

   # Blocking call interrupted by loop.stop()
   try:
       loop.run_forever()
   finally:
       loop.close()

也參考: 使用協程和 "run()" 函式建立的類似 Hello World 範例。


使用 call_later() 顯示目前日期
------------------------------

一個回呼的範例，每秒顯示目前日期。回呼使用 "loop.call_later()" 方法在
5 秒後重新排程自己，然後停止事件迴圈：

   import asyncio
   import datetime

   def display_date(end_time, loop):
       print(datetime.datetime.now())
       if (loop.time() + 1.0) < end_time:
           loop.call_later(1, display_date, end_time, loop)
       else:
           loop.stop()

   loop = asyncio.new_event_loop()

   # Schedule the first call to display_date()
   end_time = loop.time() + 5.0
   loop.call_soon(display_date, end_time, loop)

   # Blocking call interrupted by loop.stop()
   try:
       loop.run_forever()
   finally:
       loop.close()

也參考: 使用協程和 "run()" 函式建立的類似 current date 範例。


監聽檔案描述器以進行讀取事件
----------------------------

使用 "loop.add_reader()" 方法等待檔案描述器接收到某些資料，然後關閉事
件迴圈：

   import asyncio
   from socket import socketpair

   # Create a pair of connected file descriptors
   rsock, wsock = socketpair()

   loop = asyncio.new_event_loop()

   def reader():
       data = rsock.recv(100)
       print("Received:", data.decode())

       # We are done: unregister the file descriptor
       loop.remove_reader(rsock)

       # Stop the event loop
       loop.stop()

   # Register the file descriptor for read event
   loop.add_reader(rsock, reader)

   # Simulate the reception of data from the network
   loop.call_soon(wsock.send, 'abc'.encode())

   try:
       # Run the event loop
       loop.run_forever()
   finally:
       # We are done. Close sockets and the event loop.
       rsock.close()
       wsock.close()
       loop.close()

也參考:

  * 使用傳輸、協定和 "loop.create_connection()" 方法的類似 範例。

  * 另一個使用高階 "asyncio.open_connection()" 函式和串流的類似 範例。


設定 SIGINT 和 SIGTERM 的訊號處理程式
-------------------------------------

（此 "signals" 範例僅在 Unix 上運作。）

使用 "loop.add_signal_handler()" 方法註冊訊號 "SIGINT" 和 "SIGTERM" 的
處理程式：

   import asyncio
   import functools
   import os
   import signal

   def ask_exit(signame, loop):
       print("got signal %s: exit" % signame)
       loop.stop()

   async def main():
       loop = asyncio.get_running_loop()

       for signame in {'SIGINT', 'SIGTERM'}:
           loop.add_signal_handler(
               getattr(signal, signame),
               functools.partial(ask_exit, signame, loop))

       await asyncio.sleep(3600)

   print("Event loop running for 1 hour, press Ctrl+C to interrupt.")
   print(f"pid {os.getpid()}: send SIGINT or SIGTERM to exit.")

   asyncio.run(main())
