examples/ping: Add a ping example
This commit is contained in:
parent
a00e9d3183
commit
bb14ec20a7
71
examples/ping/client/main.go
Normal file
71
examples/ping/client/main.go
Normal file
@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import "os"
|
||||
import "io"
|
||||
import "fmt"
|
||||
import "log"
|
||||
import "net"
|
||||
import "time"
|
||||
import "errors"
|
||||
import "context"
|
||||
import "git.tebibyte.media/sashakoshka/hopp"
|
||||
import "git.tebibyte.media/sashakoshka/hopp/examples/ping"
|
||||
|
||||
func main() {
|
||||
name := os.Args[0]
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s HOST:PORT\n", name)
|
||||
os.Exit(2)
|
||||
}
|
||||
address := os.Args[1]
|
||||
|
||||
conn, err := dial(address)
|
||||
handleErr(1, err)
|
||||
trans, err := conn.OpenTrans()
|
||||
handleErr(1, err)
|
||||
|
||||
go func() {
|
||||
defer fmt.Fprintf(os.Stdout, "(i) disconnected\n")
|
||||
for {
|
||||
message, _, err := ping.Receive(trans)
|
||||
if err != nil {
|
||||
if !errors.Is(err, io.EOF) {
|
||||
handleErr(1, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
switch message := message.(type) {
|
||||
case *ping.MessagePong:
|
||||
log.Printf("--> pong (%d) from %v", message, address)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
message := ping.MessagePing(0)
|
||||
for {
|
||||
log.Printf("<-- ping (%d)", message)
|
||||
_, err := ping.Send(trans, &message)
|
||||
handleErr(1, err)
|
||||
message ++
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func dial(address string) (hopp.Conn, error) {
|
||||
// ctx, done := context.WithTimeout(context.Background(), 16 * time.Second)
|
||||
// defer done()
|
||||
// conn, err := hopp.Dial(ctx, "tcp", address, nil)
|
||||
// if err != nil { return nil, err }
|
||||
// return conn, nil
|
||||
underlying, err := net.Dial("tcp", address)
|
||||
if err != nil { return nil, err }
|
||||
conn := hopp.AdaptA(underlying, hopp.PartyClient)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func handleErr(code int, err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", os.Args[0], err)
|
||||
os.Exit(code)
|
||||
}
|
||||
}
|
||||
143
examples/ping/protocol.go
Normal file
143
examples/ping/protocol.go
Normal file
@ -0,0 +1,143 @@
|
||||
package ping
|
||||
|
||||
// Code generated by the Holanet PDL compiler. DO NOT EDIT.
|
||||
// The source file is located at <path>
|
||||
// Please edit that file instead, and re-compile it to this location.
|
||||
// HOPP, TAPE, METADAPT, PDL/0 (c) 2025 holanet.xyz
|
||||
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/sashakoshka/hopp"
|
||||
import "git.tebibyte.media/sashakoshka/hopp/tape"
|
||||
|
||||
// Table is a KTV table with an undefined schema.
|
||||
type Table = map[uint16] any
|
||||
|
||||
// Message is any message that can be sent along this protocol.
|
||||
type Message interface {
|
||||
tape.Encodable
|
||||
tape.Decodable
|
||||
|
||||
// Method returns the method code of the message.
|
||||
Method() uint16
|
||||
}
|
||||
|
||||
// Send encodes a message and sends it along a transaction.
|
||||
func Send(trans hopp.Trans, message Message) (n int, err error) {
|
||||
writer, err := trans.SendWriter(message.Method())
|
||||
if err != nil { return n, err }
|
||||
defer writer.Close()
|
||||
encoder := tape.NewEncoder(writer)
|
||||
n, err = message.Encode(encoder)
|
||||
if err != nil { return n, err }
|
||||
return n, encoder.Flush()
|
||||
}
|
||||
|
||||
// canAssign determines if data from the given source tag can be assigned to
|
||||
// a Go type represented by destination. It is designed to receive destination
|
||||
// values from [generate.Generator.generateCanAssign]. The eventual Go type and
|
||||
// the destination tag must come from the same (or hash-equivalent) PDL type.
|
||||
func canAssign(destination, source tape.Tag) bool {
|
||||
if destination.Is(source) { return true }
|
||||
if (destination.Is(tape.SBA) || destination.Is(tape.LBA)) &&
|
||||
(source.Is(tape.SBA) || source.Is(tape.LBA)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// boolInt converts a bool to an integer.
|
||||
func boolInt(input bool) int {
|
||||
if input {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// ensure ucontainer is always imported
|
||||
var _ hopp.Option[int]
|
||||
|
||||
// Ping is sent by the client to the server. It may contain any number. This
|
||||
// number will be returned to the client via a [Pong] message.
|
||||
type MessagePing int32
|
||||
|
||||
// Method returns the message's method number.
|
||||
func(this *MessagePing) Method() uint16 { return 0x0000 }
|
||||
|
||||
// Encode encodes this message's tag and value.
|
||||
func(this *MessagePing) Encode(encoder *tape.Encoder) (n int, err error) {
|
||||
tag_1 := tape.LSI.WithCN(3)
|
||||
nn, err := encoder.WriteTag(tag_1)
|
||||
n += nn; if err != nil { return n, err }
|
||||
nn, err = encoder.WriteInt32(int32((*this)))
|
||||
n += nn; if err != nil { return n, err }
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Decode decodes this message's tag and value.
|
||||
func(this *MessagePing) Decode(decoder *tape.Decoder) (n int, err error) {
|
||||
tag, nn, err := decoder.ReadTag()
|
||||
n += nn; if err != nil { return n, err }
|
||||
if !(canAssign(tape.LSI, tag)) {
|
||||
nn, err = tape.Skim(decoder, tag)
|
||||
n += nn; if err != nil { return n, err }
|
||||
return n, nil
|
||||
}
|
||||
destination_2, nn, err := decoder.ReadInt32()
|
||||
n += nn; if err != nil { return n, err }
|
||||
*this = MessagePing(destination_2)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Pong is sent by the server to the client in response to a [Ping] message, It
|
||||
// will contain the same number as that message.
|
||||
type MessagePong int32
|
||||
|
||||
// Method returns the message's method number.
|
||||
func(this *MessagePong) Method() uint16 { return 0x0001 }
|
||||
|
||||
// Encode encodes this message's tag and value.
|
||||
func(this *MessagePong) Encode(encoder *tape.Encoder) (n int, err error) {
|
||||
tag_3 := tape.LSI.WithCN(3)
|
||||
nn, err := encoder.WriteTag(tag_3)
|
||||
n += nn; if err != nil { return n, err }
|
||||
nn, err = encoder.WriteInt32(int32((*this)))
|
||||
n += nn; if err != nil { return n, err }
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Decode decodes this message's tag and value.
|
||||
func(this *MessagePong) Decode(decoder *tape.Decoder) (n int, err error) {
|
||||
tag, nn, err := decoder.ReadTag()
|
||||
n += nn; if err != nil { return n, err }
|
||||
if !(canAssign(tape.LSI, tag)) {
|
||||
nn, err = tape.Skim(decoder, tag)
|
||||
n += nn; if err != nil { return n, err }
|
||||
return n, nil
|
||||
}
|
||||
destination_4, nn, err := decoder.ReadInt32()
|
||||
n += nn; if err != nil { return n, err }
|
||||
*this = MessagePong(destination_4)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Receive decodes a message from a transaction and returns it as a value.
|
||||
// Use a type switch to determine what type of message it is.
|
||||
func Receive(trans hopp.Trans) (message any, n int, err error) {
|
||||
method, reader, err := trans.ReceiveReader()
|
||||
decoder := tape.NewDecoder(reader)
|
||||
if err != nil { return nil, n, err }
|
||||
switch method {
|
||||
case 0x0000:
|
||||
var message MessagePing
|
||||
nn, err := message.Decode(decoder)
|
||||
n += nn; if err != nil { return nil, n, err }
|
||||
return message, n, nil
|
||||
case 0x0001:
|
||||
var message MessagePong
|
||||
nn, err := message.Decode(decoder)
|
||||
n += nn; if err != nil { return nil, n, err }
|
||||
return message, n, nil
|
||||
}
|
||||
return nil, n, fmt.Errorf("%w: M%04X", hopp.ErrUnknownMethod, method)
|
||||
}
|
||||
7
examples/ping/protocol.pdl
Normal file
7
examples/ping/protocol.pdl
Normal file
@ -0,0 +1,7 @@
|
||||
// Ping is sent by the client to the server. It may contain any number. This
|
||||
// number will be returned to the client via a [Pong] message.
|
||||
M0000 Ping I32
|
||||
|
||||
// Pong is sent by the server to the client in response to a [Ping] message, It
|
||||
// will contain the same number as that message.
|
||||
M0001 Pong I32
|
||||
77
examples/ping/server/main.go
Normal file
77
examples/ping/server/main.go
Normal file
@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import "os"
|
||||
import "io"
|
||||
import "fmt"
|
||||
import "log"
|
||||
import "errors"
|
||||
import "git.tebibyte.media/sashakoshka/hopp"
|
||||
import "git.tebibyte.media/sashakoshka/hopp/examples/ping"
|
||||
|
||||
func main() {
|
||||
name := os.Args[0]
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s HOST:PORT\n", name)
|
||||
os.Exit(2)
|
||||
}
|
||||
address := os.Args[1]
|
||||
err := listen(address)
|
||||
handleErr(1, err)
|
||||
}
|
||||
|
||||
func listen(address string) error {
|
||||
listener, err := hopp.Listen("tcp", address, nil)
|
||||
if err != nil { return err }
|
||||
log.Printf("(i) hosting on %s", address)
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil { return err }
|
||||
go run(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func run(conn hopp.Conn) {
|
||||
log.Printf("-=E %v connected", conn.RemoteAddr())
|
||||
defer log.Printf("X=- %v disconnected", conn.RemoteAddr())
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
trans, err := conn.AcceptTrans()
|
||||
if err != nil {
|
||||
if !errors.Is(err, io.EOF) {
|
||||
log.Printf("XXX %v failed: %v", conn.RemoteAddr(), err)
|
||||
}
|
||||
return
|
||||
}
|
||||
go runTrans(conn, trans)
|
||||
}
|
||||
}
|
||||
|
||||
func runTrans(conn hopp.Conn, trans hopp.Trans) {
|
||||
for {
|
||||
message, _, err := ping.Receive(trans)
|
||||
if err != nil {
|
||||
if !errors.Is(err, io.EOF) {
|
||||
log.Printf("XXX failed to receive message: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
switch message := message.(type) {
|
||||
case *ping.MessagePing:
|
||||
log.Printf("--> ping (%d) from %v", message, conn.RemoteAddr())
|
||||
response := ping.MessagePong(*message)
|
||||
_, err := ping.Send(trans, &response)
|
||||
if err != nil {
|
||||
log.Printf("XXX failed to send message: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleErr(code int, err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", os.Args[0], err)
|
||||
os.Exit(code)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user