[インデックス 15169] ファイルの概要
このコミットは、Go言語のnet
パッケージにおけるlistenerSockaddr
関数の重複コードを削除し、Unix系システム向けの共通実装としてsrc/pkg/net/sock_unix.go
に集約するものです。これにより、コードの重複が解消され、保守性と一貫性が向上しています。
コミット
- コミットハッシュ:
dc6e51cfd22336654f5a3ab5b3dcbbe45d998f1a
- 作者: Mikio Hara mikioh.mikioh@gmail.com
- コミット日時: 2013年2月8日 金曜日 17:02:08 +0900
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dc6e51cfd22336654f5a3ab5b3dcbbe45d998f1a
元コミット内容
net: delete duplicate listenerSockaddr
R=golang-dev, dave
CC=golang-dev
https://golang.org/cl/7299067
変更の背景
Go言語のnet
パッケージは、ネットワーク通信を扱うための基本的な機能を提供します。このパッケージ内には、ソケットのリスニング(接続待ち受け)に関連する処理を行うlistenerSockaddr
という関数が存在していました。しかし、この関数は、BSD系OS (sock_bsd.go
)、Linux (sock_linux.go
)、Windows (sock_windows.go
) といった複数のOS固有のファイルに、ほぼ同じ内容で重複して実装されていました。
コードの重複は、以下のような問題を引き起こします。
- 保守性の低下: 同じロジックが複数箇所に存在するため、バグ修正や機能追加の際に、すべての重複箇所を更新する必要があり、見落としや不整合のリスクが高まります。
- コード量の増加: 不要なコードの繰り返しにより、全体のコードベースが肥大化します。
- 一貫性の欠如: 将来的に各OS固有のファイルで個別に変更が加えられた場合、ロジックに差異が生じ、予期せぬ動作やバグにつながる可能性があります。
このコミットは、このようなコードの重複を解消し、Unix系OS(Darwin, FreeBSD, Linux, NetBSD, OpenBSD)で共通するlistenerSockaddr
のロジックをsrc/pkg/net/sock_unix.go
という新しいファイルに集約することで、上記の課題を解決することを目的としています。Windows固有のロジックはsock_windows.go
に残しつつ、重複部分を削減しています。
前提知識の解説
Go言語のnet
パッケージ
net
パッケージは、Go言語でTCP/IPネットワークプログラミングを行うための主要なパッケージです。クライアントとサーバーの両方の機能を提供し、TCP、UDP、Unixドメインソケットなどのプロトコルをサポートします。ソケットの作成、接続、リスニング、データの送受信といった低レベルな操作を抽象化し、使いやすいAPIを提供しています。
syscall.Sockaddr
syscall.Sockaddr
は、Go言語のsyscall
パッケージで定義されているインターフェースで、ソケットアドレスを表します。ソケットアドレスは、IPアドレスとポート番号(TCP/UDPの場合)や、ファイルパス(Unixドメインソケットの場合)など、ソケットが通信相手を識別するために必要な情報を含みます。OSによってソケットアドレスの構造が異なるため、syscall
パッケージは各OSのネイティブなソケットアドレス構造をGoの型にマッピングしています。例えば、IPv4アドレスとポート番号はsyscall.SockaddrInet4
、IPv6アドレスとポート番号はsyscall.SockaddrInet6
、Unixドメインソケットのパスはsyscall.SockaddrUnix
といった具体的な型で表現されます。
listenerSockaddr
関数の役割
listenerSockaddr
関数は、Goのnet
パッケージ内部で、ソケットのリスニングを開始する前に、そのソケットに適切なオプションを設定するために使用される補助関数です。具体的には、以下の処理を行います。
- デフォルトのリスナーソケットオプション設定: TCPやUnixドメインソケットの場合、
setDefaultListenerSockopts
を呼び出して、再利用可能なアドレス(SO_REUSEADDR
)などの一般的なソケットオプションを設定します。 - マルチキャストソケットオプション設定: UDPソケットでマルチキャストアドレスを使用する場合、
setDefaultMulticastSockopts
を呼び出して、マルチキャストグループへの参加やTTL(Time-To-Live)などのオプションを設定します。また、マルチキャストIPアドレスをIPv4zero
(0.0.0.0)やIPv6unspecified
(::)に設定し、任意のインターフェースからのマルチキャストパケットを受け入れられるように調整します。 - アドレス変換:
syscall.Sockaddr
からGoのnet.Addr
型(例:*net.TCPAddr
,*net.UDPAddr
,*net.UnixAddr
)への変換を行い、その情報に基づいて適切なソケットオプションを適用します。
ビルドタグ (+build
)
Go言語では、ファイルの先頭に+build
ディレクティブを記述することで、特定の環境(OS、アーキテクチャなど)でのみコンパイルされるように制御できます。これを「ビルドタグ」と呼びます。例えば、// +build linux
と書かれたファイルはLinux環境でのみコンパイルされ、// +build darwin freebsd linux netbsd openbsd
と書かれたファイルは、指定されたUnix系OSのいずれかでコンパイルされます。この機能は、OS固有のシステムコールやAPIを使用する際に、クロスプラットフォーム対応を容易にするために不可欠です。
コードの重複とその問題点
前述の通り、コードの重複はソフトウェア開発において避けるべきプラクティスです。DRY (Don't Repeat Yourself) 原則は、同じ情報を複数回記述することを避けるべきであるというソフトウェア開発の原則です。この原則に従うことで、コードの保守性、可読性、拡張性が向上します。
技術的詳細
このコミットの主要な技術的変更は、listenerSockaddr
関数の実装をOS固有のファイルから共通のファイルに移動し、重複を排除することです。
変更前: 各OS固有ファイルでの重複実装
変更前は、src/pkg/net/sock_bsd.go
(BSD系OS), src/pkg/net/sock_linux.go
(Linux), src/pkg/net/sock_windows.go
(Windows) のそれぞれに、ほぼ同一のlistenerSockaddr
関数が定義されていました。これらの関数は、引数としてソケットディスクリプタs
、アドレスファミリーf
、ローカルソケットアドレスla
、そしてsyscall.Sockaddr
をnet.Addr
に変換する関数toAddr
を受け取ります。
各実装は、net.Addr
の型(*TCPAddr
, *UnixAddr
, *UDPAddr
)に応じて異なる処理を行っていました。
- TCPAddr / UnixAddr:
setDefaultListenerSockopts(s)
を呼び出して、一般的なリスナーソケットオプションを設定します。 - UDPAddr:
IP.IsMulticast()
でマルチキャストアドレスかどうかをチェックします。- マルチキャストの場合、
setDefaultMulticastSockopts(s)
を呼び出してマルチキャスト関連のオプションを設定します。 - アドレスファミリー(
syscall.AF_INET
またはsyscall.AF_INET6
)に応じて、IPアドレスをIPv4zero
またはIPv6unspecified
に設定し、任意のインターフェースからのマルチキャストパケットを受け入れられるようにします。 - 最後に、
v.sockaddr(f)
を呼び出して、更新されたアドレス情報でsyscall.Sockaddr
を再構築します。
このロジックが各OS固有のファイルで繰り返されていたため、非効率的でした。
変更後: sock_unix.go
への集約とsock_windows.go
の修正
このコミットでは、以下の変更が行われました。
-
src/pkg/net/sock_unix.go
の新規作成:- このファイルは、
// +build darwin freebsd linux netbsd openbsd
というビルドタグを持ち、Unix系OSでのみコンパイルされます。 - 重複していた
listenerSockaddr
関数のロジックがこのファイルに完全に移動されました。これにより、Unix系OS向けの共通実装が提供されます。
- このファイルは、
-
src/pkg/net/sock_bsd.go
およびsrc/pkg/net/sock_linux.go
からの削除:- これらのファイルから
listenerSockaddr
関数が完全に削除されました。これにより、大幅なコード削減が実現されました。
- これらのファイルから
-
src/pkg/net/sock_windows.go
の修正:- Windows固有の
listenerSockaddr
関数は残されましたが、Unix系OSと共通するロジック部分が削除され、より簡潔な形に修正されました。特に、switch v := a.(type)
の変数をv
からa
に変更するなど、新しいsock_unix.go
のスタイルに合わせるための微調整も行われています。
- Windows固有の
-
src/pkg/net/sock_posix.go
の修正:- このファイルでは、コメントの削除(
// Sockets
から// Generic POSIX socket creation.
への変更)と、ビルドタグの調整が行われています。listenerSockaddr
関数自体は含まれていませんが、関連するコードの整理が行われたものと考えられます。
- このファイルでは、コメントの削除(
変更のメリット
この変更により、以下のメリットが得られます。
- コードの重複排除:
listenerSockaddr
のロジックが単一の場所に集約され、コードベースがよりDRYになりました。 - 保守性の向上: 今後
listenerSockaddr
のロジックに変更が必要になった場合、sock_unix.go
の1箇所を修正するだけで済み、バグの混入リスクが低減されます。 - コードの明確化: 各OS固有のファイルが、そのOSに特有のロジックのみを含むようになり、コードの役割がより明確になりました。
- 一貫性の確保: Unix系OS間での
listenerSockaddr
の動作が一貫していることが保証されます。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルと、その変更内容は以下の通りです。
src/pkg/net/sock_bsd.go
--- a/src/pkg/net/sock_bsd.go
+++ b/src/pkg/net/sock_bsd.go
@@ -4,8 +4,6 @@
// +build darwin freebsd netbsd openbsd
-// Sockets for BSD variants
-
package net
import (
@@ -31,32 +29,3 @@ func maxListenerBacklog() int {
}
return int(n)
}
-
-func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
- a := toAddr(la)
- if a == nil {
- return la, nil
- }
- switch v := a.(type) {
- case *TCPAddr, *UnixAddr:
- err := setDefaultListenerSockopts(s)
- if err != nil {
- return nil, err
- }
- case *UDPAddr:
- if v.IP.IsMulticast() {
- err := setDefaultMulticastSockopts(s)
- if err != nil {
- return nil, err
- }
- switch f {
- case syscall.AF_INET:
- v.IP = IPv4zero
- case syscall.AF_INET6:
- v.IP = IPv6unspecified
- }
- return v.sockaddr(f)
- }
- }
- return la, nil
-}
listenerSockaddr
関数が完全に削除されました。
src/pkg/net/sock_linux.go
--- a/src/pkg/net/sock_linux.go
+++ b/src/pkg/net/sock_linux.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Sockets for Linux
-
package net
import "syscall"
@@ -25,32 +23,3 @@ func maxListenerBacklog() int {
}
return n
}
-
-func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
- a := toAddr(la)
- if a == nil {
- return la, nil
- }
- switch v := a.(type) {
- case *TCPAddr, *UnixAddr:
- err := setDefaultListenerSockopts(s)
- if err != nil {
- return nil, err
- }
- case *UDPAddr:
- if v.IP.IsMulticast() {
- err := setDefaultMulticastSockopts(s)
- if err != nil {
- return nil, err
- }
- switch f {
- case syscall.AF_INET:
- v.IP = IPv4zero
- case syscall.AF_INET6:
- v.IP = IPv6unspecified
- }
- return v.sockaddr(f)
- }
- }\n\treturn la, nil
-}
listenerSockaddr
関数が完全に削除されました。
src/pkg/net/sock_posix.go
--- a/src/pkg/net/sock_posix.go
+++ b/src/pkg/net/sock_posix.go
@@ -4,8 +4,6 @@
// +build darwin freebsd linux netbsd openbsd windows
-// Sockets
-
package net
import (
@@ -15,7 +13,7 @@ import (
var listenerBacklog = maxListenerBacklog()
-// Generic socket creation.
+// Generic POSIX socket creation.
func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
s, err := sysSocket(f, t, p)
if err != nil {
コメントが変更され、ビルドタグが調整されました。
src/pkg/net/sock_unix.go
--- /dev/null
+++ b/src/pkg/net/sock_unix.go
@@ -0,0 +1,36 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd
+
+package net
+
+import "syscall"
+
+func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
+ a := toAddr(la)
+ if a == nil {
+ return la, nil
+ }
+ switch a := a.(type) {
+ case *TCPAddr, *UnixAddr:
+ if err := setDefaultListenerSockopts(s); err != nil {
+ return nil, err
+ }
+ case *UDPAddr:
+ if a.IP.IsMulticast() {
+ if err := setDefaultMulticastSockopts(s); err != nil {
+ return nil, err
+ }
+ switch f {
+ case syscall.AF_INET:
+ a.IP = IPv4zero
+ case syscall.AF_INET6:
+ a.IP = IPv6unspecified
+ }
+ return a.sockaddr(f)
+ }
+ }
+ return la, nil
+}
listenerSockaddr
関数が新規ファイルとして追加されました。このファイルはUnix系OSでのみコンパイルされます。
src/pkg/net/sock_windows.go
--- a/src/pkg/net/sock_windows.go
+++ b/src/pkg/net/sock_windows.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Sockets for Windows
-
package net
import "syscall"
@@ -18,25 +16,23 @@ func listenerSockaddr(s syscall.Handle, f int, la syscall.Sockaddr, toAddr func(\
if a == nil {
return la, nil
}
- switch v := a.(type) {
+ switch a := a.(type) {
case *TCPAddr, *UnixAddr:
- err := setDefaultListenerSockopts(s)
- if err != nil {
+ if err := setDefaultListenerSockopts(s); err != nil {
return nil, err
}
case *UDPAddr:
- if v.IP.IsMulticast() {
- err := setDefaultMulticastSockopts(s)
- if err != nil {
+ if a.IP.IsMulticast() {
+ if err := setDefaultMulticastSockopts(s); err != nil {
return nil, err
}
switch f {
case syscall.AF_INET:
- v.IP = IPv4zero
+ a.IP = IPv4zero
case syscall.AF_INET6:
- v.IP = IPv6unspecified
+ a.IP = IPv6unspecified
}
- return v.sockaddr(f)
+ return a.sockaddr(f)
}
}
return la, nil
listenerSockaddr
関数が修正され、重複部分が削除されました。switch
文の変数名がv
からa
に変更されています。
コアとなるコードの解説
このコミットのコアとなるコードは、新しく作成されたsrc/pkg/net/sock_unix.go
内のlistenerSockaddr
関数です。
// +build darwin freebsd linux netbsd openbsd
package net
import "syscall"
func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
a := toAddr(la) // syscall.Sockaddrをnet.Addrに変換
if a == nil {
return la, nil // 変換できない場合は元のsyscall.Sockaddrを返す
}
switch a := a.(type) { // net.Addrの具体的な型によって処理を分岐
case *TCPAddr, *UnixAddr:
// TCPまたはUnixドメインソケットの場合、デフォルトのリスナーソケットオプションを設定
if err := setDefaultListenerSockopts(s); err != nil {
return nil, err
}
case *UDPAddr:
// UDPソケットの場合
if a.IP.IsMulticast() { // マルチキャストアドレスかどうかをチェック
// マルチキャストの場合、デフォルトのマルチキャストソケットオプションを設定
if err := setDefaultMulticastSockopts(s); err != nil {
return nil, err
}
switch f { // アドレスファミリーによってIPアドレスを調整
case syscall.AF_INET:
a.IP = IPv4zero // IPv4マルチキャストの場合、0.0.0.0に設定
case syscall.AF_INET6:
a.IP = IPv6unspecified // IPv6マルチキャストの場合、::に設定
}
return a.sockaddr(f) // 更新されたアドレス情報でsyscall.Sockaddrを再構築して返す
}
}
return la, nil // 上記のいずれにも該当しない場合は元のsyscall.Sockaddrを返す
}
この関数は、以下のロジックで動作します。
- アドレス変換: 引数として渡された
syscall.Sockaddr
(la
) を、toAddr
関数を使ってGoのnet.Addr
インターフェースの具体的な型(例:*net.TCPAddr
,*net.UDPAddr
,*net.UnixAddr
)に変換します。変換できない場合は、元のsyscall.Sockaddr
をそのまま返します。 - 型による分岐: 変換された
net.Addr
の具体的な型によって処理を分岐します。*TCPAddr
または*UnixAddr
の場合:setDefaultListenerSockopts(s)
を呼び出します。この関数は、ソケットの再利用(SO_REUSEADDR
)や、TCPのNagleアルゴリズム無効化(TCP_NODELAY
)など、一般的なリスナーソケットに推奨されるオプションを設定します。エラーが発生した場合はそれを返します。
*UDPAddr
の場合:- まず、
a.IP.IsMulticast()
をチェックし、UDPアドレスがマルチキャストアドレスであるかどうかを判断します。 - マルチキャストアドレスの場合:
setDefaultMulticastSockopts(s)
を呼び出します。この関数は、マルチキャストグループへの参加(IP_ADD_MEMBERSHIP
)や、マルチキャストパケットのTTL設定(IP_MULTICAST_TTL
)など、マルチキャスト通信に必要なソケットオプションを設定します。- 次に、アドレスファミリー(
f
)に応じて、IPアドレスを調整します。syscall.AF_INET
(IPv4)の場合はIPv4zero
(0.0.0.0)に、syscall.AF_INET6
(IPv6)の場合はIPv6unspecified
(::)に設定します。これにより、ソケットが特定のネットワークインターフェースにバインドされることなく、システム上の任意のインターフェースからのマルチキャストパケットを受け入れられるようになります。 - 最後に、更新された
UDPAddr
情報からa.sockaddr(f)
を呼び出して新しいsyscall.Sockaddr
を構築し、それを返します。
- ユニキャストアドレスの場合: 特にソケットオプションの変更は行わず、そのまま処理を続行します。
- まず、
- デフォルトの戻り値: 上記のいずれのケースにも該当しない場合(例:
net.Addr
がnil
でないが、TCPAddr
,UnixAddr
,UDPAddr
のいずれでもない場合)、または処理が完了した場合は、元のsyscall.Sockaddr
(la
) を返します。
この集約されたlistenerSockaddr
関数は、Goのnet
パッケージが様々なOS上で一貫したネットワークリスニング動作を提供するための重要な役割を担っています。
関連リンク
- Go CL 7299067: https://golang.org/cl/7299067
参考にした情報源リンク
- Go
net
package documentation: https://pkg.go.dev/net - Go
syscall
package documentation: https://pkg.go.dev/syscall - Go build constraints (build tags): https://pkg.go.dev/cmd/go#hdr-Build_constraints
- DRY (Don't Repeat Yourself) principle: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
SO_REUSEADDR
explanation: https://stackoverflow.com/questions/14388706/what-is-the-purpose-of-so-reuseaddr-option-in-socket- Go
net
package source code (for context onsetDefaultListenerSockopts
,setDefaultMulticastSockopts
,IPv4zero
,IPv6unspecified
): https://github.com/golang/go/tree/master/src/net