概要
以下、自分用のメモです。
たまにちょこっと通信するプログラム書くときに、GoでTCP通信どうやるんやっけ??ってよくなるので、忘れないようにここにメモメモ。。。
自分用のボイラープレートコードみたいなものです。
サンプル
大したことしてなくて、サーバとクライアントがいて、所定のプロトコルに沿ったメッセージをクライアントが送り、サーバがエコー(大文字化)して返すというものです。
server.go
// Go でのソケットプログラミング 基本 (2) // // 本パッケージはサーバ側の処理です。 // // # REFERENCES // - https://pkg.go.dev/net@go1.19.3 // - https://www.developer.com/languages/intro-socket-programming-go/ // - https://stackoverflow.com/questions/13417095/how-do-i-stop-a-listening-server-in-go // - https://stackoverflow.com/a/237495 package main import ( "bytes" "encoding/binary" "fmt" "io" "log" "net" "os" "os/signal" "strings" ) const ( PORT = 8888 ) var ( appLog = log.New(os.Stderr, "[server] ", log.Ltime|log.Lmicroseconds) ) func exitOnErr(err error) { if err != nil { if err != io.EOF { panic(err) } } } func main() { // Start var ( listener net.Listener err error ) listener, err = net.Listen("tcp", fmt.Sprintf(":%d", PORT)) exitOnErr(err) appLog.Println("server start...") // Regist SIGINT(Ctrl-C) handler var ( quitCh = make(chan os.Signal, 1) ) signal.Notify(quitCh, os.Interrupt) go func() { <-quitCh listener.Close() }() // Accept var ( count int ) for { var ( conn net.Conn ) conn, err = listener.Accept() if err != nil { appLog.Println("shutdown...") break } appLog.Printf("accept from %v", conn.RemoteAddr()) go proc(conn) count++ } log.Printf("COUNT=%d\n", count) } func proc(stream io.ReadWriteCloser) { defer stream.Close() // Recv // // Protocol: // (1) length: uint32 (4-bytes) // (2) data : string (variable) var ( totalBuf = new(bytes.Buffer) err error ) // (1) length var ( buf = make([]byte, 4) length uint32 ) _, err = stream.Read(buf) exitOnErr(err) length = binary.BigEndian.Uint32(buf[:]) _, err = totalBuf.Write(buf) exitOnErr(err) // (2) data var ( message string ) buf = make([]byte, length) _, err = stream.Read(buf) exitOnErr(err) message = string(buf) _, err = totalBuf.Write(buf) exitOnErr(err) // Send var ( resp = strings.ToUpper(message) respBuf = []byte(resp) ) _, err = stream.Write(respBuf) exitOnErr(err) appLog.Printf("\t[bytes] %v\n", totalBuf.Bytes()) }
client.go
// Go でのソケットプログラミング 基本 (2) // // 本パッケージはクライアント側の処理です。 // // # REFERENCES // - https://pkg.go.dev/net@go1.19.3 // - https://www.developer.com/languages/intro-socket-programming-go/ // - https://stackoverflow.com/questions/13417095/how-do-i-stop-a-listening-server-in-go // - https://stackoverflow.com/a/237495 package main import ( "bytes" "encoding/binary" "fmt" "io" "log" "math/rand" "net" "os" "time" ) const ( PORT = 8888 ) var ( items = []string{ "golang", "java", "python3", "C", "C++", "C#", "rust", "javascript", } appLog = log.New(os.Stderr, "[client] ", log.Ltime|log.Lmicroseconds) ) func init() { rand.Seed(time.Now().UnixNano()) } func exitOnErr(err error) { if err != nil { if err != io.EOF { panic(err) } } } func main() { // Connect var ( conn net.Conn addr *net.TCPAddr err error ) addr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", PORT)) exitOnErr(err) appLog.Printf("connect to %v", addr) conn, err = net.Dial("tcp", addr.String()) exitOnErr(err) defer conn.Close() // Send // // Protocol: // (1) length: uint32 (4-bytes) // (2) data : string (variable) var ( message = items[rand.Intn(len(items))] length = make([]byte, 4) buf = new(bytes.Buffer) ) binary.BigEndian.PutUint32(length, uint32(len(message))) _, err = buf.Write(length) exitOnErr(err) _, err = buf.Write([]byte(message)) exitOnErr(err) appLog.Printf("send %v --> %v", conn.LocalAddr(), conn.RemoteAddr()) _, err = conn.Write(buf.Bytes()) exitOnErr(err) // Recv var ( resp []byte ) resp, err = io.ReadAll(conn) exitOnErr(err) appLog.Printf("recv %v --> %v", conn.RemoteAddr(), conn.LocalAddr()) appLog.Printf("\t%v --> %v", message, string(resp)) }
Taskfile.yml
version: '3' tasks: default: cmds: - task: server - task: client server: dir: server cmds: - cmd: go build -o server - cmd: ./server & ignore_error: true client: dir: client cmds: - cmd: go build -o client - cmd: timeout 1 bash -c 'while true; do echo '--------------------'; ./client; done;'; true ignore_error: true - cmd: pkill -SIGINT server
実行結果
実行すると以下のようになります。
gitpod /workspace/try-golang (master) $ task -d examples/socket/tcp_02_twoway_recvside_close/ task: [server] go build -o server task: [server] ./server & task: [client] go build -o client [server] 04:46:47.161861 server start... task: [client] timeout 1 bash -c 'while true; do echo '--------------------'; ./client; done;'; true -------------------- [client] 04:46:47.449368 connect to :8888 [client] 04:46:47.449519 send 127.0.0.1:57098 --> 127.0.0.1:8888 [server] 04:46:47.449609 accept from 127.0.0.1:57098 [server] 04:46:47.449700 [bytes] [0 0 0 10 106 97 118 97 115 99 114 105 112 116] [client] 04:46:47.449754 recv 127.0.0.1:8888 --> 127.0.0.1:57098 [client] 04:46:47.449770 javascript --> JAVASCRIPT -------------------- [client] 04:46:47.451532 connect to :8888 [client] 04:46:47.451678 send 127.0.0.1:57100 --> 127.0.0.1:8888 [server] 04:46:47.451711 accept from 127.0.0.1:57100 [server] 04:46:47.451795 [bytes] [0 0 0 1 67] [client] 04:46:47.451833 recv 127.0.0.1:8888 --> 127.0.0.1:57100 [client] 04:46:47.451838 C --> C -------------------- [client] 04:46:47.453747 connect to :8888 [client] 04:46:47.453868 send 127.0.0.1:57104 --> 127.0.0.1:8888 [server] 04:46:47.453885 accept from 127.0.0.1:57104 [server] 04:46:47.453945 [bytes] [0 0 0 7 112 121 116 104 111 110 51] [client] 04:46:47.453974 recv 127.0.0.1:8888 --> 127.0.0.1:57104 [client] 04:46:47.453986 python3 --> PYTHON3 ・ ・ 割愛 ・ ・ -------------------- [client] 04:46:48.442361 connect to :8888 [client] 04:46:48.442576 send 127.0.0.1:42858 --> 127.0.0.1:8888 [server] 04:46:48.442584 accept from 127.0.0.1:42858 [server] 04:46:48.442663 [bytes] [0 0 0 4 114 117 115 116] [client] 04:46:48.442715 recv 127.0.0.1:8888 --> 127.0.0.1:42858 [client] 04:46:48.442733 rust --> RUST -------------------- [client] 04:46:48.444926 connect to :8888 [client] 04:46:48.445122 send 127.0.0.1:42870 --> 127.0.0.1:8888 [server] 04:46:48.445150 accept from 127.0.0.1:42870 [server] 04:46:48.445222 [bytes] [0 0 0 3 67 43 43] [client] 04:46:48.445277 recv 127.0.0.1:8888 --> 127.0.0.1:42870 [client] 04:46:48.445294 C++ --> C++ -------------------- task: [client] pkill -SIGINT server [server] 04:46:48.450327 shutdown... 2022/12/05 04:46:48 COUNT=453
上記のサンプルは以下にアップしています。
try-golang/examples/socket/tcp_02_twoway_recvside_close at master · devlights/try-golang · GitHub
参考情報
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。