(IV)移動して、チャットシステムのReactJSを構築するために使用

このセクションでは、完全なコードを提供します。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 - 最適化されたフロントエンド

おすすめ

転載: juejin.im/post/5d43d4c0f265da03cd0a6060