first commit

This commit is contained in:
harshabose
2025-02-18 20:48:19 +05:30
commit f8d4c826c6
8 changed files with 342 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/third_party
.idea

27
go.mod Normal file
View File

@@ -0,0 +1,27 @@
module datachannel
go 1.23
require (
github.com/google/uuid v1.6.0 // indirect
github.com/pion/datachannel v1.5.10 // indirect
github.com/pion/dtls/v3 v3.0.4 // indirect
github.com/pion/ice/v4 v4.0.6 // indirect
github.com/pion/interceptor v0.1.37 // indirect
github.com/pion/logging v0.2.3 // indirect
github.com/pion/mdns/v2 v2.0.7 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.15 // indirect
github.com/pion/rtp v1.8.11 // indirect
github.com/pion/sctp v1.8.35 // indirect
github.com/pion/sdp/v3 v3.0.10 // indirect
github.com/pion/srtp/v3 v3.0.4 // indirect
github.com/pion/stun/v3 v3.0.0 // indirect
github.com/pion/transport/v3 v3.0.7 // indirect
github.com/pion/turn/v4 v4.0.0 // indirect
github.com/pion/webrtc/v4 v4.0.10 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
)

42
go.sum Normal file
View File

@@ -0,0 +1,42 @@
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=
github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg=
github.com/pion/ice/v4 v4.0.6 h1:jmM9HwI9lfetQV/39uD0nY4y++XZNPhvzIPCb8EwxUM=
github.com/pion/ice/v4 v4.0.6/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
github.com/pion/rtp v1.8.11 h1:17xjnY5WO5hgO6SD3/NTIUPvSFw/PbLsIJyz1r1yNIk=
github.com/pion/rtp v1.8.11/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4=
github.com/pion/sctp v1.8.35 h1:qwtKvNK1Wc5tHMIYgTDJhfZk7vATGVHhXbUDfHbYwzA=
github.com/pion/sctp v1.8.35/go.mod h1:EcXP8zCYVTRy3W9xtOF7wJm1L1aXfKRQzaM33SjQlzg=
github.com/pion/sdp/v3 v3.0.10 h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA=
github.com/pion/sdp/v3 v3.0.10/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
github.com/pion/webrtc/v4 v4.0.10 h1:Hq/JLjhqLxi+NmCtE8lnRPDr8H4LcNvwg8OxVcdv56Q=
github.com/pion/webrtc/v4 v4.0.10/go.mod h1:ViHLVaNpiuvaH8pdiuQxuA9awuE6KVzAXx3vVWilOck=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

21
main.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"fmt"
)
// TIP <p>To run your code, right-click the code and select <b>Run</b>.</p> <p>Alternatively, click
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.</p>
func main() {
// TIP <p>Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined text
// to see how GoLand suggests fixing the warning.</p><p>Alternatively, if available, click the lightbulb to view possible fixes.</p>
s := "gopher"
fmt.Printf("Hello and welcome, %s!\n", s)
for i := 1; i <= 5; i++ {
// TIP <p>To start your debugging session, right-click your code in the editor and select the Debug option.</p> <p>We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.</p>
fmt.Println("i =", 100/i)
}
}

6
pkg/constants.go Normal file
View File

@@ -0,0 +1,6 @@
package pkg
const (
DefaultBindPort = 14551
DefaultLoopBackPort = 14550
)

85
pkg/datachannel.go Normal file
View File

@@ -0,0 +1,85 @@
package pkg
import (
"context"
"fmt"
"github.com/pion/webrtc/v4"
)
type DataChannels map[string]*DataChannel
type DataChannel struct {
datachannel *webrtc.DataChannel
loopback *LoopBack
ctx context.Context
}
func CreateDataChannel(ctx context.Context, label string, peerConnection *webrtc.PeerConnection, loopback *LoopBack) (*DataChannel, error) {
var (
datachannel *webrtc.DataChannel
dataChannelNegotiated = true
dataChannelProtocol = "binary"
dataChannelOrdered = true
dataChannelID = uint16(1) // Add explicit ID
dataChannelInit = webrtc.DataChannelInit{
Negotiated: &dataChannelNegotiated,
Protocol: &dataChannelProtocol,
Ordered: &dataChannelOrdered,
ID: &dataChannelID,
}
err error
)
if datachannel, err = peerConnection.CreateDataChannel(label, &dataChannelInit); err != nil {
return nil, err
}
if err = loopback.AttachDataChannel(datachannel); err != nil {
return nil, err
}
return &DataChannel{
datachannel: datachannel,
loopback: loopback,
ctx: ctx,
}, nil
}
func (dataChannel *DataChannel) Label() string {
return dataChannel.datachannel.Label()
}
func (dataChannel *DataChannel) Send(message []byte) error {
return dataChannel.datachannel.Send(message)
}
func (dataChannel *DataChannel) Close() (err error) {
if err = dataChannel.datachannel.Close(); err == nil {
return nil
}
return err
}
func (dataChannel *DataChannel) onOpen() *DataChannel {
dataChannel.datachannel.OnOpen(func() {
fmt.Printf("dataChannel Open with Label: %s\n", dataChannel.datachannel.Label())
})
return dataChannel
}
func (dataChannel *DataChannel) onClose() *DataChannel {
dataChannel.datachannel.OnClose(func() {
fmt.Printf("dataChannel Closed with Label: %s\n", dataChannel.datachannel.Label())
})
return dataChannel
}
func (dataChannel *DataChannel) onMessage() *DataChannel {
dataChannel.datachannel.OnMessage(func(message webrtc.DataChannelMessage) {
if err := dataChannel.loopback.Send(message.Data); err != nil {
fmt.Println("Error sending data: " + err.Error())
}
})
return dataChannel
}

137
pkg/loopback.go Normal file
View File

@@ -0,0 +1,137 @@
package pkg
import (
"context"
"errors"
"fmt"
"net"
"time"
"github.com/pion/webrtc/v4"
)
type LoopBack struct {
dataChannel *webrtc.DataChannel
udpListener *net.UDPConn
loopBackPort *net.UDPAddr
ctx context.Context
}
func CreateLoopBack(ctx context.Context, options ...LoopBackOption) (*LoopBack, error) {
loopBack := &LoopBack{
ctx: ctx,
}
for _, option := range options {
if err := option(loopBack); err != nil {
return nil, err
}
}
return loopBack, nil
}
func (loopback *LoopBack) AttachDataChannel(datachannel *webrtc.DataChannel) error {
if loopback.dataChannel == nil {
loopback.dataChannel = datachannel
return nil
}
return errors.New("datachannel already attached")
}
func (loopback *LoopBack) Start() {
go loopback.loop()
}
func (loopback *LoopBack) loop() {
var (
buffer []byte
nRead = 0
)
defer loopback.Close()
for {
select {
case <-loopback.ctx.Done():
return
default:
if loopback.udpListener == nil {
fmt.Println("Bind port not yet detected. Sleeping for 0.1 second...")
time.Sleep(100 * time.Millisecond)
continue
}
if buffer, nRead = loopback.readMessageFromUDPPort(); nRead > 0 && nRead < 1025 {
loopback.sendMessageThroughDataChannel(buffer[:nRead])
}
}
}
}
func (loopback *LoopBack) Close() {
if loopback.udpListener != nil {
loopback.udpListener.Close()
}
}
func (loopback *LoopBack) Send(message []byte) error {
var (
bytesWritten = 0
err error = nil
)
if loopback.udpListener == nil {
return fmt.Errorf("bind port not yet set. Skipping message")
}
if loopback.loopBackPort == nil {
return fmt.Errorf("loopback port not yet discovered. Skipping message")
}
if bytesWritten, err = loopback.udpListener.WriteToUDP(message, loopback.loopBackPort); err == nil {
if bytesWritten != len(message) {
err = fmt.Errorf("written bytes (%d) != message length (%d)", bytesWritten, len(message))
}
}
return err
}
func (loopback *LoopBack) readMessageFromUDPPort() ([]byte, int) {
var (
buffer []byte = make([]byte, 1024)
nRead = 0
senderAddr *net.UDPAddr = nil
err error = nil
)
if nRead, senderAddr, err = loopback.udpListener.ReadFromUDP(buffer); err != nil {
fmt.Println("Error while reading message from bind port" + err.Error())
return nil, 0
}
if loopback.loopBackPort == nil {
loopback.loopBackPort = &net.UDPAddr{IP: senderAddr.IP, Port: senderAddr.Port}
fmt.Println("Found sender port to bind port")
}
if senderAddr != nil && senderAddr.Port != loopback.loopBackPort.Port {
fmt.Println(fmt.Sprintf("expected port %d but got %d", loopback.loopBackPort.Port, senderAddr.Port))
}
return buffer, nRead
}
func (loopback *LoopBack) sendMessageThroughDataChannel(message []byte) {
var err error = nil
if loopback.dataChannel == nil {
fmt.Println("datachannel not yet set")
return
}
if loopback.dataChannel.ReadyState() == webrtc.DataChannelStateOpen {
if err = loopback.dataChannel.Send(message); err != nil {
fmt.Println("failed to send data: " + err.Error())
}
return
}
}

22
pkg/loopback_option.go Normal file
View File

@@ -0,0 +1,22 @@
package pkg
import "net"
type LoopBackOption = func(*LoopBack) error
func WithBindPort(loopback *LoopBack) error {
var err error
if loopback.udpListener, err = net.ListenUDP("udp4", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: DefaultBindPort}); err != nil {
return err
}
return nil
}
func WithLoopBackPort(loopback *LoopBack) error {
loopback.loopBackPort = &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: DefaultLoopBackPort}
return nil
}
func WithMAVP2P(loopback *LoopBack) error {
return nil
}