LoginSignup
1
2

More than 1 year has passed since last update.

チャットルームとユーザーのモデル化【O'Reilley/Golang/Webアプリケーション】

Last updated at Posted at 2022-01-23

概要

Go言語によるWebアプリケーション開発(O'Reilley)のChapter1.2.4までの内容をモデル図を書いて要約する
設計を書いてからコードを書くor 読むと納得しやすいので参考までに

モデル化

ユーザー

image.png

client.go
type client struct {
	socket *websocket.Conn
	send chan []byte
	room *room
}

チャットルーム

image.png

ソースコード

room.go
type room struct {
	forward chan []byte
	join chan *client
	leave chan *client
	clients map[*client]bool
}

メッセージの送受信

image.png

当初client.read()およびclient.write()がサーバーサイドから見た読み書きであることがわからず困惑した。

ソースコード

client.go
func (c *client) read() {
	for {
		if _, msg, err := c.socket.ReadMessage(); err == nil {
			c.room.forward <- msg
		} else {
			break
		}
	}
	c.socket.Close()
}

func (c *client) write() {
	for msg := range c.send {
		if err := c.socket.WriteMessage(websocket.TextMessage, msg); err != nil {
			break
		}
	}
	c.socket.Close()
}
room.go
func (r *room) run() {
	for {
		select {
		// caseが同時に実行されることはない
		case client := <-r.join:
			// 参加
			r.clients[client] = true
		case client := <-r.leave:
			// 退室
			delete(r.clients, client)
			close(client.send)
		case msg := <-r.forward:
			// すべてのクライアントにメッセージを転送
			for client := range r.clients {
				select {
				// メッセージを送信
				case client.send <- msg:
				default:
					// 送信に失敗
					delete(r.clients, client)
					close(client.send)
				}
			}
		}
	}
}

ユーザーとの接続の確立

ついでに、送受信のモデルで説明しなかった最初にユーザーがチャットルームに入るときのフローをまとめておく

  1. Websocketを使えるようにHTTP接続をアップグレードする
  2. ユーザーがチャットルームに入室するHTTPリクエストを飛ばす
  3. room.ServeHTTPメソッドが呼び出される
  4. upgrader.UpgradeメソッドがWebSocketコネクションを取得
  5. コネクション取得に成功したならば、clientを生成し、room.joinチャネルに渡す
  6. クライアント終了時にルームから退出するように遅延処理
  7. 並行処理でメッセージ受信を待機
  8. メインスレッドでメッセージ送信を待機

ソースコード

room.go
const (
	socketBufferSize = 1024
	messageBuffuerSize = 256
)

// HTTP接続をアップグレードしてWebSocketを使えるようにする
var upgrader = &websocket.Upgrader{ReadBufferSize: socketBufferSize,WriteBufferSize: socketBufferSize}

func (r *room) ServeHTTP(w http.ResponseWriter, req *http.Request)  {
	// WebSocketコネクションを取得
	socket, err := upgrader.Upgrade(w,req,nil)
	if err != nil {
		log.Fatal("ServeHTTP:", err)
		return
	}
	client := &client{
		socket: socket,
		send: make(chan []byte,messageBuffuerSize),
		room: r,
	}
	r.join <- client
	defer func() {r.leave <- client}()
	go client.write()
	client.read()
}

参考

Go言語によるWebアプリケーション開発(O'Reilley)

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2