質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

2回答

1016閲覧

Go言語で受信待ちしながら時々送信したい(TCPクライアント)

kamuycikap

総合スコア135

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

0クリップ

投稿2022/01/13 07:43

作りたいもの:TCPクライアント
実装したい仕組み:常に受信待ちしながら、時々送信したい

今回悩んでいるのは、たまにサーバーから飛んでくる「通知」を受信する場合のコーディングです。

TCPサーバーに対し、データを送信した後に応答受信を行うプログラムは作成できており、動作も確認済み。
サーバーに対して、送信 → 応答の処理を繰り返すような作りであれば参考資料がgoogle検索結果に沢山出てくるのですが、、、、

・必ずしも「送信」→「応答」の流れではない
・サーバーからクライアントに、不定期に「通知」が飛んでくる
※クライアント側でコネクト完了している状態をキープ?
・通知を待ちながらも、条件に応じて「送信」→「応答」を行う必要がある

上記の条件を満たしたい時のコーディング(プログラム構築)は、どのように考えて行えば良いのでしょうか?
勘所、コツなど教えて頂ければ幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

普通にgoroutineで受信スレッドを作っておいて、
そこで受けた内容を振り分け、返信だけをチャネルに載せます。通知は通知向けの処理を行います。
「返信の受け取りの必要な送信」側の処理では送信したあと上記のチャネルから受け取りを行うという流れはどうでしょうか。(実際には受け取れなかった時のタイムアウト処理なども必要になります)

クライアント実装例

go

1package main 2 3import ( 4 "log" 5 "net" 6 "strings" 7 "time" 8) 9 10type Client struct { 11 conn net.Conn 12 repCh chan []byte 13} 14 15func NewClient(conn net.Conn) *Client { 16 c := &Client{ 17 conn: conn, 18 repCh: make(chan []byte), 19 } 20 go func() { 21 buff := make([]byte, 4096) 22 for { 23 n, err := c.conn.Read(buff) 24 if err != nil { 25 log.Print(err) 26 return 27 } 28 rec := string(buff[:n]) 29 switch { 30 case strings.HasPrefix(rec, "evt:"): 31 log.Print(rec) 32 case strings.HasPrefix(rec, "rep:"): 33 c.repCh <- buff[:n] 34 default: 35 log.Print("invalid packet: %q", rec) 36 } 37 } 38 }() 39 return c 40} 41 42func (c *Client) Send(msg string) (string, error) { 43 _, err := c.conn.Write([]byte(msg)) 44 if err != nil { 45 return "", err 46 } 47 return string(<-c.repCh), nil 48} 49 50func main() { 51 c, err := net.Dial("tcp", ":8080") 52 if err != nil { 53 log.Fatal(err) 54 } 55 defer c.Close() 56 client := NewClient(c) 57 for { 58 msg := time.Now().Format("req:" + time.RFC3339Nano + "\n") 59 log.Print(msg) 60 rep, err := client.Send(msg) 61 if err != nil { 62 log.Print(err) 63 return 64 } 65 log.Print(rep) 66 time.Sleep(5 * time.Second) 67 } 68}

投稿2022/01/13 08:16

編集2022/01/13 11:32
nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kamuycikap

2022/01/14 00:22

コード例までありがとうございます。 様々試行錯誤する中で、同じIPアドレスとポート番号で複数のクライアントが作れることに気が付いてアレコレためしていたところでした。 なるほど・・・このような考え方もあるのですね。 もう少し試行錯誤してみます! せっかくのチャンスですので、結果のコードも含めてお返事させて頂きます。
nobonobo

2022/01/14 02:05

確かにサーバー側の通知処理は一つの通知を複数のサブスクライバー(クライアント)に一斉送信するというPubSubみたいな仕掛けが必要かもしれませんね。
nobonobo

2022/01/14 02:19 編集

ただ、PubSub用ポートとReqRep用ポートに分けたら クライアントはReqRep用ポートに書き込んで読む、PubSub用ポートを読み続けるというやり方もあり。 (用途の異なるものを同じ通信路に載せるのは載せる方も分別する方もコストが上がってしまうので) ちなみにHTTPはReqRep型が基本ですが、SSE(ServerSentEvents)というPubSub型の方法と併用することで同じポートに対し違う用途で複数のTCP接続をするという方法もあります。(HTTPヘッダーで用途を指定するということ)
nobonobo

2022/01/14 02:51

脱線気味かもしれませんが、以下のリンクに8080ポートひとつでReqRepとPubSubが混在している実装をnet/httpとJSクライアントでつくってみたものがあります。お試しを。 https://go.dev/play/p/DwGDwRuFA-8
kamuycikap

2022/01/24 08:40

今回はコメント書けるだろうか?? しばらくサイトが不安定で何も出来なく・・・このコメントが残ることを願います。 頂いたコードを動かしながら試行錯誤している状況です。 上記、頂いたコードをWindows10上で動作させたところ。 dial tcp :8080: connectex: No connection could be made because the target machine actively refused it. となりまして。 8080ポートをブロックしている要素はなく、別プログラムで自分が作ったTCP/IPサーバーを8080ポートで動かしてみたところ、そっちは動きました。 ※コード量が若干多めなので、なんとかしてココに貼ろうとしたのですが、サイトの不具合のためかうまく載せられず。 考え方としては ・利用するIPアドレスとPort番号は一つ。 ・接続コネクションは一つだけ。 ・goルーチンで受信部分を回し続ける。  → 通知と送信応答の両方を受信する。 ・送信部分もgoルーチンで送信させる。  → 送信のタイミングはとりあえず適当。(5秒おきとか) こんな試行錯誤続けております。 参考のサイトの情報も参照させていただきながら・・・すこし時間かかってますが、せっかくご教示いただきましたのでなんとか完成させたいです。
guest

0

Goってのはあんまし知らないですが、
マルチスレッドとか使わないのであれば、
受信データが有るかどうかのチェックを行い、無いならその他の処理を、あるなら受信データを取り込み(必要ならデータ処理)というのをループで回しておく、ってところですね

投稿2022/01/13 07:54

y_waiwai

総合スコア87747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kamuycikap

2022/01/14 00:25

なるほど。 ループで回すアイデアですね。 「サーバーにつなぎに行って、受信待ちするんだけれど時々送信したい」をループの中で切り分ける感じでしょうか。 ありがとうございます。 もう少し悩んでみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問