Skip to content

Commit 9b72db0

Browse files
authored
Merge pull request #928 from devlights/add-syscall-noerror-example
2 parents b4d6678 + db95971 commit 9b72db0

File tree

7 files changed

+333
-0
lines changed

7 files changed

+333
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
app_*
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# これは何?
2+
3+
[unix](https://pkg.go.dev/golang.org/x/sys/unix) パッケージの
4+
5+
- SyscallNoError()
6+
7+
を使っているサンプルです。
8+
9+
ついでに、同じ実装を
10+
11+
- unix.Syscall()
12+
- unixパッケージのラッパー関数 (ex: unix.Listen()など)
13+
- 標準ライブラリの関数(net.Listen()など)
14+
15+
でしてみて、どのように変わるかも試しています。
16+
17+
## 実行結果
18+
19+
```sh
20+
task: [run_noerror] rm -f ./app_noerror
21+
task: [run_noerror] go build -o app_noerror main.go
22+
task: [run_noerror] go build -o app_client client/main.go
23+
task: [run_noerror] ./app_noerror &
24+
task: [run_noerror] sleep 1
25+
task: [run_noerror] ./app_client
26+
[accept] EP: 127.0.0.1:53418
27+
task: [run_noerror] pkill app_noerror
28+
-------------------------------------------------
29+
task: [run_unix] rm -f ./app_unix
30+
task: [run_unix] go build -o app_unix unix/main.go
31+
task: [run_unix] go build -o app_client client/main.go
32+
task: [run_unix] ./app_unix &
33+
task: [run_unix] sleep 1
34+
task: [run_unix] ./app_client
35+
[accept] EP: 127.0.0.1:34092
36+
task: [run_unix] pkill app_unix
37+
-------------------------------------------------
38+
task: [run_stdlib] rm -f ./app_stdlib
39+
task: [run_stdlib] go build -o app_stdlib stdlib/main.go
40+
task: [run_stdlib] go build -o app_client client/main.go
41+
task: [run_stdlib] ./app_stdlib &
42+
task: [run_stdlib] sleep 1
43+
task: [run_stdlib] ./app_client
44+
[accept] EP: 127.0.0.1:34094
45+
task: [run_stdlib] pkill app_stdlib
46+
```
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# https://taskfile.dev
2+
3+
version: '3'
4+
5+
tasks:
6+
default:
7+
cmds:
8+
- task: run_noerror
9+
- task: hr
10+
- task: run_unix
11+
- task: hr
12+
- task: run_stdlib
13+
hr:
14+
cmds:
15+
- echo '-------------------------------------------------'
16+
silent: true
17+
run_noerror:
18+
cmds:
19+
- rm -f ./app_noerror
20+
- go build -o app_noerror main.go
21+
- go build -o app_client client/main.go
22+
- ./app_noerror &
23+
- sleep 1
24+
- ./app_client
25+
- pkill app_noerror
26+
ignore_error: true
27+
run_unix:
28+
cmds:
29+
- rm -f ./app_unix
30+
- go build -o app_unix unix/main.go
31+
- go build -o app_client client/main.go
32+
- ./app_unix &
33+
- sleep 1
34+
- ./app_client
35+
- pkill app_unix
36+
ignore_error: true
37+
run_stdlib:
38+
cmds:
39+
- rm -f ./app_stdlib
40+
- go build -o app_stdlib stdlib/main.go
41+
- go build -o app_client client/main.go
42+
- ./app_stdlib &
43+
- sleep 1
44+
- ./app_client
45+
- pkill app_stdlib
46+
ignore_error: true
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"io"
6+
"log"
7+
"net"
8+
)
9+
10+
func main() {
11+
conn, err := net.Dial("tcp", "127.0.0.1:8888")
12+
if err != nil {
13+
log.Fatal(err)
14+
}
15+
defer conn.Close()
16+
17+
buf := make([]byte, 1)
18+
for {
19+
clear(buf)
20+
_, err := conn.Read(buf)
21+
if errors.Is(err, io.EOF) {
22+
return
23+
}
24+
}
25+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package main
2+
3+
import (
4+
"encoding/binary"
5+
"log"
6+
"net"
7+
"time"
8+
"unsafe"
9+
10+
"golang.org/x/sys/unix"
11+
)
12+
13+
func main() {
14+
log.SetFlags(0)
15+
if err := run(); err != nil {
16+
log.Fatal(err)
17+
}
18+
}
19+
20+
func run() error {
21+
//
22+
// TCPリスナーの起動までを Syscall() で実装
23+
// なお、利用する関数は SyscallNoError (エラーを返さないバージョン) を意図的に利用
24+
//
25+
const (
26+
zero = uintptr(0) // 不要な引数値を表す
27+
)
28+
29+
// socket(2)
30+
var (
31+
domain = uintptr(unix.AF_INET)
32+
sockType = uintptr(unix.SOCK_STREAM)
33+
protocol = uintptr(0)
34+
sFd, _ = unix.SyscallNoError(unix.SYS_SOCKET, domain, sockType, protocol)
35+
)
36+
defer func(fd uintptr) {
37+
_, _ = unix.SyscallNoError(unix.SYS_CLOSE, fd, zero, zero)
38+
}(sFd)
39+
40+
// setsockopt(2) (SO_REUSEADDR)
41+
var (
42+
level = uintptr(unix.SOL_SOCKET)
43+
optname = uintptr(unix.SO_REUSEADDR)
44+
optval = 1
45+
optvalPtr = uintptr(unsafe.Pointer(&optval))
46+
optlen = uintptr(unsafe.Sizeof(optval))
47+
)
48+
_, _, _ = unix.Syscall6(unix.SYS_SETSOCKOPT, sFd, level, optname, optvalPtr, optlen, zero)
49+
50+
// ソケットアドレス生成
51+
// アドレスを表現する構造体として unix.SockaddrInet4 と unix.RawSockaddrInet4 の2つがあるが
52+
// unix.Syscall()を利用して直接システムコールを呼び出す場合は、カーネルが期待するメモリレイアウトを
53+
// そのまま表現する unix.RawSockaddrInet4 を利用する。(要はCの構造体と同じ形の方を使う)
54+
// unix.RawSockaddrInet4の方は、ネットワークバイトオーダーで値を保持する。
55+
var (
56+
sAddr unix.RawSockaddrInet4
57+
sAddrPtr uintptr
58+
sAddrLen uintptr
59+
)
60+
sAddr = unix.RawSockaddrInet4{
61+
Family: unix.AF_INET,
62+
Port: htons(8888),
63+
Addr: [4]byte{127, 0, 0, 1},
64+
}
65+
sAddrPtr = uintptr(unsafe.Pointer(&sAddr))
66+
sAddrLen = uintptr(unix.SizeofSockaddrInet4)
67+
68+
// bind(2)
69+
_, _ = unix.SyscallNoError(unix.SYS_BIND, sFd, sAddrPtr, sAddrLen)
70+
71+
// listen(2)
72+
_, _ = unix.SyscallNoError(unix.SYS_LISTEN, sFd, uintptr(5), zero)
73+
74+
for {
75+
// accept(2)
76+
var (
77+
cAddr unix.RawSockaddrInet4
78+
cAddrPtr = uintptr(unsafe.Pointer(&cAddr))
79+
cAddrLen uint32 = unix.SizeofSockaddrInet4
80+
cAddrLenPtr = uintptr(unsafe.Pointer(&cAddrLen))
81+
cFd, _ = unix.SyscallNoError(unix.SYS_ACCEPT, sFd, cAddrPtr, cAddrLenPtr)
82+
)
83+
log.Printf("[accept] EP: %v:%d", net.IP(cAddr.Addr[:]), ntohs(cAddr.Port))
84+
85+
go func(fd uintptr) {
86+
time.Sleep(1 * time.Second)
87+
_, _ = unix.SyscallNoError(unix.SYS_CLOSE, fd, zero, zero)
88+
}(cFd)
89+
}
90+
}
91+
92+
// ホストバイトオーダーからネットワークバイトオーダーに変換(Host to Network Short)
93+
func htons(port uint16) uint16 {
94+
bytes := make([]byte, 2)
95+
binary.LittleEndian.PutUint16(bytes, port)
96+
return binary.BigEndian.Uint16(bytes)
97+
}
98+
99+
// ネットワークバイトオーダーからホストバイトオーダーに変換(Network to Host Short)
100+
func ntohs(port uint16) uint16 {
101+
bytes := make([]byte, 2)
102+
binary.BigEndian.PutUint16(bytes, port)
103+
return binary.LittleEndian.Uint16(bytes)
104+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net"
6+
"time"
7+
)
8+
9+
func main() {
10+
log.SetFlags(0)
11+
if err := run(); err != nil {
12+
log.Fatal(err)
13+
}
14+
}
15+
16+
func run() error {
17+
//
18+
// unix.SyscallNoError()を用いたサンプルと同じものを
19+
// 標準ライブライブラリで提供されている関数を使って実装
20+
//
21+
// 元のサンプルと同様にエラー処理は割愛する
22+
//
23+
var (
24+
ln net.Listener
25+
)
26+
ln, _ = net.Listen("tcp", ":8888")
27+
defer ln.Close()
28+
29+
for {
30+
var (
31+
conn net.Conn
32+
addr *net.TCPAddr
33+
)
34+
conn, _ = ln.Accept()
35+
addr = conn.RemoteAddr().(*net.TCPAddr)
36+
37+
log.Printf("[accept] EP: %v:%d", addr.IP, addr.Port)
38+
39+
go func(conn net.Conn) {
40+
time.Sleep(1 * time.Second)
41+
conn.Close()
42+
}(conn)
43+
}
44+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net"
6+
"time"
7+
8+
"golang.org/x/sys/unix"
9+
)
10+
11+
func main() {
12+
log.SetFlags(0)
13+
if err := run(); err != nil {
14+
log.Fatal(err)
15+
}
16+
}
17+
18+
func run() error {
19+
//
20+
// unix.SyscallNoError()を用いたサンプルと同じものを
21+
// unixパッケージで提供されている各ラッパー関数を使って実装
22+
//
23+
// 元のサンプルと同様にエラー処理は割愛する
24+
//
25+
26+
// socket(2)
27+
var (
28+
fd int
29+
)
30+
fd, _ = unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
31+
defer unix.Close(fd)
32+
33+
// setsockopt(2) (SO_REUSEADDR)
34+
_ = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
35+
36+
// ソケットアドレス生成
37+
var (
38+
sAddr = unix.SockaddrInet4{
39+
Port: 8888,
40+
Addr: [4]byte{127, 0, 0, 1},
41+
}
42+
)
43+
44+
// bind(2)
45+
_ = unix.Bind(fd, &sAddr)
46+
47+
// listen(2)
48+
_ = unix.Listen(fd, 5)
49+
50+
for {
51+
// accept(2)
52+
var (
53+
cfd int
54+
sa unix.Sockaddr
55+
ca *unix.SockaddrInet4
56+
)
57+
cfd, sa, _ = unix.Accept(fd)
58+
ca = sa.(*unix.SockaddrInet4)
59+
60+
log.Printf("[accept] EP: %v:%d", net.IP(ca.Addr[:]), ca.Port)
61+
62+
go func(fd int) {
63+
time.Sleep(1 * time.Second)
64+
_ = unix.Close(fd)
65+
}(cfd)
66+
}
67+
}

0 commit comments

Comments
 (0)