Created
July 21, 2016 07:56
-
-
Save zserge/39390a983ba3e108e48f145806451004 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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