まずserverにおりてgo run main.goでaddress with portをcheck
package main
// TinyGo Pico W HTTP Client
// Build with -scheduler=tasks
import (
"context"
"errors"
"io"
"log/slog"
"machine"
"net/netip"
"time"
"github.com/soypat/cyw43439"
"github.com/soypat/cyw43439/examples/cywnet"
"github.com/soypat/cyw43439/examples/cywnet/credentials"
"github.com/soypat/lneto/http/httpraw"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/tcp"
)
const (
connTimeout = 5 * time.Second
tcpbufsize = 2030
// HTTPサーバーのIPアドレスとポート
serverAddrStr = "192.168.0.6:8080"
ourHostname = "tinygo-http-client"
)
// DHCPで要求するIPアドレス
var requestedIP = [4]byte{192, 168, 0, 99}
func main() {
// USBシリアル接続待ち
time.Sleep(2 * time.Second)
logger := slog.New(
slog.NewTextHandler(
machine.Serial,
&slog.HandlerOptions{
Level: slog.LevelInfo,
},
),
)
println("--- Pico W HTTP Client Start ---")
// ========================================
// Wi-Fi 初期化
// ========================================
devcfg := cyw43439.DefaultWifiConfig()
devcfg.Logger = logger
cystack, err := cywnet.NewConfiguredPicoWithStack(
credentials.SSID(),
credentials.Password(),
devcfg,
cywnet.StackConfig{
Hostname: ourHostname,
MaxActiveTCPPorts: 1,
},
)
if err != nil {
panic("WiFi setup failed: " + err.Error())
}
// ========================================
// ネットワーク処理開始
// ========================================
go loopForeverStack(cystack)
// ========================================
// DHCP
// ========================================
dhcpResults, err := cystack.SetupWithDHCP(
cywnet.DHCPConfig{
RequestedAddr: netip.AddrFrom4(requestedIP),
},
)
if err != nil {
panic("DHCP failed: " + err.Error())
}
logger.Info(
"DHCP complete",
slog.String(
"addr",
ipv4.String(dhcpResults.AssignedAddr4),
),
)
// ========================================
// HTTPサーバーアドレス
// ========================================
serverAddr, err := netip.ParseAddrPort(serverAddrStr)
if err != nil {
panic("Server address error: " + err.Error())
}
// ========================================
// TCPスタック
// ========================================
stack := cystack.StackAsync()
rstack := stack.StackRetrying(
cywnet.DefaultStackBackoff,
)
// ========================================
// TCP接続設定
// ========================================
var conn tcp.Conn
err = conn.Configure(
tcp.ConnConfig{
RxBuf: make([]byte, tcpbufsize),
TxBuf: make([]byte, tcpbufsize),
TxPacketQueueSize: 3,
// lneto v0.4.0 では必須 ここが重要げ
RWBackoff: cywnet.DefaultStackBackoff,
},
)
if err != nil {
panic("conn configure: " + err.Error())
}
// ========================================
// HTTP GET リクエスト作成
// ========================================
var hdr httpraw.HeaderV1
hdr.SetMethod("GET")
hdr.SetRequestTarget(
"/update?t=23&h=45",
)
hdr.SetProtocol("HTTP/1.1")
hdr.Set(
"Host",
serverAddr.String(),
)
hdr.Set(
"Connection",
"close",
)
reqbytes, err := hdr.AppendRequest(nil)
if err != nil {
panic("HTTP request error: " + err.Error())
}
logger.Info(
"http:ready",
slog.String("serveraddr", serverAddrStr),
)
// ========================================
// TCP接続・HTTP送信
// ========================================
rxBuf := make([]byte, 4096)
for {
time.Sleep(2 * time.Second)
// ランダムなローカルポート
localPort := uint16(stack.Prand32()>>17) + 1024
logger.Info(
"TCP dialing",
slog.String("server", serverAddrStr),
)
// TCP接続
err = rstack.DoDialTCP(
context.Background(),
&conn,
localPort,
serverAddr,
connTimeout,
3,
)
if err != nil {
logger.Error(
"TCP dial failed",
slog.String("err", err.Error()),
)
closeConn(&conn)
continue
}
logger.Info("TCP connected")
// ====================================
// HTTP GET送信
// ====================================
logger.Info(
"HTTP GET sending",
slog.String(
"path",
"/update?t=23&h=45",
),
)
_, err = conn.Write(reqbytes)
if err != nil {
logger.Error(
"HTTP write failed",
slog.String("err", err.Error()),
)
closeConn(&conn)
continue
}
// 送信完了待ち
err = conn.Flush()
if err != nil {
logger.Error(
"HTTP flush failed",
slog.String("err", err.Error()),
)
closeConn(&conn)
continue
}
// ====================================
// HTTPレスポンス受信
// ====================================
println("--- HTTP Response ---")
_ = conn.SetReadDeadline(
time.Now().Add(5 * time.Second),
)
for {
n, readErr := conn.Read(rxBuf)
if n > 0 {
_, _ = machine.Serial.Write(
rxBuf[:n],
)
}
if readErr != nil {
if !errors.Is(readErr, io.EOF) {
logger.Info(
"HTTP read finished",
slog.String("err", readErr.Error()),
)
}
break
}
if n == 0 {
break
}
}
println("\n--- HTTP Complete ---")
closeConn(&conn)
// 1回送信して終了
return
}
}
// ============================================
// TCP接続終了処理
// ============================================
func closeConn(conn *tcp.Conn) {
_ = conn.Close()
for i := 0; i < 50; i++ {
if conn.State().IsClosed() {
break
}
time.Sleep(100 * time.Millisecond)
}
conn.Abort()
}
// ============================================
// ネットワーク処理ループ
// ============================================
func loopForeverStack(stack *cywnet.Stack) {
for {
send, recv, _ := stack.RecvAndSend()
if send == 0 && recv == 0 {
time.Sleep(5 * time.Millisecond)
}
}
}