Add pairchat example #2
@@ -1 +1,2 @@
|
||||
iroh-go/target/
|
||||
.*.sw[a-z]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# connect
|
||||
t connect
|
||||
|
||||
## Build a binary
|
||||
|
||||
```
|
||||
go build --ldflags '-linkmode external -extldflags=-static -s -w' ./main.go
|
||||
go build --ldflags '-linkmode external -extldflags=-static -s -w' ./connect.go
|
||||
```
|
||||
|
||||
If you run into `glibc` incompatibility, you can compile with `zig`/`musl`.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# pairchat
|
||||
|
||||
This is a more complicated version of `examples/connect` which allows the listener to also
|
||||
write back to the sender. The result is a basic pairwise (only 2 peers can connect at once)
|
||||
chat program.
|
||||
|
||||
## Build a binary
|
||||
|
||||
```
|
||||
go build --ldflags '-linkmode external -extldflags=-static -s -w' ./pairchat.go
|
||||
```
|
||||
|
||||
If you run into `glibc` incompatibility, you can compile with `zig`/`musl`.
|
||||
|
||||
```
|
||||
CC="zig cc -target x86_64-linux-musl -lunwind" \
|
||||
CGO_ENABLED=1 \
|
||||
CGO_LDFLAGS="-static" \
|
||||
GOOS=linux GOARCH=amd64 \
|
||||
go build -v -a -ldflags '-extldflags "-static"' ./pairchat.go
|
||||
```
|
||||
|
||||
## Terminal 1 (listener)
|
||||
|
||||
```
|
||||
./pairchat
|
||||
```
|
||||
|
||||
## Terminal 2 (sender)
|
||||
|
||||
```
|
||||
./pairchat -endpoint <endpoint-id>
|
||||
```
|
||||
|
||||
You'll see a connection notice if the two endpoints manage to connect to each other. You can
|
||||
type messages on both sides of the connection, and they will appear on the other end. So
|
||||
Terminal 1 will see what Terminal 2 types, and Terminal 2 will see what Terminal 1 types.
|
||||
@@ -0,0 +1,144 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
iroh "git.coopcloud.tech/decentral1se/iroh-go"
|
||||
)
|
||||
|
||||
var (
|
||||
alpn = []byte("iroh-go-pairchat/0")
|
||||
frameSize uint32 = 256
|
||||
endpointPeer *string
|
||||
)
|
||||
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
irohErr := err.(*iroh.IrohError)
|
||||
panic(irohErr.Message())
|
||||
}
|
||||
}
|
||||
|
||||
func awaitInterrupt() {
|
||||
done := make(chan os.Signal, 1)
|
||||
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-done
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func parseFlags() {
|
||||
endpointPeer = flag.String("endpoint", "", "endpoint id of peer")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func send(e *iroh.Endpoint, id string) {
|
||||
remote, err := iroh.EndpointIdFromString(id)
|
||||
check(err)
|
||||
|
||||
addr := iroh.NewEndpointAddr(remote, nil, nil)
|
||||
conn, err := e.Connect(addr, alpn)
|
||||
check(err)
|
||||
|
||||
stream, err := conn.OpenBi()
|
||||
check(err)
|
||||
fmt.Println("connected")
|
||||
recv := stream.Recv()
|
||||
done := make(chan struct{})
|
||||
go handleInput(stream, done)
|
||||
go handleReading(recv, done)
|
||||
}
|
||||
|
||||
func listen(e *iroh.Endpoint) {
|
||||
for {
|
||||
incoming := *e.AcceptNext()
|
||||
go handleIncoming(incoming)
|
||||
}
|
||||
}
|
||||
|
||||
func handleIncoming (incoming *iroh.Incoming) {
|
||||
accepting, err := incoming.Accept()
|
||||
check(err)
|
||||
|
||||
conn, err := accepting.Connect()
|
||||
check(err)
|
||||
|
||||
stream, err := conn.AcceptBi()
|
||||
check(err)
|
||||
|
||||
recv := stream.Recv()
|
||||
done := make(chan struct{})
|
||||
go handleInput(stream, done)
|
||||
go handleReading(recv, done)
|
||||
}
|
||||
|
||||
func handleInput (stream *iroh.BiStream, done chan struct{}) {
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
if line := scanner.Text(); line != "" {
|
||||
err := stream.Send().WriteAll([]byte(line))
|
||||
check(err)
|
||||
}
|
||||
|
||||
select {
|
||||
// exit if channel done is closed
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func handleReading(recv *iroh.RecvStream, done chan struct{}) {
|
||||
for {
|
||||
frame, err := recv.Read(frameSize)
|
||||
// handle timeout
|
||||
if err != nil {
|
||||
irohErr := err.(*iroh.IrohError)
|
||||
errMsg := irohErr.Message()
|
||||
if strings.HasPrefix(errMsg, "ConnectionLost") {
|
||||
fmt.Println("lost connection")
|
||||
break
|
||||
} else {
|
||||
check(err)
|
||||
}
|
||||
}
|
||||
fmt.Println(string(frame))
|
||||
}
|
||||
close(done)
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
parseFlags()
|
||||
|
||||
preset := iroh.PresetN0()
|
||||
opts := iroh.EndpointOptions{
|
||||
Preset: &preset,
|
||||
Alpns: &[][]byte{alpn},
|
||||
}
|
||||
|
||||
endpoint, err := iroh.EndpointBind(opts)
|
||||
check(err)
|
||||
endpoint.Online()
|
||||
|
||||
if *endpointPeer == "" {
|
||||
fmt.Println("this endpoint is online and listening! use this id to connect via a peer:")
|
||||
fmt.Printf("pairchat -endpoint %s\n", endpoint.Id())
|
||||
go func() { listen(endpoint) }()
|
||||
} else {
|
||||
go func() { send(endpoint, *endpointPeer) }()
|
||||
}
|
||||
|
||||
awaitInterrupt()
|
||||
}
|
||||
Reference in New Issue
Block a user