Raspbian「stretch」上にGO言語をインストール」でGo言語をインストールしましたが、今回はサーバアドレスを設定ファイルから読み込み、Json形式のデータをHTTPでサーバにアップロードします。アプリはWindows10上のVisual Studio Codeで開発し、Raspberry Pi上で動作させます。

開発環境

  • Raspbian:buster
  • go言語のバージョン:go1.13.4
  • コーディング環境:Visual Studio Code 1.44.2

Go言語パッケージのインストール

次のコマンドで設定ファイルを読み込み用のパッケージ「ini.v1」をインストールします。

$ go get gopkg.in/ini.v1

パッケージ「ini.v1」

設定ファイルを読み込む機能を持つパッケージで、設定ファイルの形式を次に示します。詳細については「package ini」に示します。
config.ini

[web]
url = https://tomosoft.jp/xxxxxx
port = 443
 
[db]
name = oreoredb
user = userA

アプリの作成

作業フォルダ「gohttp」に次に示す「httpmain.go」「httplib.go」「inilib.go」を作成します。

  • package名は「main」固定とします。これにより、別ファイルの変数も参照可能となります。最初の文字が大文字で始まる変数、関数等は、外部パッケージ(package名が異なる)から参照可能になります。
  • package名「main」の関数名「main」がメインルーチンとなります。メインルーチンの前には初期化ルーチン「init」が最初に一度だけ実行されます。
  • アプリは、メインルーチンからゴルーチン「send」を起動し、ゴルーチン「send」からチャンネル「httpch」を使用してJson形式の送信データをHTTPルーチンに通知します。

メインルーチン「main」と初期化ルーチン「init」を次に示します。

  • 16行目で設定ファイルを読み込みます。
  • 13行目でチャンネル「httpch」を作成し、20行目でゴルーチン「send」を起動し、26行目でチャンネル「httpch」から作成したJson形式の送信データをゴルーチン「send」に送信します。10秒ごとに送信データを更新して送信し続けます。

httpmain.go

package main

import (
	"fmt"
	"time"
)

type Httpst struct {
	ID   string
	data []byte
}

var httpch = make(chan Httpst)

func init() {
	readconfig()
}

func main() {
	go send()

	var i byte = 1
	for {
		b := []byte{0xff, uint8(i)}
		i++
		httpch <- Httpst{"TomoSoft", b}
		time.Sleep(time.Second * 10)
	}

	fmt.Printf("Exit")
}

HTTPルーチン「send」を次に示します。

  • 20行目でチャンネル「httpch」を経由して送信するJson形式の送信データを受け取ります。
  • 13行目いでJson形式「Sample 」を定義し、24行目でデータを作成します。
  • 33行目でJson形式でサーバにデータを送信します。

httplib.go

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

var URL string

// sample structure
type Sample struct {
	Id    string `json: "id"`
	Count int    `json: "Count"`
}

func httpsend() {
	URL = Cnf.Url
	senddata := <-httpch
	fmt.Printf("%#v\n", senddata)

	// build sample structure
	sample := new(Sample)
	sample.Id = senddata.ID
	sample.Count = int(senddata.data[1])

	// encode json
	sample_json, _ := json.Marshal(sample)
	fmt.Printf("[+] %s\n", string(sample_json))

	// send json
	res, err := http.Post(URL, "application/json", bytes.NewBuffer(sample_json))
	defer res.Body.Close()

	if err != nil {
		fmt.Printf("[!] " + err.Error() + "\n")
	} else {
		fmt.Printf("[*] " + res.Status + "\n")
	}
}
func send() {
	for {
		httpsend()
	}
}

設定ファイル読み込みルーチン「readconfig」を次に示します。

  • 24行目のMustInt関数は、エラーが発生すると「0」を戻します。
  • 25行目のMustString関数は、指定された文字列が空の場合、指定された文字列を戻します。

inilib.go

package main

import (
	"fmt"

	"gopkg.in/ini.v1"
)

type Config struct {
	Url  string
	Port int
	Db   string
	User string
}

var Cnf Config

func readconfig() {
	c, _ := ini.Load("conf/config.ini")

	// MustStringはデフォルトあり Stringはデフォルトなし、
	Cnf = Config{
		Url:  c.Section("web").Key("url").String(),
		Port: c.Section("web").Key("port").MustInt(),
		Db:   c.Section("db").Key("name").MustString("hogehoge.sql"),
		User: c.Section("db").Key("user").String(),
	}

	fmt.Printf("%v \n", Cnf.Url)
	fmt.Printf("%v \n", Cnf.Port)
	fmt.Printf("%v \n", Cnf.Db)
	fmt.Printf("%v \n", Cnf.User)
}

アプリの実行

次のコマンドで作成したアプリをコンパイルして実行します。設定ファイルのデータが読み込まれ、指定したサーバアドレスにデータを送信されていることが確認できます。なお、あらかじめテスト用のサーバを起動しておき、サーバはデータを受信すると「200 OK」を戻します。

$ go build
$ ./gohttp
https://tomosoft.jp/xxxxx
443
oreoredb
userA
main.Httpst{ID:"TomoSoft", data:[]uint8{0xff, 0x1}}
[+] {"Id":"TomoSoft","Count":1}
[*] 200 OK
main.Httpst{ID:"TomoSoft", data:[]uint8{0xff, 0x2}}
[+] {"Id":"TomoSoft","Count":2}
[*] 200 OK
main.Httpst{ID:"TomoSoft", data:[]uint8{0xff, 0x3}}
[+] {"Id":"TomoSoft","Count":3}
[*] 200 OK
main.Httpst{ID:"TomoSoft", data:[]uint8{0xff, 0x4}}
[+] {"Id":"TomoSoft","Count":4}
[*] 200 OK