Skip to content

Instantly share code, notes, and snippets.

@zserge
Created July 21, 2016 07:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zserge/39390a983ba3e108e48f145806451004 to your computer and use it in GitHub Desktop.
Save zserge/39390a983ba3e108e48f145806451004 to your computer and use it in GitHub Desktop.
//
// A simple SCTP echo client to demonstrate wrapping low-level socket file descriptor into a net.Conn.
// Should work fine on linux, darwin, freebsd, dragonfly and solaris (where syscalls are available for SCTP sockets).
//
package main
import (
"errors"
"fmt"
"io"
"log"
"net"
"os"
"sync"
"syscall"
)
// Errors returned by DialSCTP
var ErrBadIPAddr = errors.New("bad IP address")
//
// DialSCTP connects to listening SCTP socket at the given address. Address
// should be in the same format as for net.Dial, because standard TCP address
// resolver is used internally.
//
func DialSCTP(addr string) (net.Conn, error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
}
var raddr syscall.Sockaddr
if ip6 := tcpAddr.IP.To16(); ip6 != nil {
addr6 := &syscall.SockaddrInet6{Port: tcpAddr.Port}
copy(addr6.Addr[:], ip6[:16])
raddr = addr6
} else if ip4 := tcpAddr.IP.To4(); ip4 != nil {
addr4 := &syscall.SockaddrInet4{Port: tcpAddr.Port}
copy(addr4.Addr[:], ip4[:4])
raddr = addr4
} else {
return nil, ErrBadIPAddr
}
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_SCTP)
if err != nil {
return nil, err
}
err = syscall.Connect(fd, raddr)
if err != nil {
syscall.Close(fd)
log.Println(err)
}
f := os.NewFile(uintptr(fd), fmt.Sprintf("socket fd %d", fd))
return net.FileConn(f)
}
func main() {
if len(os.Args) < 3 {
fmt.Printf("USAGE: %s <address> <message>\n", os.Args[0])
os.Exit(0)
}
conn, err := DialSCTP(os.Args[1])
if err != nil {
log.Fatal("dial failed: ", err)
}
defer conn.Close()
log.Println("connected to", conn.RemoteAddr(), "from", conn.LocalAddr())
wg := &sync.WaitGroup{}
wg.Add(1)
// To terminte reader correctly we need to count all bytes sent and received
messages := os.Args[2:]
total := 0
for _, msg := range messages {
total = total + len(msg)
}
go func(count int) {
defer wg.Done()
buf := make([]byte, 1024)
for count > 0 {
if n, err := conn.Read(buf); err != nil {
if err != io.EOF {
log.Println("read error", err)
}
return
} else {
log.Println("read", string(buf[:n]))
count = count - n
}
}
}(total)
for _, msg := range os.Args[2:] {
n, err := conn.Write([]byte(msg))
if err != nil {
log.Println("write error", err)
conn.Close() // This will break the reader loop
break
} else {
log.Println("written", n, "bytes")
}
}
wg.Wait()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment