截至目前為止,我們都把程式碼全部塞在 ./cmd/olivine/main.go 裡--雖然 AI 常常這麼幹,但這顯然不是個好主意。
App 結構體我一般會習慣在 main package 底下建立一個 App{} 結構體,它會用來控制整個應用程式的生命週期(初始化、啟動、關閉等)。
首先,建立一個 App{},並且定義 main.(*App).Run() 用於啟動服務:
// file: ./cmd/olivine/app.go
package main
type App struct{}
func (*App) Run() error {}
將原本放在 ./cmd/olivine/main.go 的實作整個搬到 main.(*App).Run() 中。
// file: ./cmd/olivine/app.go
func (*App) Run() error {
slog.Info("starting olivine server...")
listener, err := net.Listen("tcp", ":16879")
if err != nil {
return fmt.Errorf("failed to listen tcp: %w", err)
}
defer listener.Close()
for {
connection, err := listener.Accept()
if err != nil {
slog.Error("failed to accept connection:", slog.Any("error", err))
continue
}
go handleConnection(connection)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
message, err := reader.ReadString('\n')
if err != nil {
slog.Error("failed to read from conn:", slog.Any("error", err))
return
}
ackmsg := strings.ToUpper(strings.ToUpper(message))
response := "ACK: " + ackmsg
if _, err := conn.Write([]byte(response)); err != nil {
slog.Error("failed to write to conn:", slog.Any("error", err))
return
}
}
並且在 main.go 調用 main.(*App).Run() 即可
// file: ./cmd/olivine/main.go
package main
import "log/slog"
func main() {
app := &App{}
if err := app.Run(); err != nil {
slog.Error("failed to run app:", slog.Any("error", err))
}
}