[インデックス 16771] ファイルの概要
このコミットは、Go言語の標準ライブラリnet
パッケージに、TCP接続のKeep-Alive機能をより細かく制御するためのSetKeepAlivePeriod
メソッドを追加するものです。これにより、アプリケーション開発者はTCP Keep-Aliveパケットの送信間隔をプログラムから設定できるようになります。
コミット
commit 918922cf850b13fd8bfe9aacb4e595bcd067c656
Author: David Presotto <presotto@gmail.com>
Date: Mon Jul 15 18:40:55 2013 -0400
net: add SetKeepAlivePeriod
Sets both the duration from the last data packet to the first
keep alive packet and the duration between keep alive packets to be
the passed duration.
I've tested the function on both Darwin (10.8.4) and 4.2 Linux.
I've compiled (make.bash) for all the OS's and tested (all.bash)
on Darwin and Linux.
R=golang-dev, dave, rsc, dvyukov, presotto+facebook, nick
CC=golang-dev, veyron-team
https://golang.org/cl/11130044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/918922cf850b13fd8bfe9aacb4e595bcd067c656
元コミット内容
net: add SetKeepAlivePeriod
このコミットは、最後のデータパケットから最初のKeep-Aliveパケットまでの期間と、Keep-Aliveパケット間の期間の両方を、渡された期間に設定するSetKeepAlivePeriod
関数を追加します。
この機能は、Darwin (10.8.4) と 4.2 Linux の両方でテストされています。すべてのOS向けにコンパイル (make.bash
) し、Darwin と Linux でテスト (all.bash
) 済みです。
変更の背景
TCP Keep-Aliveは、アイドル状態のTCP接続がまだ有効であるかどうかを確認するために使用されるメカニズムです。これは、ネットワークの途中でルーターやファイアウォールがアイドル接続をタイムアウトさせてしまうことを防いだり、接続の片側がクラッシュした場合に他方がそれを検出し、リソースを解放できるようにするために重要です。
Go言語のnet
パッケージには、すでにSetKeepAlive(bool)
メソッドが存在し、Keep-Alive機能を有効/無効にすることができました。しかし、Keep-Aliveパケットの送信間隔を細かく制御する手段がありませんでした。多くのオペレーティングシステム(OS)では、TCP Keep-Aliveの動作を制御するためのソケットオプション(例: TCP_KEEPIDLE
, TCP_KEEPINTVL
, TCP_KEEPCNT
)を提供しています。これらのオプションをGoアプリケーションから直接設定できるようにすることで、開発者は特定のネットワーク環境やアプリケーションの要件に合わせて、Keep-Aliveの動作を最適化できるようになります。
このコミットは、特に長時間アイドル状態になる可能性のある接続(例: データベース接続、WebSocket接続、ストリーミングサービスなど)において、接続の健全性を維持し、不要なタイムアウトやリソースリークを防ぐことを目的としています。
前提知識の解説
TCP Keep-Alive
TCP Keep-Aliveは、TCP/IPプロトコルスタックの一部として提供される機能です。これは、アプリケーションがデータを送信していないアイドル状態のTCP接続が、まだ有効であるかどうかを定期的に確認するために使用されます。
-
目的:
- アイドル接続の検出: ネットワークの途中でルーターやファイアウォールがアイドル接続をタイムアウトさせてしまうことを防ぎます。
- ピアのクラッシュ検出: 接続の片側(ピア)がクラッシュしたり、ネットワークから切断されたりした場合に、他方がそれを検出し、関連するリソースを解放できるようにします。これにより、半開きの接続(half-open connection)がシステムリソースを無駄に消費し続けることを防ぎます。
- NATタイムアウトの回避: ネットワークアドレス変換(NAT)デバイスが、一定期間データが流れない接続をタイムアウトさせるのを防ぐのに役立ちます。
-
動作:
- TCP接続が一定期間アイドル状態になると、Keep-Aliveプローブパケットが送信されます。
- ピアが応答すれば、接続はまだ有効であると判断され、タイマーがリセットされます。
- ピアが応答しない場合、プローブは複数回再送信されます。
- 設定された再試行回数を超えても応答がない場合、接続は切断されたと判断され、アプリケーションにエラーが通知されます。
TCP Keep-Aliveに関連するソケットオプション
オペレーティングシステムによって、TCP Keep-Aliveの動作を制御するためのソケットオプションが提供されています。主なものは以下の通りです。
-
TCP_KEEPALIVE
(macOS/BSD系):- macOSやBSD系のシステムでは、
TCP_KEEPALIVE
オプションがアイドルタイムアウト期間を設定するために使用されます。これは、接続がアイドル状態になってから最初のKeep-Aliveプローブを送信するまでの時間を秒単位で指定します。このオプションは、TCP_KEEPIDLE
とTCP_KEEPINTVL
の両方の役割を兼ねる場合があります。
- macOSやBSD系のシステムでは、
-
TCP_KEEPIDLE
(Linux/POSIX系):- 接続がアイドル状態になってから、最初のKeep-Aliveプローブを送信するまでの時間を秒単位で指定します。
-
TCP_KEEPINTVL
(Linux/POSIX系):- Keep-Aliveプローブが応答されなかった場合に、次のプローブを送信するまでの間隔を秒単位で指定します。
-
TCP_KEEPCNT
(Linux/POSIX系):- 接続が切断されたと判断するまでに、Keep-Aliveプローブを再送信する最大回数を指定します。
これらのオプションは、setsockopt
システムコールを通じて設定されます。Go言語のnet
パッケージは、これらのOS固有のシステムコールを抽象化し、クロスプラットフォームで利用できるAPIを提供します。
Go言語のnet
パッケージとOS固有の実装
Go言語のnet
パッケージは、ネットワークプログラミングのためのプラットフォーム非依存のインターフェースを提供しますが、その内部では各OSのシステムコールを呼び出しています。そのため、TCPソケットオプションのようなOS固有の機能については、OSごとに異なる実装を持つファイル(例: tcpsockopt_darwin.go
, tcpsockopt_linux.go
, tcpsockopt_windows.go
など)が存在します。これらのファイルは、ビルドタグ(+build
ディレクティブ)によって、特定のOSでのみコンパイルされるように制御されています。
技術的詳細
このコミットは、net.TCPConn
型にSetKeepAlivePeriod(d time.Duration)
メソッドを追加し、TCP Keep-Aliveのアイドル期間とプローブ間隔を設定できるようにします。この機能は、OSごとに異なるソケットオプションの挙動を吸収し、統一されたAPIを提供します。
net.TCPConn.SetKeepAlivePeriod
の追加
src/pkg/net/tcpsock_posix.go
に、TCPConn
の新しいメソッドとしてSetKeepAlivePeriod
が追加されます。このメソッドは、内部的にOS固有のsetKeepAlivePeriod
関数を呼び出します。
// src/pkg/net/tcpsock_posix.go
func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
if !c.ok() {
return syscall.EINVAL
}
return setKeepAlivePeriod(c.fd, d)
}
このメソッドは、TCPConn
が有効な状態であるかを確認し、問題がなければnetFD
(ファイルディスクリプタをラップする内部構造体)と指定された期間d
を引数としてsetKeepAlivePeriod
を呼び出します。
OS固有のsetKeepAlivePeriod
の実装
このコミットの主要な変更は、各OS向けにsetKeepAlivePeriod
関数を実装する新しいファイルが追加されたことです。
-
src/pkg/net/tcpsockopt_darwin.go
(macOS)- このファイルは、macOS (Darwin) 向けの
setKeepAlivePeriod
を実装します。 - macOSでは、
syscall.TCP_KEEPALIVE
ソケットオプションを使用してKeep-Aliveのアイドル期間を設定します。 - Goの
time.Duration
を秒単位の整数に変換し、setsockopt
システムコールを呼び出します。 d += (time.Second - time.Nanosecond)
という行は、期間を秒に切り上げるための処理です。例えば、1秒未満の期間が指定された場合でも、少なくとも1秒として扱われるようにします。
// src/pkg/net/tcpsockopt_darwin.go func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // ... (incref/decref for fd) d += (time.Second - time.Nanosecond) // Round up to next highest second secs := int(d.Seconds()) return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)) }
- このファイルは、macOS (Darwin) 向けの
-
src/pkg/net/tcpsockopt_openbsd.go
(OpenBSD)- OpenBSD向けの実装も、macOSと同様に
syscall.TCP_KEEPALIVE
を使用します。コードはtcpsockopt_darwin.go
とほぼ同じです。
// src/pkg/net/tcpsockopt_openbsd.go func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // ... (incref/decref for fd) d += (time.Second - time.Nanosecond) // Round up to next highest second secs := int(d.Seconds()) return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)) }
- OpenBSD向けの実装も、macOSと同様に
-
src/pkg/net/tcpsockopt_posix.go
(FreeBSD, Linux, NetBSD)- このファイルは、Linux、FreeBSD、NetBSDなどのPOSIX準拠システム向けの実装です。
- これらのシステムでは、
TCP_KEEPINTVL
(Keep-Aliveプローブ間の間隔)とTCP_KEEPIDLE
(アイドル期間)の2つのソケットオプションを設定します。 SetKeepAlivePeriod
に渡された期間d
が、これら両方のオプションに適用されます。- ここでも、期間を秒に切り上げる処理が行われます。
// src/pkg/net/tcpsockopt_posix.go // +build freebsd linux netbsd func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // ... (incref/decref for fd) d += (time.Second - time.Nanosecond) // Round up to next highest second secs := int(d.Seconds()) err := os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs)) if err != nil { return err } return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs)) }
-
src/pkg/net/tcpsockopt_windows.go
(Windows)- Windows向けの実装は、他のOSとは異なり、
SetKeepAlivePeriod
が実質的に何もしない(no-op)実装となっています。 - これは、WindowsのTCP Keep-Alive設定がソケット単位ではなく、システム全体の設定に依存しているため、GoのAPIから個々の接続に対して期間を設定することが困難であるためです。エラーを返すのではなく、単に
nil
を返すことで、呼び出し元がエラー処理を簡略化できるようにしています。
// src/pkg/net/tcpsockopt_windows.go func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // ... (incref/decref for fd) // We can't actually set this per connection. Act as a noop rather than an error. return nil }
- Windows向けの実装は、他のOSとは異なり、
クロスプラットフォーム対応の考慮
このコミットは、Go言語が異なるOS環境で動作することを考慮し、各OSのTCP Keep-Alive実装の差異を吸収しています。特に、Windowsでのno-op実装は、プラットフォームの制約を認識しつつ、可能な限り一貫したAPIを提供しようとするGoの設計哲学を反映しています。
コアとなるコードの変更箇所
このコミットによる主要なコード変更は以下のファイルに集中しています。
-
src/pkg/net/tcpsock_posix.go
:TCPConn
構造体にSetKeepAlivePeriod(d time.Duration) error
メソッドが追加されました。このメソッドは、内部的にOS固有のsetKeepAlivePeriod
関数を呼び出すためのエントリポイントとなります。
-
src/pkg/net/tcpsockopt_darwin.go
(新規ファイル):- macOS (Darwin) 向けの
setKeepAlivePeriod
関数が実装されました。syscall.TCP_KEEPALIVE
ソケットオプションを使用してKeep-Alive期間を設定します。
- macOS (Darwin) 向けの
-
src/pkg/net/tcpsockopt_openbsd.go
(新規ファイル):- OpenBSD向けの
setKeepAlivePeriod
関数が実装されました。macOSと同様にsyscall.TCP_KEEPALIVE
を使用します。
- OpenBSD向けの
-
src/pkg/net/tcpsockopt_posix.go
(新規ファイル):- Linux, FreeBSD, NetBSDなどのPOSIX準拠システム向けの
setKeepAlivePeriod
関数が実装されました。syscall.TCP_KEEPINTVL
とsyscall.TCP_KEEPIDLE
の2つのソケットオプションを設定します。
- Linux, FreeBSD, NetBSDなどのPOSIX準拠システム向けの
-
src/pkg/net/tcpsockopt_windows.go
(新規ファイル):- Windows向けの
setKeepAlivePeriod
関数が実装されました。この実装は、Windowsの制約により、実質的に何もしない(no-op)動作となります。
- Windows向けの
コアとなるコードの解説
src/pkg/net/tcpsock_posix.go
の変更
// ...
// SetKeepAliveIdlePeriod sets period between keep alives.
func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
if !c.ok() {
return syscall.EINVAL
}
return setKeepAlivePeriod(c.fd, d)
}
// ...
このコードは、TCPConn
型に新しいパブリックメソッドSetKeepAlivePeriod
を追加しています。
c.ok()
:TCPConn
が有効な状態(閉じられていないなど)であるかを確認します。無効な場合はsyscall.EINVAL
(無効な引数)エラーを返します。c.fd
:netFD
型の内部フィールドで、ネットワークファイルディスクリプタを抽象化したものです。setKeepAlivePeriod(c.fd, d)
: 実際のOS固有のソケットオプション設定を行うプライベート関数を呼び出します。この関数は、ビルドタグによって各OSの実装にディスパッチされます。
OS固有の tcpsockopt_*.go
ファイルの解説
これらのファイルは、Goのビルドシステムにおける「ビルドタグ」の概念を利用しています。ファイルの先頭にある// +build freebsd linux netbsd
のようなコメントは、そのファイルが特定のOSでのみコンパイルされることを示します。これにより、Goは異なるOSのシステムコールやソケットオプションの差異を透過的に扱うことができます。
src/pkg/net/tcpsockopt_darwin.go
および src/pkg/net/tcpsockopt_openbsd.go
// ...
// Set keep alive period.
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
// The kernel expects seconds so round to next highest second.
d += (time.Second - time.Nanosecond)
secs := int(d.Seconds())
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs))
}
fd.incref(false)
/fd.decref()
:netFD
の参照カウントを管理し、ファイルディスクリプタが安全に使用されていることを保証します。これはGoの内部的なリソース管理メカニズムです。d += (time.Second - time.Nanosecond)
:time.Duration
はナノ秒単位で時間を表しますが、カーネルのsetsockopt
は通常秒単位の整数を期待します。この行は、指定された期間を最も近い秒に切り上げるためのトリックです。例えば、1s + (1s - 1ns)
は2s - 1ns
となり、整数にキャストすると1
ではなく2
になります。これにより、0.1s
のような短い期間でも1s
として扱われます。secs := int(d.Seconds())
:time.Duration
を秒単位の浮動小数点数に変換し、それを整数にキャストします。syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)
: 実際のシステムコール呼び出しです。fd.sysfd
: 基となるOSのファイルディスクリプタ。syscall.IPPROTO_TCP
: TCPプロトコルレベルのオプションを指定。syscall.TCP_KEEPALIVE
: 設定するソケットオプション。macOS/OpenBSDでは、これがKeep-Aliveのアイドル期間を制御します。secs
: 設定する期間(秒)。
os.NewSyscallError("setsockopt", ...)
: システムコールがエラーを返した場合に、Goのエラー型にラップして返します。
src/pkg/net/tcpsockopt_posix.go
// +build freebsd linux netbsd
// ...
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
// ... (incref/decref for fd)
d += (time.Second - time.Nanosecond)
secs := int(d.Seconds())
err := os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs))
if err != nil {
return err
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs))
}
このファイルは、LinuxなどのPOSIXシステム向けです。
syscall.TCP_KEEPINTVL
: Keep-Aliveプローブ間の間隔を設定します。syscall.TCP_KEEPIDLE
: 接続がアイドル状態になってから最初のKeep-Aliveプローブを送信するまでの時間を設定します。- この実装では、
SetKeepAlivePeriod
に渡された単一の期間d
を、TCP_KEEPINTVL
とTCP_KEEPIDLE
の両方に設定しています。これは、コミットメッセージにある「Sets both the duration from the last data packet to the first keep alive packet and the duration between keep alive packets to be the passed duration.」という説明と一致します。
src/pkg/net/tcpsockopt_windows.go
// ...
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
// We can't actually set this per connection. Act as a noop rather than an error.
return nil
}
Windowsでは、TCP Keep-Aliveの期間を個々のソケットに対して設定する直接的なAPIが提供されていないため、この関数は単にnil
(エラーなし)を返します。これは、Goがプラットフォームの制約を認識し、可能な限り一貫したAPIを提供しようとするアプローチを示しています。アプリケーション開発者は、SetKeepAlivePeriod
を呼び出してもWindows上では効果がないことを認識しておく必要があります。
関連リンク
- Go言語の
net
パッケージドキュメント: https://pkg.go.dev/net - TCP Keep-Aliveに関するWikipedia記事: https://en.wikipedia.org/wiki/TCP_keepalive
setsockopt
システムコールに関するmanページ (Linux):man 7 tcp
(TCP_KEEPALIVE, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNTのセクションを参照)
参考にした情報源リンク
- Go言語のソースコード (特に
src/pkg/net
ディレクトリ) - TCP Keep-Aliveに関する一般的なネットワークプロトコルの知識
- 各OSの
setsockopt
に関するドキュメント (例: Linux man pages, Apple Developer Documentation) - Go言語のコードレビューシステム (Gerrit) の変更セット: https://golang.org/cl/11130044
[インデックス 16771] ファイルの概要
このコミットは、Go言語の標準ライブラリnet
パッケージに、TCP接続のKeep-Alive機能をより細かく制御するためのSetKeepAlivePeriod
メソッドを追加するものです。これにより、アプリケーション開発者はTCP Keep-Aliveパケットの送信間隔をプログラムから設定できるようになります。
コミット
commit 918922cf850b13fd8bfe9aacb4e595bcd067c656
Author: David Presotto <presotto@gmail.com>
Date: Mon Jul 15 18:40:55 2013 -0400
net: add SetKeepAlivePeriod
Sets both the duration from the last data packet to the first
keep alive packet and the duration between keep alive packets to be
the passed duration.
I've tested the function on both Darwin (10.8.4) and 4.2 Linux.
I've compiled (make.bash) for all the OS's and tested (all.bash)
on Darwin and Linux.
R=golang-dev, dave, rsc, dvyukov, presotto+facebook, nick
CC=golang-dev, veyron-team
https://golang.org/cl/11130044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/918922cf850b13fd8bfe9aacb4e595bcd067c656
元コミット内容
net: add SetKeepAlivePeriod
このコミットは、最後のデータパケットから最初のKeep-Aliveパケットまでの期間と、Keep-Aliveパケット間の期間の両方を、渡された期間に設定するSetKeepAlivePeriod
関数を追加します。
この機能は、Darwin (10.8.4) と 4.2 Linux の両方でテストされています。すべてのOS向けにコンパイル (make.bash
) し、Darwin と Linux でテスト (all.bash
) 済みです。
変更の背景
TCP Keep-Aliveは、アイドル状態のTCP接続がまだ有効であるかどうかを確認するために使用されるメカニズムです。これは、ネットワークの途中でルーターやファイアウォールがアイドル接続をタイムアウトさせてしまうことを防いだり、接続の片側がクラッシュした場合に他方がそれを検出し、リソースを解放できるようにするために重要です。
Go言語のnet
パッケージには、すでにSetKeepAlive(bool)
メソッドが存在し、Keep-Alive機能を有効/無効にすることができました。しかし、Keep-Aliveパケットの送信間隔を細かく制御する手段がありませんでした。多くのオペレーティングシステム(OS)では、TCP Keep-Aliveの動作を制御するためのソケットオプション(例: TCP_KEEPIDLE
, TCP_KEEPINTVL
, TCP_KEEPCNT
)を提供しています。これらのオプションをGoアプリケーションから直接設定できるようにすることで、開発者は特定のネットワーク環境やアプリケーションの要件に合わせて、Keep-Aliveの動作を最適化できるようになります。
このコミットは、特に長時間アイドル状態になる可能性のある接続(例: データベース接続、WebSocket接続、ストリーミングサービスなど)において、接続の健全性を維持し、不要なタイムアウトやリソースリークを防ぐことを目的としています。
前提知識の解説
TCP Keep-Alive
TCP Keep-Aliveは、TCP/IPプロトコルスタックの一部として提供される機能です。これは、アプリケーションがデータを送信していないアイドル状態のTCP接続が、まだ有効であるかどうかを定期的に確認するために使用されます。
-
目的:
- アイドル接続の検出: ネットワークの途中でルーターやファイアウォールがアイドル接続をタイムアウトさせてしまうことを防ぎます。
- ピアのクラッシュ検出: 接続の片側(ピア)がクラッシュしたり、ネットワークから切断されたりした場合に、他方がそれを検出し、関連するリソースを解放できるようにします。これにより、半開きの接続(half-open connection)がシステムリソースを無駄に消費し続けることを防ぎます。
- NATタイムアウトの回避: ネットワークアドレス変換(NAT)デバイスが、一定期間データが流れない接続をタイムアウトさせるのを防ぐのに役立ちます。
-
動作:
- TCP接続が一定期間アイドル状態になると、Keep-Aliveプローブパケットが送信されます。
- ピアが応答すれば、接続はまだ有効であると判断され、タイマーがリセットされます。
- ピアが応答しない場合、プローブは複数回再送信されます。
- 設定された再試行回数を超えても応答がない場合、接続は切断されたと判断され、アプリケーションにエラーが通知されます。
TCP Keep-Aliveに関連するソケットオプション
オペレーティングシステムによって、TCP Keep-Aliveの動作を制御するためのソケットオプションが提供されています。主なものは以下の通りです。
-
TCP_KEEPALIVE
(macOS/BSD系):- macOSやBSD系のシステムでは、
TCP_KEEPALIVE
オプションがアイドルタイムアウト期間を設定するために使用されます。これは、接続がアイドル状態になってから最初のKeep-Aliveプローブを送信するまでの時間を秒単位で指定します。このオプションは、TCP_KEEPIDLE
とTCP_KEEPINTVL
の両方の役割を兼ねる場合があります。
- macOSやBSD系のシステムでは、
-
TCP_KEEPIDLE
(Linux/POSIX系):- 接続がアイドル状態になってから、最初のKeep-Aliveプローブを送信するまでの時間を秒単位で指定します。
-
TCP_KEEPINTVL
(Linux/POSIX系):- Keep-Aliveプローブが応答されなかった場合に、次のプローブを送信するまでの間隔を秒単位で指定します。
-
TCP_KEEPCNT
(Linux/POSIX系):- 接続が切断されたと判断するまでに、Keep-Aliveプローブを再送信する最大回数を指定します。
これらのオプションは、setsockopt
システムコールを通じて設定されます。Go言語のnet
パッケージは、これらのOS固有のシステムコールを抽象化し、クロスプラットフォームで利用できるAPIを提供します。
Go言語のnet
パッケージとOS固有の実装
Go言語のnet
パッケージは、ネットワークプログラミングのためのプラットフォーム非依存のインターフェースを提供しますが、その内部では各OSのシステムコールを呼び出しています。そのため、TCPソケットオプションのようなOS固有の機能については、OSごとに異なる実装を持つファイル(例: tcpsockopt_darwin.go
, tcpsockopt_linux.go
, tcpsockopt_windows.go
など)が存在します。これらのファイルは、ビルドタグ(+build
ディレクティブ)によって、特定のOSでのみコンパイルされるように制御されています。
技術的詳細
このコミットは、net.TCPConn
型にSetKeepAlivePeriod(d time.Duration)
メソッドを追加し、TCP Keep-Aliveのアイドル期間とプローブ間隔を設定できるようにします。この機能は、OSごとに異なるソケットオプションの挙動を吸収し、統一されたAPIを提供します。
net.TCPConn.SetKeepAlivePeriod
の追加
src/pkg/net/tcpsock_posix.go
に、TCPConn
の新しいメソッドとしてSetKeepAlivePeriod
が追加されます。このメソッドは、内部的にOS固有のsetKeepAlivePeriod
関数を呼び出します。
// src/pkg/net/tcpsock_posix.go
func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
if !c.ok() {
return syscall.EINVAL
}
return setKeepAlivePeriod(c.fd, d)
}
このメソッドは、TCPConn
が有効な状態であるかを確認し、問題がなければnetFD
(ファイルディスクリプタをラップする内部構造体)と指定された期間d
を引数としてsetKeepAlivePeriod
を呼び出します。
OS固有のsetKeepAlivePeriod
の実装
このコミットの主要な変更は、各OS向けにsetKeepAlivePeriod
関数を実装する新しいファイルが追加されたことです。
-
src/pkg/net/tcpsockopt_darwin.go
(macOS)- このファイルは、macOS (Darwin) 向けの
setKeepAlivePeriod
を実装します。 - macOSでは、
syscall.TCP_KEEPALIVE
ソケットオプションを使用してKeep-Aliveのアイドル期間を設定します。 - Goの
time.Duration
を秒単位の整数に変換し、setsockopt
システムコールを呼び出します。 d += (time.Second - time.Nanosecond)
という行は、期間を秒に切り上げるための処理です。例えば、1秒未満の期間が指定された場合でも、少なくとも1秒として扱われるようにします。
// src/pkg/net/tcpsockopt_darwin.go func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // ... (incref/decref for fd) d += (time.Second - time.Nanosecond) // Round up to next highest second secs := int(d.Seconds()) return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)) }
- このファイルは、macOS (Darwin) 向けの
-
src/pkg/net/tcpsockopt_openbsd.go
(OpenBSD)- OpenBSD向けの実装も、macOSと同様に
syscall.TCP_KEEPALIVE
を使用します。コードはtcpsockopt_darwin.go
とほぼ同じです。
// src/pkg/net/tcpsockopt_openbsd.go func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // ... (incref/decref for fd) d += (time.Second - time.Nanosecond) // Round up to next highest second secs := int(d.Seconds()) return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)) }
- OpenBSD向けの実装も、macOSと同様に
-
src/pkg/net/tcpsockopt_posix.go
(FreeBSD, Linux, NetBSD)- このファイルは、Linux、FreeBSD、NetBSDなどのPOSIX準拠システム向けの実装です。
- これらのシステムでは、
TCP_KEEPINTVL
(Keep-Aliveプローブ間の間隔)とTCP_KEEPIDLE
(アイドル期間)の2つのソケットオプションを設定します。 SetKeepAlivePeriod
に渡された期間d
が、これら両方のオプションに適用されます。- ここでも、期間を秒に切り上げる処理が行われます。
// src/pkg/net/tcpsockopt_posix.go // +build freebsd linux netbsd func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // ... (incref/decref for fd) d += (time.Second - time.Nanosecond) // Round up to next highest second secs := int(d.Seconds()) err := os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs)) if err != nil { return err } return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs)) }
-
src/pkg/net/tcpsockopt_windows.go
(Windows)- Windows向けの実装は、他のOSとは異なり、
SetKeepAlivePeriod
が実質的に何もしない(no-op)実装となっています。 - これは、WindowsのTCP Keep-Alive設定がソケット単位ではなく、システム全体の設定に依存しているため、GoのAPIから個々の接続に対して期間を設定することが困難であるためです。エラーを返すのではなく、単に
nil
を返すことで、呼び出し元がエラー処理を簡略化できるようにしています。
// src/pkg/net/tcpsockopt_windows.go func setKeepAlivePeriod(fd *netFD, d time.Duration) error { // ... (incref/decref for fd) // We can't actually set this per connection. Act as a noop rather than an error. return nil }
- Windows向けの実装は、他のOSとは異なり、
クロスプラットフォーム対応の考慮
このコミットは、Go言語が異なるOS環境で動作することを考慮し、各OSのTCP Keep-Alive実装の差異を吸収しています。特に、Windowsでのno-op実装は、プラットフォームの制約を認識しつつ、可能な限り一貫したAPIを提供しようとするGoの設計哲学を反映しています。
コアとなるコードの変更箇所
このコミットによる主要なコード変更は以下のファイルに集中しています。
-
src/pkg/net/tcpsock_posix.go
:TCPConn
構造体にSetKeepAlivePeriod(d time.Duration) error
メソッドが追加されました。このメソッドは、内部的にOS固有のsetKeepAlivePeriod
関数を呼び出すためのエントリポイントとなります。
-
src/pkg/net/tcpsockopt_darwin.go
(新規ファイル):- macOS (Darwin) 向けの
setKeepAlivePeriod
関数が実装されました。syscall.TCP_KEEPALIVE
ソケットオプションを使用してKeep-Alive期間を設定します。
- macOS (Darwin) 向けの
-
src/pkg/net/tcpsockopt_openbsd.go
(新規ファイル):- OpenBSD向けの
setKeepAlivePeriod
関数が実装されました。macOSと同様にsyscall.TCP_KEEPALIVE
を使用します。
- OpenBSD向けの
-
src/pkg/net/tcpsockopt_posix.go
(新規ファイル):- Linux, FreeBSD, NetBSDなどのPOSIX準拠システム向けの
setKeepAlivePeriod
関数が実装されました。syscall.TCP_KEEPINTVL
とsyscall.TCP_KEEPIDLE
の2つのソケットオプションを設定します。
- Linux, FreeBSD, NetBSDなどのPOSIX準拠システム向けの
-
src/pkg/net/tcpsockopt_windows.go
(新規ファイル):- Windows向けの
setKeepAlivePeriod
関数が実装されました。この実装は、Windowsの制約により、実質的に何もしない(no-op)動作となります。
- Windows向けの
コアとなるコードの解説
src/pkg/net/tcpsock_posix.go
の変更
// ...
// SetKeepAliveIdlePeriod sets period between keep alives.
func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error {
if !c.ok() {
return syscall.EINVAL
}
return setKeepAlivePeriod(c.fd, d)
}
// ...
このコードは、TCPConn
型に新しいパブリックメソッドSetKeepAlivePeriod
を追加しています。
c.ok()
:TCPConn
が有効な状態(閉じられていないなど)であるかを確認します。無効な場合はsyscall.EINVAL
(無効な引数)エラーを返します。c.fd
:netFD
型の内部フィールドで、ネットワークファイルディスクリプタを抽象化したものです。setKeepAlivePeriod(c.fd, d)
: 実際のOS固有のソケットオプション設定を行うプライベート関数を呼び出します。この関数は、ビルドタグによって各OSの実装にディスパッチされます。
OS固有の tcpsockopt_*.go
ファイルの解説
これらのファイルは、Goのビルドシステムにおける「ビルドタグ」の概念を利用しています。ファイルの先頭にある// +build freebsd linux netbsd
のようなコメントは、そのファイルが特定のOSでのみコンパイルされることを示します。これにより、Goは異なるOSのシステムコールやソケットオプションの差異を透過的に扱うことができます。
src/pkg/net/tcpsockopt_darwin.go
および src/pkg/net/tcpsockopt_openbsd.go
// ...
// Set keep alive period.
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
// The kernel expects seconds so round to next highest second.
d += (time.Second - time.Nanosecond)
secs := int(d.Seconds())
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs))
}
fd.incref(false)
/fd.decref()
:netFD
の参照カウントを管理し、ファイルディスクリプタが安全に使用されていることを保証します。これはGoの内部的なリソース管理メカニズムです。d += (time.Second - time.Nanosecond)
:time.Duration
はナノ秒単位で時間を表しますが、カーネルのsetsockopt
は通常秒単位の整数を期待します。この行は、指定された期間を最も近い秒に切り上げるためのトリックです。例えば、1s + (1s - 1ns)
は2s - 1ns
となり、整数にキャストすると1
ではなく2
になります。これにより、0.1s
のような短い期間でも1s
として扱われます。secs := int(d.Seconds())
:time.Duration
を秒単位の浮動小数点数に変換し、それを整数にキャストします。syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)
: 実際のシステムコール呼び出しです。fd.sysfd
: 基となるOSのファイルディスクリプタ。syscall.IPPROTO_TCP
: TCPプロトコルレベルのオプションを指定。syscall.TCP_KEEPALIVE
: 設定するソケットオプション。macOS/OpenBSDでは、これがKeep-Aliveのアイドル期間を制御します。secs
: 設定する期間(秒)。
os.NewSyscallError("setsockopt", ...)
: システムコールがエラーを返した場合に、Goのエラー型にラップして返します。
src/pkg/net/tcpsockopt_posix.go
// +build freebsd linux netbsd
// ...
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
// ... (incref/decref for fd)
d += (time.Second - time.Nanosecond)
secs := int(d.Seconds())
err := os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs))
if err != nil {
return err
}
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs))
}
このファイルは、LinuxなどのPOSIXシステム向けです。
syscall.TCP_KEEPINTVL
: Keep-Aliveプローブ間の間隔を設定します。syscall.TCP_KEEPIDLE
: 接続がアイドル状態になってから最初のKeep-Aliveプローブを送信するまでの時間を設定します。- この実装では、
SetKeepAlivePeriod
に渡された単一の期間d
を、TCP_KEEPINTVL
とTCP_KEEPIDLE
の両方に設定しています。これは、コミットメッセージにある「Sets both the duration from the last data packet to the first keep alive packet and the duration between keep alive packets to be the passed duration.」という説明と一致します。
src/pkg/net/tcpsockopt_windows.go
// ...
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
// We can't actually set this per connection. Act as a noop rather than an error.
return nil
}
Windowsでは、TCP Keep-Aliveの期間を個々のソケットに対して設定する直接的なAPIが提供されていないため、この関数は単にnil
(エラーなし)を返します。これは、Goがプラットフォームの制約を認識し、可能な限り一貫したAPIを提供しようとするアプローチを示しています。アプリケーション開発者は、SetKeepAlivePeriod
を呼び出してもWindows上では効果がないことを認識しておく必要があります。
関連リンク
- Go言語の
net
パッケージドキュメント: https://pkg.go.dev/net - TCP Keep-Aliveに関するWikipedia記事: https://en.wikipedia.org/wiki/TCP_keepalive
setsockopt
システムコールに関するmanページ (Linux):man 7 tcp
(TCP_KEEPALIVE, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNTのセクションを参照)
参考にした情報源リンク
- Go言語のソースコード (特に
src/pkg/net
ディレクトリ) - TCP Keep-Aliveに関する一般的なネットワークプロトコルの知識
- 各OSの
setsockopt
に関するドキュメント (例: Linux man pages, Apple Developer Documentation) - Go言語のコードレビューシステム (Gerrit) の変更セット: https://golang.org/cl/11130044