- オリジナル住所:パート4 -複数のクライアントの処理
- アドレス変換:github.com/watermelo/d ...
- 原作:エリオット・フォーブス
- 翻訳:カーキカーキ
- 限られたレベルの翻訳者は、翻訳や誤謬を理解している場合、助けてください指摘しました
このセクションでは、完全なコードを提供します。GitHubのを
この記事ではReactJSゴーの使用上の記事のシリーズのパート4で、チャットアプリケーションを構築します。あなたはここにパート3を見つけることができます- フロントエンドの実装
この節の主な機能は、クライアント・メッセージ処理の多数、および接続ごとにクライアントに受信されたブロードキャストメッセージを達成します。このシリーズのこの部分の最後に、我々は以下となります。
- プールメカニズムを実装し、効果的なWebSocketサービスの接続数を追跡することができます。
- 受信したすべてのメッセージは、接続プール内のすべての接続にブロードキャストすることができます。
- 別のクライアントが接続または切断すると、クライアントは、既存の通知を受けることができます。
もちろん、この部分の終わりには、私たちのアプリケーションは、次のようになります。
スプリットのWebSocketコード
今必要な基本的な作業を完了し、我々は、コードベースを改善し続けることができます。一部のアプリケーションでは、開発のためのサブパケットに分割することができます。
今、理想的に、あなたのmain.go
ファイルは、唯一のエントリ囲碁アプリケーション、それはかなり小さいはずであるべきであり、プロジェクト内の他のパッケージを呼び出すことができます。
注- -私たちは行く非公式標準のプロジェクト構造のレイアウトを参照しますgolang-標準/プロジェクトレイアウト
のは、バックエンドディレクトリと呼ばれるプロジェクトを作成してみましょうpkg/
新しいディレクトリのを。一方で、我々は別の名前を作成しようとしているwebsocket/
ディレクトリを、ディレクトリが含まれていますwebsocket.go
ファイルを。
私たちは、に提示しますmain.go
使用して新しいファイルに多くのモバイルベースのWebSocketコードwebsocket.go
ファイルを。
注 - もう一つ注意すべきは、コピー機能、各機能の最初の文字が首都に必要がある場合には、我々はエクスポートすることができ、プロジェクトの残りのため、これらの機能を期待していることです。
package websocket
import (
"fmt"
"io"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
func Upgrade(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return ws, err
}
return ws, nil
}
func Reader(conn *websocket.Conn) {
for {
messageType, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
fmt.Println(string(p))
if err := conn.WriteMessage(messageType, p); err != nil {
log.Println(err)
return
}
}
}
func Writer(conn *websocket.Conn) {
for {
fmt.Println("Sending")
messageType, r, err := conn.NextReader()
if err != nil {
fmt.Println(err)
return
}
w, err := conn.NextWriter(messageType)
if err != nil {
fmt.Println(err)
return
}
if _, err := io.Copy(w, r); err != nil {
fmt.Println(err)
return
}
if err := w.Close(); err != nil {
fmt.Println(err)
return
}
}
}
复制代码
私たちは今、新しい作成したwebsocket
パッケージを、その後、我々は更新するmain.go
このパッケージを起動するためにファイルを。まず、あなたは、ファイルの先頭にインポートリストに新規インポートを追加する必要があり、その後、使用することができwebsocket.
、パッケージの関数を呼び出します。このように:
package main
import (
"fmt"
"net/http"
"realtime-chat-go-react/backend/pkg/websocket"
)
func serveWs(pool *websocket.Pool, w http.ResponseWriter, r *http.Request) {
fmt.Println("WebSocket Endpoint Hit")
conn, err := websocket.Upgrade(w, r)
if err != nil {
fmt.Fprintf(w, "%+v\n", err)
}
client := &websocket.Client{
Conn: conn,
Pool: pool,
}
pool.Register <- client
client.Read()
}
func setupRoutes() {
pool := websocket.NewPool()
go pool.Start()
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
serveWs(pool, w, r)
})
}
func main() {
fmt.Println("Distributed Chat App v0.01")
setupRoutes()
http.ListenAndServe(":8080", nil)
}
复制代码
これらの変更の後、我々はこれらの既存の機能を弱体化させるかどうかを確認する必要があります。それでもメッセージを送受信できることを確認し、バックエンドとフロントエンドを実行するために、もう一度お試しください:
$ cd backend/
$ go run main.go
复制代码
成功した場合、我々は、マルチクライアントライブラリを処理するコードを拡大し続けることができます。
次のようにこれまでのところ、ディレクトリ構造は次のようになります。
- backend/
- - pkg/
- - - websocket/
- - - - websocket.go
- - main.go
- - go.mod
- - go.sum
- frontend/
- ...
复制代码
複数のクライアントを扱います
今の基本的な操作を完了した、我々は改善し、複数のクライアントを処理するためのバックエンド機能を実装し続けることができます。
この目的のために、我々はWebSocketのサービスとの接続を処理する方法を検討する必要があります。たびに新しい接続は、我々は既存の接続プールに追加し、メッセージが送信されるたびに、プール内の全員がメッセージを受信することを確認する必要があります。
チャネルを使用します
私たちは、同時接続数の多いシステムを開発する必要があります。接続時間は、新たな始まりますgoroutine
各接続に対処するために。これは、我々はこれらの同時気にしなければならないことを意味goroutine
間の通信、およびスレッドセーフ。
さらに実装するとPool
、構造、我々は使用を考慮する必要がありsync.Mutex
、他のブロックするようにgoroutine
データを/同時アクセスを変更、または私達は使用することができますchannels
。
このプロジェクトでは、私はそれを使用するのが最善だと思うchannels
複数の同時にし、安全な方法goroutine
でコミュニケーション。
注-あなたが行くの詳細を知りたい場合は
channels
、あなたがここに私の他の記事を閲覧することができます:チュートリアルチャンネルを行きます
client.go
のは、名前のファイルを作成してみましょうclient.go
、それが中に存在する、新しいファイルをpkg/websocket
ディレクトリ、以下が含まれるファイルで定義されるであろうClient
構造を:
- ID:一意に特定の接続文字列を識別
- コネティカット:指し示す
websocket.Conn
ポインタを - プール:指し示す
Pool
ポインタを??
また、定義する必要がありRead()
、常にこれに耳を傾けます方法は、Client
発行され、接続用WebSocket上の新しいメッセージです。
新しいメッセージを受信した場合、それは細胞にメッセージを渡しますBroadcast
チャネル、チャネル各クライアントプールに受信された放送メッセージ。
package websocket
import (
"fmt"
"log"
"github.com/gorilla/websocket"
)
type Client struct {
ID string
Conn *websocket.Conn
Pool *Pool
}
type Message struct {
Type int `json:"type"`
Body string `json:"body"`
}
func (c *Client) Read() {
defer func() {
c.Pool.Unregister <- c
c.Conn.Close()
}()
for {
messageType, p, err := c.Conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
message := Message{Type: messageType, Body: string(p)}
c.Pool.Broadcast <- message
fmt.Printf("Message Received: %+v\n", message)
}
}
复制代码
グレート、私たちは、クライアントコードを定義したプールを達成するために続けています。
プールの構造
私たちは、pkg/websocket
ディレクトリに新しいファイルを作成しますpool.go
。
まず定義Pool
通信するために必要な当社同時のすべてが含まれています構造channels
だけでなく、クライアントをmap
。
package websocket
import "fmt"
type Pool struct {
Register chan *Client
Unregister chan *Client
Clients map[*Client]bool
Broadcast chan Message
}
func NewPool() *Pool {
return &Pool{
Register: make(chan *Client),
Unregister: make(chan *Client),
Clients: make(map[*Client]bool),
Broadcast: make(chan Message),
}
}
复制代码
私たちは、同時書き込みをアプリケーションが1点のみのWebSocket接続に書き込むことができていることを確認し、または問題に直面する必要があります。したがって、定義Start()
渡されていた方法は、監視するPool
チャネルのコンテンツを、それが一つのチャンネルの内容に送信され受信した場合、次に、それが適切な行動を取るだろう。
- 登録 -新しいクライアントが接続し、
Register channel
このプール内のすべてのクライアントを送信しますNew User Joined...
- 登録解除 -クライアントが切断したときのプールを通知、ユーザーをログオフ
- クライアント -クライアントブール値のマッピング。あなたは、クライアントのアクティブ/非アクティブを決定するブール値を使用することができます
- ブロードキャスト -それはメッセージがプールにすべてのクライアントを横断する通過して、ソケットを介してメッセージを送信チャンネル、。
コード:
func (pool *Pool) Start() {
for {
select {
case client := <-pool.Register:
pool.Clients[client] = true
fmt.Println("Size of Connection Pool: ", len(pool.Clients))
for client, _ := range pool.Clients {
fmt.Println(client)
client.Conn.WriteJSON(Message{Type: 1, Body: "New User Joined..."})
}
break
case client := <-pool.Unregister:
delete(pool.Clients, client)
fmt.Println("Size of Connection Pool: ", len(pool.Clients))
for client, _ := range pool.Clients {
client.Conn.WriteJSON(Message{Type: 1, Body: "User Disconnected..."})
}
break
case message := <-pool.Broadcast:
fmt.Println("Sending message to all clients in Pool")
for client, _ := range pool.Clients {
if err := client.Conn.WriteJSON(message); err != nil {
fmt.Println(err)
return
}
}
}
}
}
复制代码
websocket.go
偉大な、我々は再びwebsocket.go
いくつかのマイナーな修正ファイル、および機能の一部を削除し、方法はもはや必要ありません。
package websocket
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
func Upgrade(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return nil, err
}
return conn, nil
}
复制代码
更新main.go
最後に、我々は、更新する必要がありmain.go
、ファイルを接続ごとに新しいものを作成Client
し、使用してPool
クライアントを登録するには:
package main
import (
"fmt"
"net/http"
"github.com/TutorialEdge/realtime-chat-go-react/pkg/websocket"
)
func serveWs(pool *websocket.Pool, w http.ResponseWriter, r *http.Request) {
fmt.Println("WebSocket Endpoint Hit")
conn, err := websocket.Upgrade(w, r)
if err != nil {
fmt.Fprintf(w, "%+v\n", err)
}
client := &websocket.Client{
Conn: conn,
Pool: pool,
}
pool.Register <- client
client.Read()
}
func setupRoutes() {
pool := websocket.NewPool()
go pool.Start()
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
serveWs(pool, w, r)
})
}
func main() {
fmt.Println("Distributed Chat App v0.01")
setupRoutes()
http.ListenAndServe(":8080", nil)
}
复制代码
テスト
Now'veすべての必要な変更を行って、我々はすでに完了した作業をテストし、すべてが期待どおりに動作することを確認する必要があります。
あなたのバックエンドアプリケーションを起動します。
$ go run main.go
Distributed Chat App v0.01
复制代码
あなたは、いくつかのブラウザ開いた場合はHTTPを:// localhostを:3000、あなたは彼らが自動的にバックエンドのWebSocketサービスに接続することを確認することができ、我々は今送信し、顧客のニュースのもう一方の端を、同じプールから受け取ることができます!
概要
このセクションでは、複数のクライアントを処理する方法を達成するために管理され、プール内のブロードキャストメッセージコネクションに接続されているすべての人へ。
今面白い取得し始め。私たちは、このようなカスタムメッセージとして、次のセクションで新しい機能を追加することができます。
次のセクション:パート5 - 最適化されたフロントエンド