GO言語でTCP/IP通信をする

# Goにおけるプロセス間通信
Go言語では, 言語レベルで用意されているgo構文によってプロセス(それともスレッド？)を立ち上げ
チャンネルを経由して互いに通信を行うことができます.
しかし, この方法で通信できるのはプログラム内部で生成したプロセス間のみで, それ以外の論理的あるいは物理的に
離れたプロセス間では通信したい場合は, TCP通信などの別の方法を使用しないといけないようです.
今回はクライアントからのメッセージをそのまま返すだけのエコープログラムを実装していこうと思います.


# クライアント
## コード


```golang:client.go
package main

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


func main() {
    connection, error := net.Dial("tcp", "localhost:10000");

    if error != nil {
        panic(error);
    }

    defer connection.Close()
    sendMessage(connection);
}

func sendMessage(connection net.Conn) {
    fmt.Print("> ");

    stdin := bufio.NewScanner(os.Stdin)
    if stdin.Scan() == false {
        fmt.Println("Ciao ciao!");
        return;
    }

    _, error := connection.Write([]byte(stdin.Text()));

    if error != nil {
        panic(error);
    }

    var response = make([]byte, 4 * 1024);
    _, error = connection.Read(response);

    if (error != nil) {
        panic(error);
    }

    fmt.Printf("Server> %s \n", response);

    sendMessage(connection)
}

```

## 解説
まずは簡単なクライアントから.
処理の流れは, まず`net`パッケージで用意されている`Dial`関数を使ってサーバとのコネクションを取得します

```golang
    connection, error := net.Dial("tcp", "localhost:10000");
```

次に, 取得したコネクションを使ってサーバにメッセージを送るのですが, プログラム終了時に確実に
コネクションが解放されるように `defer`構文を使ってコネクション開放命令を予約しておきます.

```golang
    defer connection.Close()
    sendMessage(connection);
```

メッセージ送信関数である`sendMessage`では, まず標準入力から一行受け取り

```golang
    stdin := bufio.NewScanner(os.Stdin)
    if stdin.Scan() == false {
        fmt.Println("Ciao ciao!");
        return;
    }
```

受け取った文字列をバイト列に変換して, コネクション経由で送信します

```golang
    _, error := connection.Write([]byte(stdin.Text()));
```

`connection#Write` 関数の一番目の返り値は送信したデータのバイト数のようですが, 今回は使わないので
`_`変数を使って捨てています.
データの送信の後, 今度はサーバからの受信を待ち, 結果を標準出力に出力します.

```golang

    var response = make([]byte, 4 * 1024);
    _, error = connection.Read(response);

    if (error != nil) {
        panic(error);
    }

    fmt.Printf("Server> %s \n", response);
```

最後に, 再び`sendMessage`関数を呼び出し入力待ちに戻ります.

```golang
    sendMessage(connection);
```

プログラムを終了したい場合は, コマンドラインからCtrl-Cを入力すると`stdin#Scan`がfalseを返すので
正常に終了します.


# Server
## コード
```golang

package main

import (
    "fmt"
    "net"
    "io"
)

func main() {

    listener, error := net.Listen("tcp", "localhost:10000");

    if error != nil {
        panic(error);
    }

    fmt.Println("Server running at localhost:10000");

    waitClient(listener);

}

func waitClient(listener net.Listener) {
    connection, error := listener.Accept();

    if error != nil {
        panic(error);
    }

    go goEcho(connection);

    waitClient(listener);
}

func goEcho(connection net.Conn) {
    defer connection.Close();
    echo(connection);
}

func echo(connection net.Conn) {

    var buf = make([]byte, 1024);

    n, error := connection.Read(buf);
    if (error != nil) {
        if error == io.EOF {
            return;
        } else {
            panic(error);
        }
    }

    fmt.Printf("Client> %s \n", buf);

    n, error = connection.Write(buf[:n])
    if error != nil {
        panic(error);
    }

    echo(connection)
}

```

## 解説
サーバ側のプログラムは, クライアントに比べるとちょっとだけ複雑になります

まず最初に, `net`パッケージの`Listen`関数を使って通信用ポートを確保します.

```golang
    listener, error := net.Listen("tcp", "localhost:10000");
```

ポートの確保に成功したら, `waitClient`関数を呼び出しクライアントからの接続待ち状態にします

```golang
    if error != nil {
        panic(error);
    }

    fmt.Println("Server running at localhost:10000");

    waitClient(listener);
```

`waitClient`関数では, `Listener#Accept`関数でクライアントからの接続を受け付け, 

```golang
    connection, error := listener.Accept();
```

その後, ゴールーチンを生成し, クライアントからのリクエスト処理を移譲します

```golang
    go goEcho(connection);
```

最後に, 再びwaitClient関数を呼び出し, クライアントからの接続待ち状態に戻ります

```golang
    waitClient(listener);
```

クライアントとの通信を移譲されたゴールーチンでは, コネクションからの入力を受け取り
同じ文字列をそのままコネクションに送信します.

```golang
func goEcho(connection net.Conn) {
    defer connection.Close();
    echo(connection);
}

func echo(connection net.Conn) {

    var buf = make([]byte, 1024);

    n, error := connection.Read(buf);
    if (error != nil) {
        if error == io.EOF {
            return;
        } else {
            panic(error);
        }
    }

    fmt.Printf("Client> %s \n", buf);

    n, error = connection.Write(buf[:n])
    if error != nil {
        panic(error);
    }

    echo(connection)
}
```

基本的な処理はクライアントと同じなので解説は省略します.
一点だけ異なるのは, クライアント側でコネクションを閉じた際, `Conn#read`関数のエラーとして`io.EOF`が返されるようなので
これを受け取ったら処理のループを抜けるようにしています.

```golang
    n, error := connection.Read(buf);
    if (error != nil) {
        if error == io.EOF {
            return;
        } else {
            panic(error);
        }
    }
```

# 最後に
これでGo言語によるTPC通信の簡単な解説を終わります.
サンプルが単純すぎて実践的でないので, もう少し複雑なサンプルができたら
続きを書きたいと思います.