package neo
import (
"errors"
"net"
"strconv"
"sync"
"syscall"
"time"
)
type Net struct {
peers map [string ]*PacketConn
}
type packet struct {
buf []byte
addr net .Addr
}
type PacketConn struct {
packets chan packet
addr net .Addr
net *Net
closedMux sync .Mutex
closed bool
mux sync .Mutex
deadline notifier
readDeadline notifier
writeDeadline notifier
}
func addrKey (a net .Addr ) string {
if u , ok := a .(*net .UDPAddr ); ok {
return "udp/" + u .String ()
}
return a .Network () + "/" + a .String ()
}
func (c *PacketConn ) ok () bool {
if c == nil {
return false
}
c .closedMux .Lock ()
defer c .closedMux .Unlock ()
return !c .closed
}
var ErrDeadline = errors .New ("deadline" )
func (c *PacketConn ) ReadFrom (p []byte ) (n int , addr net .Addr , err error ) {
if !c .ok () {
return 0 , nil , syscall .EINVAL
}
c .mux .Lock ()
deadline := c .deadline
readDeadline := c .readDeadline
c .mux .Unlock ()
select {
case pp := <- c .packets :
return copy (p , pp .buf ), pp .addr , nil
case <- readDeadline :
return 0 , nil , ErrDeadline
case <- deadline :
return 0 , nil , ErrDeadline
}
}
func (c *PacketConn ) WriteTo (p []byte , a net .Addr ) (n int , err error ) {
if !c .ok () {
return 0 , syscall .EINVAL
}
c .mux .Lock ()
deadline := c .deadline
writeDeadline := c .writeDeadline
c .mux .Unlock ()
select {
case c .net .peers [addrKey (a )].packets <- packet {
addr : c .addr ,
buf : append ([]byte {}, p ...),
}:
return len (p ), nil
case <- writeDeadline :
return 0 , ErrDeadline
case <- deadline :
return 0 , ErrDeadline
}
}
func (c *PacketConn ) LocalAddr () net .Addr { return c .addr }
func (c *PacketConn ) Close () error {
if !c .ok () {
return syscall .EINVAL
}
c .closedMux .Lock ()
defer c .closedMux .Unlock ()
if c .closed {
return syscall .EINVAL
}
c .closed = true
close (c .packets )
return nil
}
type notifier chan struct {}
func simpleDeadline (t time .Time ) notifier {
deadline := make (notifier )
go func () {
now := time .Now ()
<-time .After (t .Sub (now ))
close (deadline )
}()
return deadline
}
func (c *PacketConn ) SetDeadline (t time .Time ) error {
if !c .ok () {
return syscall .EINVAL
}
c .mux .Lock ()
c .deadline = simpleDeadline (t )
c .mux .Unlock ()
return nil
}
func (c *PacketConn ) SetReadDeadline (t time .Time ) error {
if !c .ok () {
return syscall .EINVAL
}
c .mux .Lock ()
c .readDeadline = simpleDeadline (t )
c .mux .Unlock ()
return nil
}
func (c *PacketConn ) SetWriteDeadline (t time .Time ) error {
if !c .ok () {
return syscall .EINVAL
}
c .mux .Lock ()
c .writeDeadline = simpleDeadline (t )
c .mux .Unlock ()
return nil
}
type NetAddr struct {
Net string
Address string
}
func (n NetAddr ) Network () string { return n .Net }
func (n NetAddr ) String () string { return n .Address }
func (n *Net ) ResolveUDPAddr (network , address string ) (*net .UDPAddr , error ) {
a := &net .UDPAddr {
Port : 0 ,
IP : net .IPv4 (127 , 0 , 0 , 1 ),
}
host , port , err := net .SplitHostPort (address )
if err != nil {
return nil , err
}
if a .IP = net .ParseIP (host ); a .IP == nil {
return nil , errors .New ("bad IP" )
}
if a .Port , err = strconv .Atoi (port ); err != nil {
return nil , err
}
return a , nil
}
func (n *Net ) ListenPacket (network , address string ) (net .PacketConn , error ) {
if network != "udp4" && network != "udp" && network != "udp6" {
return nil , errors .New ("bad net" )
}
a , err := n .ResolveUDPAddr (network , address )
if err != nil {
return nil , err
}
pc := &PacketConn {
net : n ,
addr : a ,
packets : make (chan packet , 10 ),
}
n .peers [addrKey (a )] = pc
return pc , nil
}
type NAT struct {
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .