第18章 ネットワーク

ネットワークプログラミングの基本

Go言語における「ネットワークプログラミングの基本」では、ネットワーク通信の基本的な概念とGoでの実装方法を理解します。ここでは、基本的なクライアントとサーバーの例を紹介します。


TCPサーバーの作成

TCPサーバーは、指定されたポートでクライアントからの接続を待ち受け、データを送受信します。

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
)

func main() {
    // TCPサーバーをlocalhostのポート8080で起動
    listener, err := net.Listen("tcp", "localhost:8080")
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(1)
    }
    defer listener.Close()
    fmt.Println("Server is listening on localhost:8080")

    for {
        // クライアントからの接続を待機
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting connection:", err)
            continue
        }

        go handleConnection(conn)
    }
}

// handleConnection はクライアントからの接続を処理します
func handleConnection(conn net.Conn) {
    defer conn.Close()

    // クライアントからのメッセージを読み込む
    reader := bufio.NewReader(conn)
    msg, err := reader.ReadString('\n')
    if err != nil {
        fmt.Println("Error reading message:", err)
        return
    }
    fmt.Printf("Received message: %s", msg)

    // クライアントに応答を送信
    conn.Write([]byte("Message received.\n"))
}

このコードでは、net.Listenを使用してTCPサーバーを起動し、`listener.Accept()`でクライアントからの接続を待ち受けます。接続が確立されると、handleConnection関数が新しいゴルーチンで呼び出され、クライアントからのメッセージを読み取り、応答を送信します。


TCPクライアントの作成

TCPクライアントは、サーバーに接続してデータの送受信を行います。

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
)

func main() {
    // サーバーへの接続を確立
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        fmt.Println("Error connecting to server:", err)
        os.Exit(1)
    }
    defer conn.Close()
    // サーバーにメッセージを送信
    fmt.Fprint(conn, "Hello from client\n")

    // サーバーからの応答を読み込む
    response, err := bufio.NewReader(conn).ReadString('\n')
    if err != nil {
        fmt.Println("Error reading response:", err)
        return
    }
    fmt.Printf("Server response: %s", response)
}

このコードでは、net.Dialを使用して指定されたアドレスのサーバーに接続し、サーバーにメッセージを送信した後、サーバーからの応答を待ち受けています。


UDPサーバーの作成
package main

import (
    "fmt"
    "net"
)

func main() {
    // UDPサーバーを起動
    addr := net.UDPAddr{
        Port: 8080,
        IP:   net.ParseIP("127.0.0.1"),
    }
    conn, err := net.ListenUDP("udp", &addr)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer conn.Close()

    fmt.Println("UDP server listening on 127.0.0.1:8080")

    // データの受信
    buffer := make([]byte, 1024)
    for {
        n, remoteAddr, err := conn.ReadFromUDP(buffer)
        if err != nil {
            fmt.Println(err)
            continue
        }
        fmt.Printf("Received: %s from %s\n", string(buffer[:n]), remoteAddr)
    }
}

このサーバーは、指定されたアドレスとポートでUDPパケットを待ち受け、受信したデータをコンソールに表示します。


UDPクライアントの作成
package main

import (
    "fmt"
    "net"
)

func main() {
    // サーバーのアドレスを設定
    serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:8080")
    if err != nil {
        fmt.Println(err)
        return
    }

    // クライアントのUDP接続を作成
    conn, err := net.DialUDP("udp", nil, serverAddr)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer conn.Close()

    // サーバーにメッセージを送信
    _, err = conn.Write([]byte("Hello UDP server!"))
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("Message sent to UDP server")
}

UDPアドレスに対してメッセージを送信します。net.DialUDP関数を使用してUDP接続を確立し、Writeメソッドでデータを送信します。


HTTPクライアントとサーバー

Go言語における「HTTPクライアントとサーバー」の概念は、Webアプリケーション開発において非常に重要です。HTTPクライアントはサーバーにリクエストを送信し、サーバーはそれに応答します。以下に、Go言語でHTTPサーバーを構築し、クライアントからのリクエストを処理する具体的な例を示します。


HTTPサーバーの構築

Go言語のnet/httpパッケージを使用して、簡単なHTTPサーバーを構築できます。

package main

import (
    "fmt"
    "net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, Go Web Server!")
}

func main() {
    // ハンドラ関数をルートURLに割り当て
    http.HandleFunc("/", helloHandler)

    // サーバーを8080ポートで起動
    fmt.Println("Server is running on http://localhost:8080")
    http.ListenAndServe(":8080", nil)
}

このコードは、ルートURL(/)にアクセスがあった際にhelloHandler関数を呼び出すように設定しています。http.ListenAndServeは、8080ポートでサーバーを起動し、リクエストを待ち受けます。


HTTPクライアントの使用

Goのnet/httpパッケージを使用して、HTTPクライアントを作成し、外部のWebサーバーにリクエストを送信できます。

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    // HTTP GETリクエストを送信
    response, err := http.Get("http://localhost:8080")
    if err != nil {
        fmt.Println("Error making GET request:", err)
        return
    }
    defer response.Body.Close()

    // レスポンスボディを読み込み
    body, err := ioutil.ReadAll(response.Body)
    if err != nil {
        fmt.Println("Error reading response body:", err)
        return
    }

    // レスポンスボディを表示
    fmt.Println("Response from server:", string(body))
}

このクライアントはhttp.Get関数を使用してhttp://localhost:8080にGETリクエストを送信し、サーバーからのレスポンスを表示します。


WEBソケット

Go言語での「Webソケット」の使用は、リアルタイム通信が必要なアプリケーションにおいて重要です。Webソケットは、サーバーとクライアント間の持続的な双方向通信を可能にします。以下に、Go言語を使用してシンプルなWebソケットサーバーとクライアントを構築する具体的な例を示します。


Webソケットサーバーの構築

Webソケットサーバーは、クライアントからのWebソケット接続を受け入れ、メッセージの送受信を行います。Goでは、github.com/gorilla/websocketパッケージが一般的に使用されます。

まず、Gorilla Websocketパッケージをインストールします。

go get github.com/gorilla/websocket

次に、Webソケットサーバーを実装します。

package main

import (
    "github.com/gorilla/websocket"
    "log"
    "net/http"
)

var upgrader = websocket.Upgrader{} // Webソケットアップグレーダー

func websocketHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil) // HTTP接続をWebソケットにアップグレード
    if err != nil {
        log.Println(err)
        return
    }
    defer conn.Close()

    for {
        messageType, message, err := conn.ReadMessage() // メッセージを受信
        if err != nil {
            log.Println("Read error:", err)
            break
        }
        log.Printf("Received: %s", message)
        // クライアントにメッセージをエコーバック
        if err := conn.WriteMessage(messageType, message); err != nil {
            log.Println("Write error:", err)
            break
        }
    }
}

func main() {
    http.HandleFunc("/ws", websocketHandler)

    log.Println("Websocket server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

このコードは、/wsエンドポイントでWebソケットリクエストを受け入れ、クライアントとの通信を開始します。サーバーはクライアントからのメッセージを読み取り、そのメッセージをエコーバックします


Webソケットクライアントの実装

Webソケットクライアントは、サーバーに接続してメッセージの送受信を行います。

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Client</title>
    <script type="text/javascript">
        var conn;

        function connect() {
            conn = new WebSocket("ws://localhost:8080/ws");

            conn.onopen = function(e) {
                console.log("Connection established!");
            };

            conn.onmessage = function(e) {
                console.log("Received message: " + e.data);
            };

            conn.onerror = function(e) {
                console.log("Error: ", e);
            };
        }

        function sendMessage() {
            var message = document.getElementById("message").value;
            conn.send(message);
        }
    </script>
</head>
<body>
    <button onclick="connect()">Connect</button>
    <input type="text" id="message">
    <button onclick="sendMessage()">Send Message</button>
</body>
</html>

このHTMLページには、「Connect」ボタンとメッセージ送信用の入力フィールドがあります。ユーザーが「Connect」ボタンをクリックすると、サーバーに対するWebソケット接続が確立され、テキストフィールドに入力されたメッセージがサーバーに送信されます。


ネットワークセキュリティ

Go言語における「ネットワークセキュリティ」は、データの安全な転送と脆弱性からの保護を目的としています。以下に、HTTPSの使用、基本認証、そして安全なデータの取り扱いに関する具体的な事例を示します。


HTTPSの使用

HTTPS(HTTP over TLS)は、データを暗号化してインターネット上で安全に転送するためのプロトコルです。Go言語では、http.ListenAndServeTLS関数を使用してHTTPSサーバーを簡単に実装できます。

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, HTTPS world!")
}

func main() {
    http.HandleFunc("/", handler)

    // HTTPSサーバーの起動
    fmt.Println("Starting HTTPS server on :443")
    err := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
    if err != nil {
        fmt.Println("Error starting server:", err)
    }
}

このコードは、証明書ファイル(server.crt)と秘密鍵ファイル(server.key)を使用してHTTPSサーバーを起動します。クライアントはこのサーバーとの間で安全な通信を行うことができます。


基本認証の実装

基本認証は、ユーザー名とパスワードを使用してHTTPリクエストの認証を行うシンプルな方法です。

func basicAuth(handler http.HandlerFunc, username, password string) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        user, pass, ok := r.BasicAuth()
        if !ok || user != username || pass != password {
            w.Header().Set("WWW-Authenticate", `Basic realm="restricted"`)
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        handler(w, r)
    }
}

func main() {
    http.HandleFunc("/", basicAuth(handler, "user", "pass"))

    http.ListenAndServe(":8080", nil)
}

このコードでは、ユーザー名とパスワードが正しい場合にのみリクエストを処理するbasicAuthミドルウェアを定義しています。


安全なデータの取り扱い

安全なWebアプリケーションの開発には、SQLインジェクションやクロスサイトスクリプティング(XSS)などの脆弱性を防ぐための対策が必要です。

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

func queryUser(db *sql.DB, username string) {
    // パラメータ化されたクエリ(SQLインジェクション対策の例)
    row := db.QueryRow("SELECT * FROM users WHERE username = ?", username)

    // ...
}

このコードでは、ユーザー入力を直接SQL文に埋め込む代わりに、プレースホルダーを使用し、QueryRow関数にパラメータとして渡しています。


練習問題1.

Go言語を使用してTCPエコーサーバーを作成し、クライアントから受信したメッセージをそのままクライアントに返すプログラムを書いてください。

package main

import (
    // 必要なパッケージをインポート
)

func main() {
    // TCPサーバーを起動し、クライアントからの接続を待ち受ける
    // クライアントからのメッセージを読み込み、同じメッセージをクライアントに返す
}


練習問題2.

Go言語を使用してUDPサーバーとクライアントを作成し、クライアントがサーバーにメッセージを送信し、サーバーがそれを受信してコンソールに表示するプログラムを書いてください。

// UDPサーバープログラム
package main

import (
    // 必要なパッケージをインポート
)

func main() {
    // UDPサーバーを起動し、メッセージを受信して表示する
}

// UDPクライアントプログラム
package main

import (
    // 必要なパッケージをインポート
)

func main() {
    // UDPサーバーにメッセージを送信する
}


練習問題3.

Go言語を使用してHTTPクライアントを作成し、特定のURL(例えば http://example.com)からデータを取得し、その内容をコンソールに表示するプログラムを書いてください。

package main

import (
    // 必要なパッケージをインポート
)

func main() {
    // HTTP GETリクエストを送信し、レスポンスを取得する
    // レスポンスボディをコンソールに表示する
}