Skip to content

Commit 0def8b5

Browse files
committed
Add syscall/syscallnoerror
1 parent b4d6678 commit 0def8b5

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
app_noerror
2+
app_client
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# https://taskfile.dev
2+
3+
version: '3'
4+
5+
tasks:
6+
default:
7+
cmds:
8+
- rm -f ./app_noerror
9+
- go build -o app_noerror main.go
10+
- go build -o app_client client/main.go
11+
- ./app_noerror &
12+
- sleep 1
13+
- ./app_client
14+
- pkill app_noerror
15+
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: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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+
16+
if err := run(); err != nil {
17+
log.Fatal(err)
18+
}
19+
}
20+
21+
func run() error {
22+
//
23+
// TCPリスナーの起動までを Syscall() で実装
24+
// なお、利用する関数は SyscallNoError (エラーを返さないバージョン) を意図的に利用
25+
//
26+
const (
27+
zero = uintptr(0) // 不要な引数値を表す
28+
)
29+
30+
// socket(2)
31+
var (
32+
domain = uintptr(unix.AF_INET)
33+
sockType = uintptr(unix.SOCK_STREAM)
34+
protocol = uintptr(0)
35+
sFd, _ = unix.SyscallNoError(unix.SYS_SOCKET, domain, sockType, protocol)
36+
)
37+
defer func(fd uintptr) {
38+
_, _ = unix.SyscallNoError(unix.SYS_CLOSE, fd, zero, zero)
39+
}(sFd)
40+
41+
// setsockopt(2) (SO_REUSEADDR)
42+
var (
43+
level = uintptr(unix.SOL_SOCKET)
44+
optname = uintptr(unix.SO_REUSEADDR)
45+
optval = 1
46+
optvalPtr = uintptr(unsafe.Pointer(&optval))
47+
optlen = uintptr(unsafe.Sizeof(optval))
48+
)
49+
_, _, _ = unix.Syscall6(unix.SYS_SETSOCKOPT, sFd, level, optname, optvalPtr, optlen, zero)
50+
51+
// ソケットアドレス生成
52+
// アドレスを表現する構造体として unix.SockaddrInet4 と unix.RawSockaddrInet4 の2つがあるが
53+
// unix.Syscall()を利用して直接システムコールを呼び出す場合は、カーネルが期待するメモリレイアウトを
54+
// そのまま表現する unix.RawSockaddrInet4 を利用する。(要はCの構造体と同じ形の方を使う)
55+
// unix.RawSockaddrInet4の方は、ネットワークバイトオーダーで値を保持する。
56+
var (
57+
sAddr unix.RawSockaddrInet4
58+
sAddrPtr uintptr
59+
sAddrLen uintptr
60+
)
61+
sAddr = unix.RawSockaddrInet4{
62+
Family: unix.AF_INET,
63+
Port: htons(8888),
64+
Addr: [4]byte{127, 0, 0, 1},
65+
}
66+
sAddrPtr = uintptr(unsafe.Pointer(&sAddr))
67+
sAddrLen = uintptr(unix.SizeofSockaddrInet4)
68+
69+
// bind(2)
70+
_, _ = unix.SyscallNoError(unix.SYS_BIND, sFd, sAddrPtr, sAddrLen)
71+
72+
// listen(2)
73+
_, _ = unix.SyscallNoError(unix.SYS_LISTEN, sFd, uintptr(5), zero)
74+
75+
for {
76+
// accept(2)
77+
var (
78+
cAddr unix.RawSockaddrInet4
79+
cAddrPtr = uintptr(unsafe.Pointer(&cAddr))
80+
cAddrLen uint32 = unix.SizeofSockaddrInet4
81+
cAddrLenPtr = uintptr(unsafe.Pointer(&cAddrLen))
82+
cFd, _ = unix.SyscallNoError(unix.SYS_ACCEPT, sFd, cAddrPtr, cAddrLenPtr)
83+
)
84+
log.Printf("[accept] EP: %v:%d", net.IP(cAddr.Addr[:]), ntohs(cAddr.Port))
85+
86+
go func(fd uintptr) {
87+
time.Sleep(1 * time.Second)
88+
_, _ = unix.SyscallNoError(unix.SYS_CLOSE, fd, zero, zero)
89+
}(cFd)
90+
}
91+
}
92+
93+
// ホストバイトオーダーからネットワークバイトオーダーに変換(Host to Network Short)
94+
func htons(port uint16) uint16 {
95+
bytes := make([]byte, 2)
96+
binary.LittleEndian.PutUint16(bytes, port)
97+
return binary.BigEndian.Uint16(bytes)
98+
}
99+
100+
// ネットワークバイトオーダーからホストバイトオーダーに変換(Network to Host Short)
101+
func ntohs(port uint16) uint16 {
102+
bytes := make([]byte, 2)
103+
binary.BigEndian.PutUint16(bytes, port)
104+
return binary.LittleEndian.Uint16(bytes)
105+
}

0 commit comments

Comments
 (0)