基本情報技術者試験問題に挑戦しよう!「ポート番号」~Pythonでつくって学ぶ~【GitHub対応】

GitHubでつくって学ぶ

基本情報技術者試験とは

基本情報技術者試験は、情報処理推進機構(IPA)が実施し、「情報処理の促進に関する法律」に基づき経済産業省が認定する国家試験である。

ITエンジニアとしてキャリアをスタートする方のIT基礎力を問う試験で、「ITエンジニアの登竜門」とも呼ばれる。
「基本情報技術者試験」の試験範囲は、IPAのWebサイトに掲載されているシラバスに明記されている。
それでは、基本情報技術者試験(科目A)のサンプル問題にチャレンジしてみよう。


今回は、解説を読むだけでなく、実際にPCの中でサーバーとクライアントの通信をPythonで再現し、双方が使っているポート番号をコードで確かめるという形で理解を深める。(2026.9.24追記)

なお、この記事で使用するコードは、筆者の学習用リポジトリ pc-labo-fe-exam-python の 06_port_number フォルダにまとめている。

問29

PC とWeb サーバがHTTP で通信している。PC からWeb サーバ宛てのパケットでは,送信元ポート番号はPC 側で割り当てた50001,宛先ポート番号は80 であった。WebサーバからPC への戻りのパケットでのポート番号の組合せはどれか。

出典:基本情報技術者試験 科目A サンプル問題 問29(2022年12月)

正解:ア

ポート番号

TCP/IPのネットワークでは、IPアドレスで通信相手のコンピューターを特定する。そのコンピューター上では、複数のアプリケーションが動作している。ポート番号は、通信相手のコンピューター上で稼働しているアプリケーションを識別する番号である。

0から65,535までのポート番号が割り当てられており、そのうち、0から1,023まではウェルノウンポートと呼ばれ、特定のアプリケーションのために予約されている。

20 FTPデータ転送
21 FTPデータ制御
25 SMTP
53 DNS
80 HTTP
110 POP3

本問では、PCからWebサーバへのパケットでは、送信元ポート番号は50001、宛先ポート番号は80である。


以後、2026.9.24追記

つまり、行きのパケットと戻りのパケットとでは、送信元と宛先が入れ替わるというのが、この問題の核心である。それでは、実際に手元のPCで通信を再現し、割り当てられるポート番号をコードで見てみよう。

Pythonでつくって確かめてみよう

Pythonの標準ライブラリ socket を使うと、サーバーとクライアントの通信を1つのスクリプトの中で再現できる。ここでは、threading モジュールでサーバーを別スレッドとして立ち上げ、クライアントから接続して、双方が実際に使っているポート番号を確認する。

なお、ここでは80番ポートの代わりに、一般的に利用しやすい8080番ポートを使う。
「番号が違うだけで、確認したい仕組みは同じ」という点に注目してほしい。

import socket
import threading

SERVER_PORT = 8080  # 80番の代わりに、特権なしで使えるポートを使う


def run_server(ready_event, result):
    server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_sock.bind(("localhost", SERVER_PORT))
    server_sock.listen(1)
    ready_event.set()  # 準備完了をクライアントに知らせる

    conn, _ = server_sock.accept()
    result["server_local"] = conn.getsockname()    # サーバー自身のアドレスとポート
    result["server_remote"] = conn.getpeername()   # 接続してきたクライアントのアドレスとポート
    conn.close()
    server_sock.close()


def run_client(ready_event, result):
    ready_event.wait()  # サーバーの準備ができるまで待つ
    client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_sock.connect(("localhost", SERVER_PORT))
    result["client_local"] = client_sock.getsockname()   # クライアント自身のアドレスとポート(OSが自動割り当て)
    result["client_remote"] = client_sock.getpeername()  # 接続先のサーバーのアドレスとポート
    client_sock.close()


def main():
    ready_event = threading.Event()
    result = {}

    server_thread = threading.Thread(target=run_server, args=(ready_event, result))
    server_thread.start()

    run_client(ready_event, result)
    server_thread.join()

    print("=== クライアント(PC)側から見た通信 ===")
    print(f"送信元(自分のポート、OSが割り当て): {result['client_local']}")
    print(f"宛先(サーバーのポート)        : {result['client_remote']}")
    print()
    print("=== サーバー(Webサーバー)側から見た通信 ===")
    print(f"自分のポート(=クライアントからの宛先): {result['server_local']}")
    print(f"クライアントのポート(=クライアントの送信元): {result['server_remote']}")
    print()
    print("=== 戻りのパケットで使われるポート番号 ===")
    print(f"送信元ポート(サーバー自身): {result['server_local'][1]}")
    print(f"宛先ポート(クライアントのポート): {result['server_remote'][1]}")


if __name__ == "__main__":
    main()

実行結果

=== クライアント(PC)側から見た通信 ===
送信元(自分のポート、OSが割り当て): ('127.0.0.1', 54118)
宛先(サーバーのポート)        : ('127.0.0.1', 8080)

=== サーバー(Webサーバー)側から見た通信 ===
自分のポート(=クライアントからの宛先): ('127.0.0.1', 8080)
クライアントのポート(=クライアントの送信元): ('127.0.0.1', 54118)

=== 戻りのパケットで使われるポート番号 ===
送信元ポート(サーバー自身): 8080
宛先ポート(クライアントのポート): 54118

※ クライアントのポート番号(例では54118)は、実行するたびにOSが自動的に割り当てるため、毎回変わる。設問の「50001」も、これと同じ仕組みでPC側にその都度割り当てられた番号である。

client_sock.getsockname() は、クライアント自身が使っているアドレスとポートを返す。今回はどこにも番号を指定していないのに、OSが自動的に空いているポート(例では54118)を割り当てていることが確認できる。これが設問の「PC側で割り当てた50001」に対応する。

client_sock.getpeername() は、接続先(サーバー)のアドレスとポートを返す。こちらは SERVER_PORT(8080)を明示的に指定して connect() しているので、常に同じ値になる。これが設問の「宛先ポート番号は80」に対応する。

サーバー側では、conn.getsockname()(自分自身のポート=8080)と conn.getpeername()(クライアントのポート=54118)が、クライアント側の値とちょうど入れ替わった形で得られている。

サーバーがPCへ戻りのパケットを送るとき、サーバーは自分のポート(8080)を送信元として、クライアントのポート(54118)を宛先として送る。これは、設問で言えば「送信元ポート番号は80、宛先ポート番号は50001」とまったく同じ構造である。コードの実行結果として、行きと戻りでポート番号の送信元・宛先が入れ替わる様子を、実際の数値で確認できた。

まとめ

  • ポート番号は、通信相手のコンピューター上で稼働しているアプリケーションを識別する番号であり、0〜1,023番はウェルノウンポートとして予約されている
  • クライアント側のポート番号は、通信のたびにOSが動的に割り当てる。サーバー側のポート番号は、サービスに応じてあらかじめ決められていることが多い(HTTPなら80番など)
  • 行きのパケットと戻りのパケットとでは、送信元ポートと宛先ポートが入れ替わる
  • Pythonの socket モジュールで簡単なサーバーとクライアントを動かし、getsockname() / getpeername() を使うことで、この仕組みを実際のポート番号として目で確認できる

参考リポジトリ

この記事で扱ったコードは、以下の筆者のGitHubリポジトリで公開している。

GitHub – HappyTalk10/pc-labo-fe-exam-python
Contribute to HappyTalk10/pc-labo-fe-exam-python development by creating an account on GitHub.

コメント

タイトルとURLをコピーしました