Skip to content

Commit a41d64d

Browse files
authored
Merge pull request #892 from devlights/add-tcp-socket-fin-rst-example
2 parents 546a069 + 2e22156 commit a41d64d

File tree

4 files changed

+235
-0
lines changed

4 files changed

+235
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
app
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# TCP通信でのRST送信のサンプル
2+
3+
Goでは ```*net.TCPConn.SetLinger()``` にて ```SO_LINGER``` が設定出来る。
4+
値を ```0``` で設定すると切断時にRST送信が行われる。(これはC言語でも同じ)
5+
6+
```go
7+
tcpConn, ok := conn.(*net.TCPConn)
8+
if ok {
9+
tcpConn.SetLinger(0)
10+
}
11+
12+
conn.Close() // RST送信
13+
```
14+
15+
```sh
16+
$ task
17+
task: [build] go build -o app main.go
18+
task: [run-fin] sudo tcpdump -i lo -n 'tcp port 8888' -S &
19+
task: [run-fin] sleep 1
20+
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
21+
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
22+
task: [run-fin] ./app -server &
23+
task: [run-fin] ./app
24+
task: [run-fin] sleep 2
25+
09:22:02.504826 IP 127.0.0.1.32782 > 127.0.0.1.8888: Flags [S], seq 1561972627, win 43690, options [mss 65495,sackOK,TS val 3209706398 ecr 0,nop,wscale 7], length 0
26+
09:22:02.504835 IP 127.0.0.1.8888 > 127.0.0.1.32782: Flags [S.], seq 2366941560, ack 1561972628, win 43690, options [mss 65495,sackOK,TS val 3209706398 ecr 3209706398,nop,wscale 7], length 0
27+
09:22:02.504844 IP 127.0.0.1.32782 > 127.0.0.1.8888: Flags [.], ack 2366941561, win 342, options [nop,nop,TS val 3209706398 ecr 3209706398], length 0
28+
09:22:02.504914 IP 127.0.0.1.8888 > 127.0.0.1.32782: Flags [P.], seq 2366941561:2366941566, ack 1561972628, win 342, options [nop,nop,TS val 3209706398 ecr 3209706398], length 5
29+
09:22:02.504924 IP 127.0.0.1.32782 > 127.0.0.1.8888: Flags [.], ack 2366941566, win 342, options [nop,nop,TS val 3209706398 ecr 3209706398], length 0
30+
09:22:02.504944 IP 127.0.0.1.32782 > 127.0.0.1.8888: Flags [F.], seq 1561972628, ack 2366941566, win 342, options [nop,nop,TS val 3209706398 ecr 3209706398], length 0
31+
09:22:02.504982 IP 127.0.0.1.8888 > 127.0.0.1.32782: Flags [F.], seq 2366941566, ack 1561972629, win 342, options [nop,nop,TS val 3209706398 ecr 3209706398], length 0
32+
09:22:02.504995 IP 127.0.0.1.32782 > 127.0.0.1.8888: Flags [.], ack 2366941567, win 342, options [nop,nop,TS val 3209706398 ecr 3209706398], length 0
33+
task: [run-fin] sudo pkill tcpdump
34+
35+
8 packets captured
36+
16 packets received by filter
37+
0 packets dropped by kernel
38+
task: [run] sleep 2
39+
task: [run-rst] sudo tcpdump -i lo -n 'tcp port 8888' -S &
40+
task: [run-rst] sleep 1
41+
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
42+
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
43+
task: [run-rst] ./app -server -rst &
44+
task: [run-rst] ./app -rst
45+
task: [run-rst] sleep 2
46+
09:22:07.566462 IP 127.0.0.1.50180 > 127.0.0.1.8888: Flags [S], seq 3347564028, win 43690, options [mss 65495,sackOK,TS val 3209711459 ecr 0,nop,wscale 7], length 0
47+
09:22:07.566476 IP 127.0.0.1.8888 > 127.0.0.1.50180: Flags [S.], seq 671550310, ack 3347564029, win 43690, options [mss 65495,sackOK,TS val 3209711459 ecr 3209711459,nop,wscale 7], length 0
48+
09:22:07.566485 IP 127.0.0.1.50180 > 127.0.0.1.8888: Flags [.], ack 671550311, win 342, options [nop,nop,TS val 3209711459 ecr 3209711459], length 0
49+
09:22:07.566556 IP 127.0.0.1.8888 > 127.0.0.1.50180: Flags [P.], seq 671550311:671550316, ack 3347564029, win 342, options [nop,nop,TS val 3209711459 ecr 3209711459], length 5
50+
09:22:07.566566 IP 127.0.0.1.50180 > 127.0.0.1.8888: Flags [.], ack 671550316, win 342, options [nop,nop,TS val 3209711459 ecr 3209711459], length 0
51+
09:22:07.566589 IP 127.0.0.1.50180 > 127.0.0.1.8888: Flags [R.], seq 3347564029, ack 671550316, win 342, options [nop,nop,TS val 3209711459 ecr 3209711459], length 0
52+
task: [run-rst] sudo pkill tcpdump
53+
54+
6 packets captured
55+
12 packets received by filter
56+
0 packets dropped by kernel
57+
```
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# https://taskfile.dev
2+
3+
version: '3'
4+
5+
tasks:
6+
default:
7+
cmds:
8+
- task: build
9+
- task: run
10+
build:
11+
cmds:
12+
- go build -o app main.go
13+
run:
14+
cmds:
15+
- task: run-fin
16+
- sleep 2
17+
- task: run-rst
18+
run-fin:
19+
cmds:
20+
- sudo tcpdump -i lo -n 'tcp port 8888' -S &
21+
- sleep 1
22+
- ./app -server &
23+
- ./app
24+
- sleep 2
25+
- sudo pkill tcpdump
26+
run-rst:
27+
cmds:
28+
- sudo tcpdump -i lo -n 'tcp port 8888' -S &
29+
- sleep 1
30+
- ./app -server -rst &
31+
- ./app -rst
32+
- sleep 2
33+
- sudo pkill tcpdump
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"net"
6+
)
7+
8+
type (
9+
Args struct {
10+
IsServer bool
11+
UseRst bool
12+
}
13+
)
14+
15+
var (
16+
args Args
17+
)
18+
19+
func init() {
20+
flag.BoolVar(&args.IsServer, "server", false, "サーバモードで起動")
21+
flag.BoolVar(&args.UseRst, "rst", false, "RST送信による強制切断を使用")
22+
}
23+
24+
func main() {
25+
flag.Parse()
26+
27+
if err := run(); err != nil {
28+
panic(err)
29+
}
30+
}
31+
32+
func run() error {
33+
var (
34+
err error
35+
)
36+
if args.IsServer {
37+
err = runServer()
38+
} else {
39+
err = runClient()
40+
}
41+
42+
if err != nil {
43+
return err
44+
}
45+
46+
return nil
47+
}
48+
49+
func runServer() error {
50+
var (
51+
l net.Listener
52+
err error
53+
)
54+
l, err = net.Listen("tcp", ":8888")
55+
if err != nil {
56+
return err
57+
}
58+
defer l.Close()
59+
60+
// サンプルなので1回のみ接続を受付
61+
var (
62+
conn net.Conn
63+
)
64+
conn, err = l.Accept()
65+
if err != nil {
66+
return err
67+
}
68+
defer conn.Close()
69+
70+
// 適当にデータを送信しクライアントが切ってきたら終わり
71+
_, err = conn.Write([]byte("hello"))
72+
if err != nil {
73+
return err
74+
}
75+
76+
var (
77+
buf = make([]byte, 10)
78+
n int
79+
)
80+
for {
81+
clear(buf)
82+
83+
n, err = conn.Read(buf)
84+
if n == 0 || err != nil {
85+
break
86+
}
87+
}
88+
89+
if args.UseRst {
90+
// RST送信するために SO_LINGER を設定
91+
// Goの場合 *net.TCPConn に SetLinger メソッドが用意されている。
92+
var (
93+
tcpConn *net.TCPConn
94+
ok bool
95+
lingerSec = 0
96+
)
97+
tcpConn, ok = conn.(*net.TCPConn)
98+
if ok {
99+
// $ go doc net.tcpconn.setlinger
100+
//
101+
// > SetLinger sets the behavior of Close on a connection which still has data waiting to be sent or to be acknowledged.
102+
// > If sec < 0 (the default), the operating system finishes sending the data in the background.
103+
// > If sec == 0, the operating system discards any unsent or unacknowledged data.
104+
// > If sec > 0, the data is sent in the background as with sec < 0. On some operating systems including Linux,
105+
// > this may cause Close to block until all data has been sent or discarded.
106+
// > On some operating systems after sec seconds have elapsed any remaining unsent data may be discarded.
107+
tcpConn.SetLinger(lingerSec)
108+
}
109+
}
110+
111+
return nil
112+
}
113+
114+
func runClient() error {
115+
var (
116+
conn net.Conn
117+
err error
118+
)
119+
conn, err = net.Dial("tcp", "localhost:8888")
120+
if err != nil {
121+
return err
122+
}
123+
defer func() {
124+
conn.Close()
125+
}()
126+
127+
// データを受信したら切断
128+
var (
129+
buf = make([]byte, 10)
130+
)
131+
_, err = conn.Read(buf)
132+
if err != nil {
133+
return err
134+
}
135+
136+
if args.UseRst {
137+
tcpConn, ok := conn.(*net.TCPConn)
138+
if ok {
139+
tcpConn.SetLinger(0)
140+
}
141+
}
142+
143+
return nil
144+
}

0 commit comments

Comments
 (0)