Commit c83b8386 authored by Russ Cox's avatar Russ Cox

package net cleanup

added ReadFrom/WriteTo for packet protocols like UDP.
simplified the net.Conn interface.
added new net.PacketConn interface for packet protocols.
implemented proper UDP listener.

cleaned up LocalAddr/RemoteAddr methods - cache in netFD.

threw away various unused methods.

an interface change:
introduced net.Addr as a network address interface,
to avoid conversion of UDP host:port to string and
back for every ReadFrom/WriteTo sequence.

another interface change:
since signature of Listener.Accept was changing anyway,
dropped the middle return value, because it is available
as c.RemoteAddr().  (the Accept signature predates the
existence of that method.)

Dial and Listen still accept strings, but the proto-specific
versions DialTCP, ListenUDP, etc. take net.Addr instead.

because the generic Dial didn't change and because
no one calls Accept directly (only indirectly via the http
server), very little code will be affected by these interface
changes.

design comments welcome.

R=p
CC=go-dev, r
http://go/go-review/1018017
parent 6e8184d8
...@@ -56,9 +56,11 @@ type Conn struct { ...@@ -56,9 +56,11 @@ type Conn struct {
} }
// Create new connection from rwc. // Create new connection from rwc.
func newConn(rwc io.ReadWriteCloser, raddr string, handler Handler) (c *Conn, err os.Error) { func newConn(rwc net.Conn, handler Handler) (c *Conn, err os.Error) {
c = new(Conn); c = new(Conn);
c.RemoteAddr = raddr; if a := rwc.RemoteAddr(); a != nil {
c.RemoteAddr = a.String();
}
c.handler = handler; c.handler = handler;
c.rwc = rwc; c.rwc = rwc;
br := bufio.NewReader(rwc); br := bufio.NewReader(rwc);
...@@ -527,11 +529,11 @@ func Serve(l net.Listener, handler Handler) os.Error { ...@@ -527,11 +529,11 @@ func Serve(l net.Listener, handler Handler) os.Error {
handler = DefaultServeMux; handler = DefaultServeMux;
} }
for { for {
rw, raddr, e := l.Accept(); rw, e := l.Accept();
if e != nil { if e != nil {
return e; return e;
} }
c, err := newConn(rw, raddr, handler); c, err := newConn(rw, handler);
if err != nil { if err != nil {
continue; continue;
} }
......
...@@ -17,6 +17,8 @@ GOFILES=\ ...@@ -17,6 +17,8 @@ GOFILES=\
parse.go\ parse.go\
port.go\ port.go\
sock.go\ sock.go\
tcpsock.go\
udpsock.go\
unixsock.go\ unixsock.go\
include $(GOROOT)/src/Make.pkg include $(GOROOT)/src/Make.pkg
...@@ -40,16 +40,6 @@ func doDial(t *testing.T, network, addr string) { ...@@ -40,16 +40,6 @@ func doDial(t *testing.T, network, addr string) {
fd.Close() fd.Close()
} }
func doDialTCP(t *testing.T, network, addr string) {
fd, err := DialTCP(network, "", addr);
if err != nil {
t.Errorf("DialTCP(%q, %q, %q) = _, %v", network, "", addr, err);
} else {
fetchGoogle(t, fd, network, addr);
}
fd.Close()
}
var googleaddrs = []string { var googleaddrs = []string {
"74.125.19.99:80", "74.125.19.99:80",
"www.google.com:80", "www.google.com:80",
...@@ -77,22 +67,18 @@ func TestDialGoogle(t *testing.T) { ...@@ -77,22 +67,18 @@ func TestDialGoogle(t *testing.T) {
} }
t.Logf("-- %s --", addr); t.Logf("-- %s --", addr);
doDial(t, "tcp", addr); doDial(t, "tcp", addr);
doDialTCP(t, "tcp", addr);
if addr[0] != '[' { if addr[0] != '[' {
doDial(t, "tcp4", addr); doDial(t, "tcp4", addr);
doDialTCP(t, "tcp4", addr);
if !preferIPv4 { if !preferIPv4 {
// make sure preferIPv4 flag works. // make sure preferIPv4 flag works.
preferIPv4 = true; preferIPv4 = true;
syscall.SocketDisableIPv6 = true; syscall.SocketDisableIPv6 = true;
doDial(t, "tcp4", addr); doDial(t, "tcp4", addr);
doDialTCP(t, "tcp4", addr);
syscall.SocketDisableIPv6 = false; syscall.SocketDisableIPv6 = false;
preferIPv4 = false; preferIPv4 = false;
} }
} }
doDial(t, "tcp6", addr); doDial(t, "tcp6", addr);
doDialTCP(t, "tcp6", addr)
} }
} }
...@@ -78,7 +78,11 @@ func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error ...@@ -78,7 +78,11 @@ func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error
} }
return in, nil return in, nil
} }
return nil, &DNSError{"no answer from server", name, c.RemoteAddr()} var server string;
if a := c.RemoteAddr(); a != nil {
server = a.String();
}
return nil, &DNSError{"no answer from server", name, server}
} }
......
...@@ -17,12 +17,14 @@ import ( ...@@ -17,12 +17,14 @@ import (
type netFD struct { type netFD struct {
// immutable until Close // immutable until Close
fd int; fd int;
family int;
proto int;
file *os.File; file *os.File;
cr chan *netFD; cr chan *netFD;
cw chan *netFD; cw chan *netFD;
net string; net string;
laddr string; laddr Addr;
raddr string; raddr Addr;
// owned by client // owned by client
rdeadline_delta int64; rdeadline_delta int64;
...@@ -289,7 +291,7 @@ func (s *pollServer) WaitWrite(fd *netFD) { ...@@ -289,7 +291,7 @@ func (s *pollServer) WaitWrite(fd *netFD) {
var pollserver *pollServer var pollserver *pollServer
func _StartServer() { func startServer() {
p, err := newPollServer(); p, err := newPollServer();
if err != nil { if err != nil {
print("Start pollServer: ", err.String(), "\n") print("Start pollServer: ", err.String(), "\n")
...@@ -297,19 +299,27 @@ func _StartServer() { ...@@ -297,19 +299,27 @@ func _StartServer() {
pollserver = p pollserver = p
} }
func newFD(fd int, net, laddr, raddr string) (f *netFD, err os.Error) { func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
if pollserver == nil { once.Do(startServer);
once.Do(_StartServer);
}
if e := syscall.SetNonblock(fd, true); e != 0 { if e := syscall.SetNonblock(fd, true); e != 0 {
return nil, &os.PathError{"setnonblock", laddr, os.Errno(e)}; return nil, &OpError{"setnonblock", net, laddr, os.Errno(e)};
} }
f = new(netFD); f = &netFD{
f.fd = fd; fd: fd,
f.net = net; family: family,
f.laddr = laddr; proto: proto,
f.raddr = raddr; net: net,
f.file = os.NewFile(fd, net + "!" + laddr + "->" + raddr); laddr: laddr,
raddr: raddr,
};
var ls, rs string;
if laddr != nil {
ls = laddr.String();
}
if raddr != nil {
rs = raddr.String();
}
f.file = os.NewFile(fd, net + ":" + ls + "->" + rs);
f.cr = make(chan *netFD, 1); f.cr = make(chan *netFD, 1);
f.cw = make(chan *netFD, 1); f.cw = make(chan *netFD, 1);
return f, nil return f, nil
...@@ -322,24 +332,6 @@ func isEAGAIN(e os.Error) bool { ...@@ -322,24 +332,6 @@ func isEAGAIN(e os.Error) bool {
return e == os.EAGAIN; return e == os.EAGAIN;
} }
func (fd *netFD) addr() string {
sa, e := syscall.Getsockname(fd.fd);
if e != 0 {
return "";
}
addr, _ := sockaddrToString(sa);
return addr;
}
func (fd *netFD) remoteAddr() string {
sa, e := syscall.Getpeername(fd.fd);
if e != 0 {
return "";
}
addr, _ := sockaddrToString(sa);
return addr;
}
func (fd *netFD) Close() os.Error { func (fd *netFD) Close() os.Error {
if fd == nil || fd.file == nil { if fd == nil || fd.file == nil {
return os.EINVAL return os.EINVAL
...@@ -413,7 +405,7 @@ func (fd *netFD) Write(p []byte) (n int, err os.Error) { ...@@ -413,7 +405,7 @@ func (fd *netFD) Write(p []byte) (n int, err os.Error) {
return nn, err return nn, err
} }
func (fd *netFD) accept() (nfd *netFD, err os.Error) { func (fd *netFD) accept(toAddr func(syscall.Sockaddr)Addr) (nfd *netFD, err os.Error) {
if fd == nil || fd.file == nil { if fd == nil || fd.file == nil {
return nil, os.EINVAL return nil, os.EINVAL
} }
...@@ -435,16 +427,12 @@ func (fd *netFD) accept() (nfd *netFD, err os.Error) { ...@@ -435,16 +427,12 @@ func (fd *netFD) accept() (nfd *netFD, err os.Error) {
} }
if e != 0 { if e != 0 {
syscall.ForkLock.RUnlock(); syscall.ForkLock.RUnlock();
return nil, &os.PathError{"accept", fd.addr(), os.Errno(e)} return nil, &OpError{"accept", fd.net, fd.laddr, os.Errno(e)}
} }
syscall.CloseOnExec(s); syscall.CloseOnExec(s);
syscall.ForkLock.RUnlock(); syscall.ForkLock.RUnlock();
raddr, err1 := sockaddrToString(sa); if nfd, err = newFD(s, fd.family, fd.proto, fd.net, fd.laddr, toAddr(sa)); err != nil {
if err1 != nil {
raddr = "invalid-address";
}
if nfd, err = newFD(s, fd.net, fd.laddr, raddr); err != nil {
syscall.Close(s); syscall.Close(s);
return nil, err return nil, err
} }
......
...@@ -180,6 +180,10 @@ func itox(i uint) string { ...@@ -180,6 +180,10 @@ func itox(i uint) string {
func (ip IP) String() string { func (ip IP) String() string {
p := ip; p := ip;
if len(ip) == 0 {
return "";
}
// If IPv4, use dotted notation. // If IPv4, use dotted notation.
if p4 := p.To4(); len(p4) == 4 { if p4 := p.To4(); len(p4) == 4 {
return itod(uint(p4[0]))+"." return itod(uint(p4[0]))+"."
......
...@@ -34,125 +34,47 @@ func listenBacklog() int { ...@@ -34,125 +34,47 @@ func listenBacklog() int {
return syscall.SOMAXCONN return syscall.SOMAXCONN
} }
// ListenerTCP is a TCP network listener.
// Clients should typically use variables of type Listener
// instead of assuming TCP.
type ListenerTCP struct {
fd *netFD;
}
// ListenTCP announces on the TCP address laddr and returns a TCP listener.
// Net must be "tcp", "tcp4", or "tcp6".
// If laddr has a port of 0, it means to listen on some available port.
// The caller can use l.Addr() to retrieve the chosen address.
func ListenTCP(net, laddr string) (l *ListenerTCP, err os.Error) {
fd, e := internetSocket(net, laddr, "", syscall.SOCK_STREAM, "listen");
if e != nil {
return nil, e
}
e1 := syscall.Listen(fd.fd, listenBacklog());
if e1 != 0 {
syscall.Close(fd.fd);
return nil, &OpError{"listen", "tcp", laddr, os.Errno(e1)};
}
l = new(ListenerTCP);
l.fd = fd;
return l, nil
}
// AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address.
func (l *ListenerTCP) AcceptTCP() (c *ConnTCP, raddr string, err os.Error) {
if l == nil || l.fd == nil || l.fd.fd < 0 {
return nil, "", os.EINVAL
}
fd, e := l.fd.accept();
if e != nil {
return nil, "", e
}
return newConnTCP(fd, fd.raddr), fd.raddr, nil
}
// Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn.
func (l *ListenerTCP) Accept() (c Conn, raddr string, err os.Error) {
c1, r1, e1 := l.AcceptTCP();
if e1 != nil {
return nil, "", e1
}
return c1, r1, nil
}
// Close stops listening on the TCP address.
// Already Accepted connections are not closed.
func (l *ListenerTCP) Close() os.Error {
if l == nil || l.fd == nil {
return os.EINVAL
}
return l.fd.Close()
}
// Addr returns the listener's network address.
func (l *ListenerTCP) Addr() string {
return l.fd.addr();
}
// Internet sockets (TCP, UDP) // Internet sockets (TCP, UDP)
func internetSocket(net, laddr, raddr string, proto int, mode string) (fd *netFD, err os.Error) { // A sockaddr represents a TCP or UDP network address that can
// Parse addresses (unless they are empty). // be converted into a syscall.Sockaddr.
var lip, rip IP; type sockaddr interface {
var lport, rport int; Addr;
sockaddr(family int) (syscall.Sockaddr, os.Error);
if laddr != "" { family() int;
if lip, lport, err = hostPortToIP(net, laddr, mode); err != nil { }
goto Error;
}
}
if raddr != "" {
if rip, rport, err = hostPortToIP(net, raddr, mode); err != nil {
goto Error;
}
}
func internetSocket(net string, laddr, raddr sockaddr, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) {
// Figure out IP version. // Figure out IP version.
// If network has a suffix like "tcp4", obey it. // If network has a suffix like "tcp4", obey it.
vers := 0; family := syscall.AF_INET6;
switch net[len(net)-1] { switch net[len(net)-1] {
case '4': case '4':
vers = 4; family = syscall.AF_INET
case '6': case '6':
vers = 6; // nothing to do
default: default:
// Otherwise, guess. // Otherwise, guess.
// If the addresses are IPv4 and we prefer IPv4, use 4; else 6. // If the addresses are IPv4 and we prefer IPv4, use 4; else 6.
if preferIPv4 && (lip == nil || lip.To4() != nil) && (rip == nil || rip.To4() != nil) { if preferIPv4 &&
vers = 4 (laddr == nil || laddr.family() == syscall.AF_INET) &&
} else { (raddr == nil || raddr.family() == syscall.AF_INET) {
vers = 6 family = syscall.AF_INET;
} }
} }
var family int;
if vers == 4 {
family = syscall.AF_INET
} else {
family = syscall.AF_INET6
}
var la, ra syscall.Sockaddr; var la, ra syscall.Sockaddr;
if lip != nil { if laddr != nil {
if la, err = ipToSockaddr(family, lip, lport); err != nil { if la, err = laddr.sockaddr(family); err != nil {
goto Error; goto Error;
} }
} }
if rip != nil { if raddr != nil {
if ra, err = ipToSockaddr(family, rip, rport); err != nil { if ra, err = raddr.sockaddr(family); err != nil {
goto Error; goto Error;
} }
} }
fd, err = socket(net, family, proto, 0, la, ra, toAddr);
fd, err = socket(net, laddr, raddr, family, proto, 0, la, ra);
if err != nil { if err != nil {
goto Error; goto Error;
} }
...@@ -166,77 +88,31 @@ Error: ...@@ -166,77 +88,31 @@ Error:
return nil, &OpError{mode, net, addr, err}; return nil, &OpError{mode, net, addr, err};
} }
func getip(fd int, remote bool) (ip []byte, port int, ok bool) {
// TCP connections. // No attempt at error reporting because
// there are no possible errors, and the
// ConnTCP is an implementation of the Conn interface // caller won't report them anyway.
// for TCP network connections. var sa syscall.Sockaddr;
type ConnTCP struct { if remote {
connBase sa, _ = syscall.Getpeername(fd);
} } else {
sa, _ = syscall.Getsockname(fd);
func (c *ConnTCP) SetNoDelay(nodelay bool) os.Error {
if c == nil {
return os.EINVAL
}
return setsockoptInt(c.sysFD(), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(nodelay))
}
func newConnTCP(fd *netFD, raddr string) *ConnTCP {
c := new(ConnTCP);
c.fd = fd;
c.raddr = raddr;
c.SetNoDelay(true);
return c
}
// DialTCP is like Dial but can only connect to TCP networks
// and returns a ConnTCP structure.
func DialTCP(net, laddr, raddr string) (c *ConnTCP, err os.Error) {
if raddr == "" {
return nil, &OpError{"dial", "tcp", "", errMissingAddress}
}
fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_STREAM, "dial");
if e != nil {
return nil, e
}
return newConnTCP(fd, raddr), nil
}
// UDP connections.
// TODO(rsc): UDP headers mode
// ConnUDP is an implementation of the Conn interface
// for UDP network connections.
type ConnUDP struct {
connBase
}
func newConnUDP(fd *netFD, raddr string) *ConnUDP {
c := new(ConnUDP);
c.fd = fd;
c.raddr = raddr;
return c
}
// DialUDP is like Dial but can only connect to UDP networks
// and returns a ConnUDP structure.
func DialUDP(net, laddr, raddr string) (c *ConnUDP, err os.Error) {
if raddr == "" {
return nil, &OpError{"dial", "udp", "", errMissingAddress}
} }
fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_DGRAM, "dial"); switch sa := sa.(type) {
if e != nil { case *syscall.SockaddrInet4:
return nil, e return &sa.Addr, sa.Port, true;
case *syscall.SockaddrInet6:
return &sa.Addr, sa.Port, true;
} }
return newConnUDP(fd, raddr), nil return;
} }
func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
switch family { switch family {
case syscall.AF_INET: case syscall.AF_INET:
if len(ip) == 0 {
ip = IPv4zero;
}
if ip = ip.To4(); ip == nil { if ip = ip.To4(); ip == nil {
return nil, os.EINVAL return nil, os.EINVAL
} }
...@@ -247,6 +123,9 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { ...@@ -247,6 +123,9 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
s.Port = port; s.Port = port;
return s, nil; return s, nil;
case syscall.AF_INET6: case syscall.AF_INET6:
if len(ip) == 0 {
ip = IPzero;
}
// IPv4 callers use 0.0.0.0 to mean "announce on any available address". // IPv4 callers use 0.0.0.0 to mean "announce on any available address".
// In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0", // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
// which it refuses to do. Rewrite to the IPv6 all zeros. // which it refuses to do. Rewrite to the IPv6 all zeros.
...@@ -302,43 +181,29 @@ func joinHostPort(host, port string) string { ...@@ -302,43 +181,29 @@ func joinHostPort(host, port string) string {
} }
// Convert "host:port" into IP address and port. // Convert "host:port" into IP address and port.
// For now, host and port must be numeric literals. func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
// Eventually, we'll have name resolution.
func hostPortToIP(net, hostport, mode string) (ip IP, iport int, err os.Error) {
host, port, err := splitHostPort(hostport); host, port, err := splitHostPort(hostport);
if err != nil { if err != nil {
goto Error; goto Error;
} }
var addr IP; var addr IP;
if host == "" { if host != "" {
if mode != "listen" { // Try as an IP address.
err = &AddrError{"no host in address", hostport};
goto Error;
}
if preferIPv4 {
addr = IPv4zero;
} else {
addr = IPzero; // wildcard - listen to all
}
}
// Try as an IP address.
if addr == nil {
addr = ParseIP(host); addr = ParseIP(host);
}
if addr == nil {
// Not an IP address. Try as a DNS name.
_, addrs, err1 := LookupHost(host);
if err1 != nil {
err = err1;
goto Error;
}
addr = ParseIP(addrs[0]);
if addr == nil { if addr == nil {
// should not happen // Not an IP address. Try as a DNS name.
err = &AddrError{"LookupHost returned invalid address", addrs[0]}; _, addrs, err1 := LookupHost(host);
goto Error; if err1 != nil {
err = err1;
goto Error;
}
addr = ParseIP(addrs[0]);
if addr == nil {
// should not happen
err = &AddrError{"LookupHost returned invalid address", addrs[0]};
goto Error;
}
} }
} }
......
...@@ -10,43 +10,71 @@ package net ...@@ -10,43 +10,71 @@ package net
import "os" import "os"
// Conn is a generic network connection. // Addr represents a network end point address.
type Addr interface {
Network() string; // name of the network
String() string; // string form of address
}
// Conn is a generic stream-oriented network connection.
type Conn interface { type Conn interface {
// Read blocks until data is ready from the connection // Read reads data from the connection.
// and then reads into b. It returns the number // Read can be made to time out and return err == os.EAGAIN
// of bytes read, or 0 if the connection has been closed. // after a fixed time limit; see SetTimeout and SetReadTimeout.
Read(b []byte) (n int, err os.Error); Read(b []byte) (n int, err os.Error);
// Write writes the data in b to the connection. // Write writes data to the connection.
// Write can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
Write(b []byte) (n int, err os.Error); Write(b []byte) (n int, err os.Error);
// Close closes the connection. // Close closes the connection.
Close() os.Error; Close() os.Error;
// LocalAddr returns the local network address. // LocalAddr returns the local network address.
LocalAddr() string; LocalAddr() Addr;
// RemoteAddr returns the remote network address. // RemoteAddr returns the remote network address.
RemoteAddr() string; RemoteAddr() Addr;
// SetTimeout sets the read and write deadlines associated
// with the connection.
SetTimeout(nsec int64) os.Error;
// SetReadTimeout sets the time (in nanoseconds) that
// Read will wait for data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
SetReadTimeout(nsec int64) os.Error;
// SetWriteTimeout sets the time (in nanoseconds) that
// Write will wait to send its data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
SetWriteTimeout(nsec int64) os.Error;
}
// For packet-based protocols such as UDP, // PacketConn is a generic packet-oriented network connection.
// ReadFrom reads the next packet from the network, type PacketConn interface {
// returning the number of bytes read and the remote // ReadFrom reads a packet from the connection,
// address that sent them. // copying the payload into b. It returns the number of
ReadFrom(b []byte) (n int, addr string, err os.Error); // bytes copied into b and the return address that
// was on the packet.
// ReadFrom can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
ReadFrom(b []byte) (n int, addr Addr, err os.Error);
// For packet-based protocols such as UDP, // WriteTo writes a packet with payload b to addr.
// WriteTo writes the byte buffer b to the network // WriteTo can be made to time out and return err == os.EAGAIN
// as a single payload, sending it to the target address. // after a fixed time limit; see SetTimeout and SetWriteTimeout.
WriteTo(addr string, b []byte) (n int, err os.Error); // On packet-oriented connections, write timeouts are rare.
WriteTo(b []byte, addr Addr) (n int, err os.Error);
// SetReadBuffer sets the size of the operating system's // Close closes the connection.
// receive buffer associated with the connection. Close() os.Error;
SetReadBuffer(bytes int) os.Error;
// SetReadBuffer sets the size of the operating system's // LocalAddr returns the local network address.
// transmit buffer associated with the connection. LocalAddr() Addr;
SetWriteBuffer(bytes int) os.Error;
// SetTimeout sets the read and write deadlines associated // SetTimeout sets the read and write deadlines associated
// with the connection. // with the connection.
...@@ -63,42 +91,14 @@ type Conn interface { ...@@ -63,42 +91,14 @@ type Conn interface {
// Even if write times out, it may return n > 0, indicating that // Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written. // some of the data was successfully written.
SetWriteTimeout(nsec int64) os.Error; SetWriteTimeout(nsec int64) os.Error;
// SetLinger sets the behavior of Close() on a connection
// which still has data waiting to be sent or to be acknowledged.
//
// If sec < 0 (the default), Close returns immediately and
// the operating system finishes sending the data in the background.
//
// If sec == 0, Close returns immediately and the operating system
// discards any unsent or unacknowledged data.
//
// If sec > 0, Close blocks for at most sec seconds waiting for
// data to be sent and acknowledged.
SetLinger(sec int) os.Error;
// SetReuseAddr sets whether it is okay to reuse addresses
// from recent connections that were not properly closed.
SetReuseAddr(reuseaddr bool) os.Error;
// SetDontRoute sets whether outgoing messages should
// bypass the system routing tables.
SetDontRoute(dontroute bool) os.Error;
// SetKeepAlive sets whether the operating system should send
// keepalive messages on the connection.
SetKeepAlive(keepalive bool) os.Error;
// BindToDevice binds a connection to a particular network device.
BindToDevice(dev string) os.Error;
} }
// A Listener is a generic network listener. // A Listener is a generic network listener for stream-oriented protocols.
// Accept waits for the next connection and Close closes the connection. // Accept waits for the next connection and Close closes the connection.
type Listener interface { type Listener interface {
Accept() (c Conn, raddr string, err os.Error); Accept() (c Conn, err os.Error);
Close() os.Error; Close() os.Error;
Addr() string; // Listener's network address Addr() Addr; // Listener's network address
} }
// Dial connects to the remote address raddr on the network net. // Dial connects to the remote address raddr on the network net.
...@@ -116,42 +116,116 @@ type Listener interface { ...@@ -116,42 +116,116 @@ type Listener interface {
// Dial("tcp", "", "google.com:80") // Dial("tcp", "", "google.com:80")
// Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80") // Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80")
// Dial("tcp", "127.0.0.1:123", "127.0.0.1:88") // Dial("tcp", "127.0.0.1:123", "127.0.0.1:88")
//
func Dial(net, laddr, raddr string) (c Conn, err os.Error) { func Dial(net, laddr, raddr string) (c Conn, err os.Error) {
switch net { switch net {
case "tcp", "tcp4", "tcp6": case "tcp", "tcp4", "tcp6":
c, err := DialTCP(net, laddr, raddr); var la, ra *TCPAddr;
if err != nil { if laddr != "" {
return nil, err if la, err = ResolveTCPAddr(laddr); err != nil {
goto Error;
}
} }
return c, nil; if raddr != "" {
if ra, err = ResolveTCPAddr(raddr); err != nil {
goto Error;
}
}
return DialTCP(net, la, ra);
case "udp", "udp4", "upd6": case "udp", "udp4", "upd6":
c, err := DialUDP(net, laddr, raddr); var la, ra *UDPAddr;
return c, err; if laddr != "" {
case "unix", "unix-dgram": if la, err = ResolveUDPAddr(laddr); err != nil {
c, err := DialUnix(net, laddr, raddr); goto Error;
return c, err; }
}
if raddr != "" {
if ra, err = ResolveUDPAddr(raddr); err != nil {
goto Error;
}
}
return DialUDP(net, la, ra);
case "unix", "unixgram":
var la, ra *UnixAddr;
if raddr != "" {
if ra, err = ResolveUnixAddr(net, raddr); err != nil {
goto Error;
}
}
if laddr != "" {
if la, err = ResolveUnixAddr(net, laddr); err != nil {
goto Error;
}
}
return DialUnix(net, la, ra);
} }
return nil, &OpError{"dial", net, raddr, UnknownNetworkError(net)}; err = UnknownNetworkError(net);
Error:
return nil, &OpError{"dial", net+" "+raddr, nil, err};
} }
// Listen announces on the local network address laddr. // Listen announces on the local network address laddr.
// The network string net must be "tcp", "tcp4", "tcp6", // The network string net must be a stream-oriented
// "unix", or "unix-dgram". // network: "tcp", "tcp4", "tcp6", or "unix".
func Listen(net, laddr string) (l Listener, err os.Error) { func Listen(net, laddr string) (l Listener, err os.Error) {
switch net { switch net {
case "tcp", "tcp4", "tcp6": case "tcp", "tcp4", "tcp6":
l, err := ListenTCP(net, laddr); var la *TCPAddr;
if laddr != "" {
if la, err = ResolveTCPAddr(laddr); err != nil {
return nil, err;
}
}
l, err := ListenTCP(net, la);
if err != nil { if err != nil {
return nil, err; return nil, err;
} }
return l, nil; return l, nil;
case "unix", "unix-dgram": case "unix":
l, err := ListenUnix(net, laddr); var la *UnixAddr;
if laddr != "" {
if la, err = ResolveUnixAddr(net, laddr); err != nil {
return nil, err;
}
}
l, err := ListenUnix(net, la);
if err != nil { if err != nil {
return nil, err; return nil, err;
} }
return l, nil; return l, nil;
// BUG(rsc): Listen should support UDP. }
return nil, UnknownNetworkError(net);
}
// ListenPacket announces on the local network address laddr.
// The network string net must be a packet-oriented network:
// "udp", "udp4", "udp6", or "unixgram".
func ListenPacket(net, laddr string) (c PacketConn, err os.Error) {
switch net {
case "udp", "udp4", "udp6":
var la *UDPAddr;
if laddr != "" {
if la, err = ResolveUDPAddr(laddr); err != nil {
return nil, err;
}
}
c, err := ListenUDP(net, la);
if err != nil {
return nil, err;
}
return c, nil;
case "unixgram":
var la *UnixAddr;
if laddr != "" {
if la, err = ResolveUnixAddr(net, laddr); err != nil {
return nil, err;
}
}
c, err := DialUnix(net, la, nil);
if err != nil {
return nil, err;
}
return c, nil;
} }
return nil, UnknownNetworkError(net); return nil, UnknownNetworkError(net);
} }
...@@ -161,7 +235,7 @@ var errMissingAddress = os.ErrorString("missing address") ...@@ -161,7 +235,7 @@ var errMissingAddress = os.ErrorString("missing address")
type OpError struct { type OpError struct {
Op string; Op string;
Net string; Net string;
Addr string; Addr Addr;
Error os.Error; Error os.Error;
} }
...@@ -170,8 +244,8 @@ func (e *OpError) String() string { ...@@ -170,8 +244,8 @@ func (e *OpError) String() string {
if e.Net != "" { if e.Net != "" {
s += " " + e.Net; s += " " + e.Net;
} }
if e.Addr != "" { if e.Addr != nil {
s += " " + e.Addr; s += " " + e.Addr.String();
} }
s += ": " + e.Error.String(); s += ": " + e.Error.String();
return s; return s;
......
...@@ -6,6 +6,7 @@ package net ...@@ -6,6 +6,7 @@ package net
import ( import (
"io"; "io";
"os";
"strings"; "strings";
"syscall"; "syscall";
"testing"; "testing";
...@@ -29,10 +30,10 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done ...@@ -29,10 +30,10 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done
if err != nil { if err != nil {
t.Fatalf("net.Listen(%q, %q) = _, %v", network, addr, err); t.Fatalf("net.Listen(%q, %q) = _, %v", network, addr, err);
} }
listening <- l.Addr(); listening <- l.Addr().String();
for { for {
fd, _, err := l.Accept(); fd, err := l.Accept();
if err != nil { if err != nil {
break; break;
} }
...@@ -45,9 +46,13 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done ...@@ -45,9 +46,13 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done
} }
func connect(t *testing.T, network, addr string) { func connect(t *testing.T, network, addr string) {
fd, err := Dial(network, "", addr); var laddr string;
if network == "unixgram" {
laddr = addr + ".local";
}
fd, err := Dial(network, laddr, addr);
if err != nil { if err != nil {
t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, "", addr, err); t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, laddr, addr, err);
} }
b := strings.Bytes("hello, world\n"); b := strings.Bytes("hello, world\n");
...@@ -81,7 +86,7 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) { ...@@ -81,7 +86,7 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) {
<-done; // make sure server stopped <-done; // make sure server stopped
} }
func TestTcpServer(t *testing.T) { func TestTCPServer(t *testing.T) {
doTest(t, "tcp", "0.0.0.0", "127.0.0.1"); doTest(t, "tcp", "0.0.0.0", "127.0.0.1");
doTest(t, "tcp", "[::]", "[::ffff:127.0.0.1]"); doTest(t, "tcp", "[::]", "[::ffff:127.0.0.1]");
doTest(t, "tcp", "[::]", "127.0.0.1"); doTest(t, "tcp", "[::]", "127.0.0.1");
...@@ -90,9 +95,75 @@ func TestTcpServer(t *testing.T) { ...@@ -90,9 +95,75 @@ func TestTcpServer(t *testing.T) {
} }
func TestUnixServer(t *testing.T) { func TestUnixServer(t *testing.T) {
os.Remove("/tmp/gotest.net");
doTest(t, "unix", "/tmp/gotest.net", "/tmp/gotest.net"); doTest(t, "unix", "/tmp/gotest.net", "/tmp/gotest.net");
os.Remove("/tmp/gotest.net");
if syscall.OS == "linux" { if syscall.OS == "linux" {
// Test abstract unix domain socket, a Linux-ism // Test abstract unix domain socket, a Linux-ism
doTest(t, "unix", "@gotest/net", "@gotest/net"); doTest(t, "unix", "@gotest/net", "@gotest/net");
} }
} }
func runPacket(t *testing.T, network, addr string, listening chan<- string, done chan<- int) {
c, err := ListenPacket(network, addr);
if err != nil {
t.Fatalf("net.ListenPacket(%q, %q) = _, %v", network, addr, err);
}
listening <- c.LocalAddr().String();
c.SetReadTimeout(10e6); // 10ms
var buf [1000]byte;
for {
n, addr, err := c.ReadFrom(&buf);
if err == os.EAGAIN {
if done <- 1 {
break;
}
continue;
}
if err != nil {
break;
}
if _, err = c.WriteTo(buf[0:n], addr); err != nil {
t.Fatalf("WriteTo %v: %v", addr, err);
}
}
c.Close();
done <- 1;
}
func doTestPacket(t *testing.T, network, listenaddr, dialaddr string) {
t.Logf("TestPacket %s %s %s\n", network, listenaddr, dialaddr);
listening := make(chan string);
done := make(chan int);
if network == "udp" {
listenaddr += ":0"; // any available port
}
go runPacket(t, network, listenaddr, listening, done);
addr := <-listening; // wait for server to start
if network == "udp" {
dialaddr += addr[strings.LastIndex(addr, ":"):len(addr)];
}
connect(t, network, dialaddr);
<-done; // tell server to stop
<-done; // wait for stop
}
func TestUDPServer(t *testing.T) {
doTestPacket(t, "udp", "0.0.0.0", "127.0.0.1");
doTestPacket(t, "udp", "[::]", "[::ffff:127.0.0.1]");
doTestPacket(t, "udp", "[::]", "127.0.0.1");
doTestPacket(t, "udp", "", "127.0.0.1");
doTestPacket(t, "udp", "0.0.0.0", "[::ffff:127.0.0.1]");
}
func TestUnixDatagramServer(t *testing.T) {
os.Remove("/tmp/gotest1.net");
os.Remove("/tmp/gotest1.net.local");
doTestPacket(t, "unixgram", "/tmp/gotest1.net", "/tmp/gotest1.net");
os.Remove("/tmp/gotest1.net");
os.Remove("/tmp/gotest1.net.local");
if syscall.OS == "linux" {
// Test abstract unix domain socket, a Linux-ism
doTestPacket(t, "unixgram", "@gotest1/net", "@gotest1/net");
}
}
...@@ -21,7 +21,7 @@ func boolint(b bool) int { ...@@ -21,7 +21,7 @@ func boolint(b bool) int {
} }
// Generic socket creation. // Generic socket creation.
func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd *netFD, err os.Error) { func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) {
// See ../syscall/exec.go for description of ForkLock. // See ../syscall/exec.go for description of ForkLock.
syscall.ForkLock.RLock(); syscall.ForkLock.RLock();
s, e := syscall.Socket(f, p, t); s, e := syscall.Socket(f, p, t);
...@@ -51,7 +51,12 @@ func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd ...@@ -51,7 +51,12 @@ func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd
} }
} }
fd, err = newFD(s, net, laddr, raddr); sa, _ := syscall.Getsockname(s);
laddr := toAddr(sa);
sa, _ = syscall.Getpeername(s);
raddr := toAddr(sa);
fd, err = newFD(s, f, p, net, laddr, raddr);
if err != nil { if err != nil {
syscall.Close(s); syscall.Close(s);
return nil, err return nil, err
...@@ -60,78 +65,6 @@ func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd ...@@ -60,78 +65,6 @@ func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd
return fd, nil return fd, nil
} }
// Generic implementation of Conn interface; not exported.
type connBase struct {
fd *netFD;
raddr string;
}
func (c *connBase) LocalAddr() string {
if c == nil {
return ""
}
return c.fd.addr();
}
func (c *connBase) RemoteAddr() string {
if c == nil {
return ""
}
return c.fd.remoteAddr();
}
func (c *connBase) File() *os.File {
if c == nil {
return nil
}
return c.fd.file;
}
func (c *connBase) sysFD() int {
if c == nil || c.fd == nil {
return -1;
}
return c.fd.fd;
}
func (c *connBase) Read(b []byte) (n int, err os.Error) {
n, err = c.fd.Read(b);
return n, err
}
func (c *connBase) Write(b []byte) (n int, err os.Error) {
n, err = c.fd.Write(b);
return n, err
}
func (c *connBase) ReadFrom(b []byte) (n int, raddr string, err os.Error) {
if c == nil {
return -1, "", os.EINVAL
}
n, err = c.Read(b);
return n, c.raddr, err
}
func (c *connBase) WriteTo(raddr string, b []byte) (n int, err os.Error) {
if c == nil {
return -1, os.EINVAL
}
if raddr != c.raddr {
return -1, os.EINVAL
}
n, err = c.Write(b);
return n, err
}
func (c *connBase) Close() os.Error {
if c == nil {
return os.EINVAL
}
return c.fd.Close()
}
func setsockoptInt(fd, level, opt int, value int) os.Error { func setsockoptInt(fd, level, opt int, value int) os.Error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, opt, value)); return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, opt, value));
} }
...@@ -141,49 +74,49 @@ func setsockoptNsec(fd, level, opt int, nsec int64) os.Error { ...@@ -141,49 +74,49 @@ func setsockoptNsec(fd, level, opt int, nsec int64) os.Error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv)); return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv));
} }
func (c *connBase) SetReadBuffer(bytes int) os.Error { func setReadBuffer(fd *netFD, bytes int) os.Error {
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes); return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes);
} }
func (c *connBase) SetWriteBuffer(bytes int) os.Error { func setWriteBuffer(fd *netFD, bytes int) os.Error {
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes); return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes);
} }
func (c *connBase) SetReadTimeout(nsec int64) os.Error { func setReadTimeout(fd *netFD, nsec int64) os.Error {
c.fd.rdeadline_delta = nsec; fd.rdeadline_delta = nsec;
return nil; return nil;
} }
func (c *connBase) SetWriteTimeout(nsec int64) os.Error { func setWriteTimeout(fd *netFD, nsec int64) os.Error {
c.fd.wdeadline_delta = nsec; fd.wdeadline_delta = nsec;
return nil; return nil;
} }
func (c *connBase) SetTimeout(nsec int64) os.Error { func setTimeout(fd *netFD, nsec int64) os.Error {
if e := c.SetReadTimeout(nsec); e != nil { if e := setReadTimeout(fd, nsec); e != nil {
return e return e
} }
return c.SetWriteTimeout(nsec) return setWriteTimeout(fd, nsec)
} }
func (c *connBase) SetReuseAddr(reuse bool) os.Error { func setReuseAddr(fd *netFD, reuse bool) os.Error {
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)); return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse));
} }
func (c *connBase) BindToDevice(dev string) os.Error { func bindToDevice(fd *netFD, dev string) os.Error {
// TODO(rsc): call setsockopt with null-terminated string pointer // TODO(rsc): call setsockopt with null-terminated string pointer
return os.EINVAL return os.EINVAL
} }
func (c *connBase) SetDontRoute(dontroute bool) os.Error { func setDontRoute(fd *netFD, dontroute bool) os.Error {
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)); return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute));
} }
func (c *connBase) SetKeepAlive(keepalive bool) os.Error { func setKeepAlive(fd *netFD, keepalive bool) os.Error {
return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)); return setsockoptInt(fd.fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive));
} }
func (c *connBase) SetLinger(sec int) os.Error { func setLinger(fd *netFD, sec int) os.Error {
var l syscall.Linger; var l syscall.Linger;
if sec >= 0 { if sec >= 0 {
l.Onoff = 1; l.Onoff = 1;
...@@ -192,11 +125,10 @@ func (c *connBase) SetLinger(sec int) os.Error { ...@@ -192,11 +125,10 @@ func (c *connBase) SetLinger(sec int) os.Error {
l.Onoff = 0; l.Onoff = 0;
l.Linger = 0; l.Linger = 0;
} }
e := syscall.SetsockoptLinger(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_LINGER, &l); e := syscall.SetsockoptLinger(fd.fd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l);
return os.NewSyscallError("setsockopt", e); return os.NewSyscallError("setsockopt", e);
} }
type UnknownSocketError struct { type UnknownSocketError struct {
sa syscall.Sockaddr; sa syscall.Sockaddr;
} }
......
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// TCP sockets
package net
import (
"os";
"syscall";
)
func sockaddrToTCP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
return &TCPAddr{&sa.Addr, sa.Port};
case *syscall.SockaddrInet6:
return &TCPAddr{&sa.Addr, sa.Port};
}
return nil;
}
// TCPAddr represents the address of a TCP end point.
type TCPAddr struct {
IP IP;
Port int;
}
// Network returns the address's network name, "tcp".
func (a *TCPAddr) Network() string {
return "tcp";
}
func (a *TCPAddr) String() string {
return joinHostPort(a.IP.String(), itoa(a.Port));
}
func (a *TCPAddr) family() int {
if a == nil || len(a.IP) <= 4 {
return syscall.AF_INET;
}
if ip := a.IP.To4(); ip != nil {
return syscall.AF_INET;
}
return syscall.AF_INET6;
}
func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) {
return ipToSockaddr(family, a.IP, a.Port);
}
func (a *TCPAddr) toAddr() sockaddr {
if a == nil { // nil *TCPAddr
return nil; // nil interface
}
return a;
}
// ResolveTCPAddr parses addr as a TCP address of the form
// host:port and resolves domain names or port names to
// numeric addresses. A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveTCPAddr(addr string) (*TCPAddr, os.Error) {
ip, port, err := hostPortToIP("tcp", addr);
if err != nil {
return nil, err;
}
return &TCPAddr{ip, port}, nil;
}
// TCPConn is an implementation of the Conn interface
// for TCP network connections.
type TCPConn struct {
fd *netFD;
}
func newTCPConn(fd *netFD) *TCPConn {
c := &TCPConn{fd};
setsockoptInt(fd.fd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1);
return c
}
func (c *TCPConn) ok() bool {
if c == nil || c.fd == nil { panic() }
return c != nil && c.fd != nil;
}
// Implementation of the Conn interface - see Conn for documentation.
// Read reads data from the TCP connection.
//
// Read can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *TCPConn) Read(b []byte) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
return c.fd.Read(b);
}
// Write writes data to the TCP connection.
//
// Write can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *TCPConn) Write(b []byte) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
return c.fd.Write(b);
}
// Close closes the TCP connection.
func (c *TCPConn) Close() os.Error {
if !c.ok() {
return os.EINVAL
}
err := c.fd.Close();
c.fd = nil;
return err;
}
// LocalAddr returns the local network address, a *TCPAddr.
func (c *TCPConn) LocalAddr() Addr {
if !c.ok() {
return nil;
}
return c.fd.laddr;
}
// RemoteAddr returns the remote network address, a *TCPAddr.
func (c *TCPConn) RemoteAddr() Addr {
if !c.ok() {
return nil;
}
return c.fd.raddr;
}
// SetTimeout sets the read and write deadlines associated
// with the connection.
func (c *TCPConn) SetTimeout(nsec int64) os.Error {
if !c.ok() {
return os.EINVAL
}
return setTimeout(c.fd, nsec);
}
// SetReadTimeout sets the time (in nanoseconds) that
// Read will wait for data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
func (c *TCPConn) SetReadTimeout(nsec int64) os.Error {
if !c.ok() {
return os.EINVAL
}
return setReadTimeout(c.fd, nsec);
}
// SetWriteTimeout sets the time (in nanoseconds) that
// Write will wait to send its data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
func (c *TCPConn) SetWriteTimeout(nsec int64) os.Error {
if !c.ok() {
return os.EINVAL
}
return setWriteTimeout(c.fd, nsec);
}
// SetReadBuffer sets the size of the operating system's
// receive buffer associated with the connection.
func (c *TCPConn) SetReadBuffer(bytes int) os.Error {
if !c.ok() {
return os.EINVAL
}
return setReadBuffer(c.fd, bytes);
}
// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
func (c *TCPConn) SetWriteBuffer(bytes int) os.Error {
if !c.ok() {
return os.EINVAL
}
return setWriteBuffer(c.fd, bytes);
}
// SetLinger sets the behavior of Close() on a connection
// which still has data waiting to be sent or to be acknowledged.
//
// If sec < 0 (the default), Close returns immediately and
// the operating system finishes sending the data in the background.
//
// If sec == 0, Close returns immediately and the operating system
// discards any unsent or unacknowledged data.
//
// If sec > 0, Close blocks for at most sec seconds waiting for
// data to be sent and acknowledged.
func (c *TCPConn) SetLinger(sec int) os.Error {
if !c.ok() {
return os.EINVAL
}
return setLinger(c.fd, sec);
}
// SetKeepAlive sets whether the operating system should send
// keepalive messages on the connection.
func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error {
if !c.ok() {
return os.EINVAL
}
return setKeepAlive(c.fd, keepalive);
}
// DialTCP is like Dial but can only connect to TCP networks
// and returns a TCPConn structure.
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) {
if raddr == nil {
return nil, &OpError{"dial", "tcp", nil, errMissingAddress}
}
fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, "dial", sockaddrToTCP);
if e != nil {
return nil, e
}
return newTCPConn(fd), nil
}
// TCPListener is a TCP network listener.
// Clients should typically use variables of type Listener
// instead of assuming TCP.
type TCPListener struct {
fd *netFD;
}
// ListenTCP announces on the TCP address laddr and returns a TCP listener.
// Net must be "tcp", "tcp4", or "tcp6".
// If laddr has a port of 0, it means to listen on some available port.
// The caller can use l.Addr() to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error) {
fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, "listen", sockaddrToTCP);
if err != nil {
return nil, err
}
errno := syscall.Listen(fd.fd, listenBacklog());
if errno != 0 {
syscall.Close(fd.fd);
return nil, &OpError{"listen", "tcp", laddr, os.Errno(errno)};
}
l = new(TCPListener);
l.fd = fd;
return l, nil
}
// AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err os.Error) {
if l == nil || l.fd == nil || l.fd.fd < 0 {
return nil, os.EINVAL
}
fd, err := l.fd.accept(sockaddrToTCP);
if err != nil {
return nil, err
}
return newTCPConn(fd), nil
}
// Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn.
func (l *TCPListener) Accept() (c Conn, err os.Error) {
c1, err := l.AcceptTCP();
if err != nil {
return nil, err
}
return c1, nil;
}
// Close stops listening on the TCP address.
// Already Accepted connections are not closed.
func (l *TCPListener) Close() os.Error {
if l == nil || l.fd == nil {
return os.EINVAL
}
return l.fd.Close()
}
// Addr returns the listener's network address, a *TCPAddr.
func (l *TCPListener) Addr() Addr {
return l.fd.laddr;
}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// UDP sockets
package net
import (
"os";
"syscall";
)
func sockaddrToUDP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
return &UDPAddr{&sa.Addr, sa.Port};
case *syscall.SockaddrInet6:
return &UDPAddr{&sa.Addr, sa.Port};
}
return nil;
}
// UDPAddr represents the address of a UDP end point.
type UDPAddr struct {
IP IP;
Port int;
}
// Network returns the address's network name, "udp".
func (a *UDPAddr) Network() string {
return "udp";
}
func (a *UDPAddr) String() string {
return joinHostPort(a.IP.String(), itoa(a.Port));
}
func (a *UDPAddr) family() int {
if a == nil || len(a.IP) <= 4 {
return syscall.AF_INET;
}
if ip := a.IP.To4(); ip != nil {
return syscall.AF_INET;
}
return syscall.AF_INET6;
}
func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) {
return ipToSockaddr(family, a.IP, a.Port);
}
func (a *UDPAddr) toAddr() sockaddr {
if a == nil { // nil *UDPAddr
return nil; // nil interface
}
return a;
}
// ResolveUDPAddr parses addr as a UDP address of the form
// host:port and resolves domain names or port names to
// numeric addresses. A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveUDPAddr(addr string) (*UDPAddr, os.Error) {
ip, port, err := hostPortToIP("udp", addr);
if err != nil {
return nil, err;
}
return &UDPAddr{ip, port}, nil;
}
// UDPConn is the implementation of the Conn and PacketConn
// interfaces for UDP network connections.
type UDPConn struct {
fd *netFD;
}
func newUDPConn(fd *netFD) *UDPConn {
return &UDPConn{fd};
}
func (c *UDPConn) ok() bool {
return c != nil && c.fd != nil;
}
// Implementation of the Conn interface - see Conn for documentation.
// Read reads data from a single UDP packet on the connection.
// If the slice b is smaller than the arriving packet,
// the excess packet data may be discarded.
//
// Read can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UDPConn) Read(b []byte) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
return c.fd.Read(b);
}
// Write writes data to the connection as a single UDP packet.
//
// Write can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UDPConn) Write(b []byte) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
return c.fd.Write(b);
}
// Close closes the UDP connection.
func (c *UDPConn) Close() os.Error {
if !c.ok() {
return os.EINVAL
}
err := c.fd.Close();
c.fd = nil;
return err;
}
// LocalAddr returns the local network address.
func (c *UDPConn) LocalAddr() Addr {
if !c.ok() {
return nil;
}
return c.fd.laddr;
}
// RemoteAddr returns the remote network address, a *UDPAddr.
func (c *UDPConn) RemoteAddr() Addr {
if !c.ok() {
return nil;
}
return c.fd.raddr;
}
// SetTimeout sets the read and write deadlines associated
// with the connection.
func (c *UDPConn) SetTimeout(nsec int64) os.Error {
if !c.ok() {
return os.EINVAL
}
return setTimeout(c.fd, nsec);
}
// SetReadTimeout sets the time (in nanoseconds) that
// Read will wait for data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
func (c *UDPConn) SetReadTimeout(nsec int64) os.Error {
if !c.ok() {
return os.EINVAL
}
return setReadTimeout(c.fd, nsec);
}
// SetWriteTimeout sets the time (in nanoseconds) that
// Write will wait to send its data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
func (c *UDPConn) SetWriteTimeout(nsec int64) os.Error {
if !c.ok() {
return os.EINVAL
}
return setWriteTimeout(c.fd, nsec);
}
// SetReadBuffer sets the size of the operating system's
// receive buffer associated with the connection.
func (c *UDPConn) SetReadBuffer(bytes int) os.Error {
if !c.ok() {
return os.EINVAL
}
return setReadBuffer(c.fd, bytes);
}
// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
func (c *UDPConn) SetWriteBuffer(bytes int) os.Error {
if !c.ok() {
return os.EINVAL
}
return setWriteBuffer(c.fd, bytes);
}
// UDP-specific methods.
// ReadFromUDP reads a UDP packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
// ReadFromUDP can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) {
if !c.ok() {
return 0, nil, os.EINVAL
}
n, sa, errno := syscall.Recvfrom(c.fd.fd, b, 0);
if errno != 0 {
err = os.Errno(errno);
}
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
addr = &UDPAddr{&sa.Addr, sa.Port};
case *syscall.SockaddrInet6:
addr = &UDPAddr{&sa.Addr, sa.Port};
}
return;
}
// ReadFrom reads a UDP packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
// ReadFrom can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) {
if !c.ok() {
return 0, nil, os.EINVAL
}
n, uaddr, err := c.ReadFromUDP(b);
return n, uaddr.toAddr(), err;
}
// WriteToUDP writes a UDP packet to addr via c, copying the payload from b.
//
// WriteToUDP can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetWriteTimeout.
// On packet-oriented connections such as UDP, write timeouts are rare.
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
sa, err := addr.sockaddr(c.fd.family);
if err != nil {
return 0, err;
}
if errno := syscall.Sendto(c.fd.fd, b, 0, sa); errno != 0 {
return 0, os.Errno(errno);
}
return len(b), nil;
}
// WriteTo writes a UDP packet with payload b to addr via c.
//
// WriteTo can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetWriteTimeout.
// On packet-oriented connections such as UDP, write timeouts are rare.
func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
a, ok := addr.(*UDPAddr);
if !ok {
return 0, &OpError{"writeto", "udp", addr, os.EINVAL};
}
return c.WriteToUDP(b, a);
}
// DialUDP connects to the remote address raddr on the network net,
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
// as the local address for the connection.
func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) {
switch net {
case "udp", "udp4", "udp6":
default:
return nil, UnknownNetworkError(net)
}
if raddr == nil {
return nil, &OpError{"dial", "udp", nil, errMissingAddress}
}
fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, "dial", sockaddrToUDP);
if e != nil {
return nil, e
}
return newUDPConn(fd), nil
}
// ListenUDP listens for incoming UDP packets addressed to the
// local address laddr. The returned connection c's ReadFrom
// and WriteTo methods can be used to receive and send UDP
// packets with per-packet addressing.
func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) {
switch net {
case "udp", "udp4", "udp6":
default:
return nil, UnknownNetworkError(net)
}
if laddr == nil {
return nil, &OpError{"listen", "udp", nil, errMissingAddress}
}
fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, "dial", sockaddrToUDP);
if e != nil {
return nil, e
}
return newUDPConn(fd), nil
}
...@@ -11,14 +11,14 @@ import ( ...@@ -11,14 +11,14 @@ import (
"syscall"; "syscall";
) )
func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error) { func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err os.Error) {
var proto int; var proto int;
switch net { switch net {
default: default:
return nil, UnknownNetworkError(net); return nil, UnknownNetworkError(net);
case "unix": case "unix":
proto = syscall.SOCK_STREAM; proto = syscall.SOCK_STREAM;
case "unix-dgram": case "unixgram":
proto = syscall.SOCK_DGRAM; proto = syscall.SOCK_DGRAM;
} }
...@@ -28,25 +28,30 @@ func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error) ...@@ -28,25 +28,30 @@ func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error)
panic("unixSocket", mode); panic("unixSocket", mode);
case "dial": case "dial":
if laddr != "" { if laddr != nil {
return nil, &OpError{mode, net, raddr, &AddrError{"unexpected local address", laddr}} la = &syscall.SockaddrUnix{Name: laddr.Name};
} }
if raddr == "" { if raddr != nil {
return nil, &OpError{mode, net, "", errMissingAddress} ra = &syscall.SockaddrUnix{Name: raddr.Name};
} else if proto != syscall.SOCK_DGRAM || laddr == nil {
return nil, &OpError{mode, net, nil, errMissingAddress}
} }
ra = &syscall.SockaddrUnix{Name: raddr};
case "listen": case "listen":
if laddr == "" { if laddr == nil {
return nil, &OpError{mode, net, "", errMissingAddress} return nil, &OpError{mode, net, nil, errMissingAddress}
} }
la = &syscall.SockaddrUnix{Name: laddr}; la = &syscall.SockaddrUnix{Name: laddr.Name};
if raddr != "" { if raddr != nil {
return nil, &OpError{mode, net, laddr, &AddrError{"unexpected remote address", raddr}} return nil, &OpError{mode, net, raddr, &AddrError{"unexpected remote address", raddr.String()}}
} }
} }
fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra); f := sockaddrToUnix;
if proto != syscall.SOCK_STREAM {
f = sockaddrToUnixgram;
}
fd, err = socket(net, syscall.AF_UNIX, proto, 0, la, ra, f);
if err != nil { if err != nil {
goto Error; goto Error;
} }
...@@ -60,108 +65,318 @@ Error: ...@@ -60,108 +65,318 @@ Error:
return nil, &OpError{mode, net, addr, err}; return nil, &OpError{mode, net, addr, err};
} }
// ConnUnix is an implementation of the Conn interface // UnixAddr represents the address of a Unix domain socket end point.
type UnixAddr struct {
Name string;
Datagram bool;
}
func sockaddrToUnix(sa syscall.Sockaddr) Addr {
if s, ok := sa.(*syscall.SockaddrUnix); ok {
return &UnixAddr{s.Name, false};
}
return nil;
}
func sockaddrToUnixgram(sa syscall.Sockaddr) Addr {
if s, ok := sa.(*syscall.SockaddrUnix); ok {
return &UnixAddr{s.Name, true};
}
return nil;
}
// Network returns the address's network name, "unix" or "unixgram".
func (a *UnixAddr) Network() string {
if a == nil || !a.Datagram {
return "unix";
}
return "unixgram";
}
func (a *UnixAddr) String() string {
if a == nil {
return "<nil>"
}
return a.Name;
}
func (a *UnixAddr) toAddr() Addr {
if a == nil { // nil *UnixAddr
return nil; // nil interface
}
return a;
}
// ResolveUnixAddr parses addr as a Unix domain socket address.
// The string net gives the network name, "unix" or "unixgram".
func ResolveUnixAddr(net, addr string) (*UnixAddr, os.Error) {
var datagram bool;
switch net {
case "unix":
case "unixgram":
datagram = true;
default:
return nil, UnknownNetworkError(net);
}
return &UnixAddr{addr, datagram}, nil;
}
// UnixConn is an implementation of the Conn interface
// for connections to Unix domain sockets. // for connections to Unix domain sockets.
type ConnUnix struct { type UnixConn struct {
connBase fd *netFD;
}
func newUnixConn(fd *netFD) *UnixConn {
return &UnixConn{fd}
}
func (c *UnixConn) ok() bool {
return c != nil && c.fd != nil;
}
// Implementation of the Conn interface - see Conn for documentation.
// Read reads data from the Unix domain connection.
//
// Read can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UnixConn) Read(b []byte) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
return c.fd.Read(b);
}
// Write writes data to the Unix domain connection.
//
// Write can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UnixConn) Write(b []byte) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
return c.fd.Write(b);
}
// Close closes the Unix domain connection.
func (c *UnixConn) Close() os.Error {
if !c.ok() {
return os.EINVAL
}
err := c.fd.Close();
c.fd = nil;
return err;
}
// LocalAddr returns the local network address, a *UnixAddr.
// Unlike in other protocols, LocalAddr is usually nil for dialed connections.
func (c *UnixConn) LocalAddr() Addr {
if !c.ok() {
return nil;
}
return c.fd.laddr;
}
// RemoteAddr returns the remote network address, a *UnixAddr.
// Unlike in other protocols, RemoteAddr is usually nil for connections
// accepted by a listener.
func (c *UnixConn) RemoteAddr() Addr {
if !c.ok() {
return nil;
}
return c.fd.raddr;
}
// SetTimeout sets the read and write deadlines associated
// with the connection.
func (c *UnixConn) SetTimeout(nsec int64) os.Error {
if !c.ok() {
return os.EINVAL
}
return setTimeout(c.fd, nsec);
}
// SetReadTimeout sets the time (in nanoseconds) that
// Read will wait for data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
func (c *UnixConn) SetReadTimeout(nsec int64) os.Error {
if !c.ok() {
return os.EINVAL
}
return setReadTimeout(c.fd, nsec);
}
// SetWriteTimeout sets the time (in nanoseconds) that
// Write will wait to send its data before returning os.EAGAIN.
// Setting nsec == 0 (the default) disables the deadline.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error {
if !c.ok() {
return os.EINVAL
}
return setWriteTimeout(c.fd, nsec);
} }
func newConnUnix(fd *netFD, raddr string) *ConnUnix { // SetReadBuffer sets the size of the operating system's
c := new(ConnUnix); // receive buffer associated with the connection.
c.fd = fd; func (c *UnixConn) SetReadBuffer(bytes int) os.Error {
c.raddr = raddr; if !c.ok() {
return c; return os.EINVAL
}
return setReadBuffer(c.fd, bytes);
}
// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
func (c *UnixConn) SetWriteBuffer(bytes int) os.Error {
if !c.ok() {
return os.EINVAL
}
return setWriteBuffer(c.fd, bytes);
}
// ReadFromUnix reads a packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
// ReadFromUnix can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) {
if !c.ok() {
return 0, nil, os.EINVAL
}
n, sa, errno := syscall.Recvfrom(c.fd.fd, b, 0);
if errno != 0 {
err = os.Errno(errno);
}
switch sa := sa.(type) {
case *syscall.SockaddrUnix:
addr = &UnixAddr{sa.Name, c.fd.proto == syscall.SOCK_DGRAM};
}
return;
}
// ReadFrom reads a packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
// ReadFrom can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetReadTimeout.
func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) {
if !c.ok() {
return 0, nil, os.EINVAL
}
n, uaddr, err := c.ReadFromUnix(b);
return n, uaddr.toAddr(), err;
}
// WriteToUnix writes a packet to addr via c, copying the payload from b.
//
// WriteToUnix can be made to time out and return err == os.EAGAIN
// after a fixed time limit; see SetTimeout and SetWriteTimeout.
// On packet-oriented connections such as UDP, write timeouts are rare.
func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
if addr.Datagram != (c.fd.proto == syscall.SOCK_DGRAM) {
return 0, os.EAFNOSUPPORT;
}
sa := &syscall.SockaddrUnix{Name: addr.Name};
if errno := syscall.Sendto(c.fd.fd, b, 0, sa); errno != 0 {
return 0, os.Errno(errno);
}
return len(b), nil;
} }
// DialUnix is like Dial but can only connect to Unix domain sockets // WriteTo writes a packet to addr via c, copying the payload from b.
// and returns a ConnUnix structure. The laddr argument must be //
// the empty string; it is included only to match the signature of // WriteTo can be made to time out and return err == os.EAGAIN
// the other dial routines. // after a fixed time limit; see SetTimeout and SetWriteTimeout.
func DialUnix(net, laddr, raddr string) (c *ConnUnix, err os.Error) { // On packet-oriented connections such as UDP, write timeouts are rare.
func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) {
if !c.ok() {
return 0, os.EINVAL
}
a, ok := addr.(*UnixAddr);
if !ok {
return 0, &OpError{"writeto", "unix", addr, os.EINVAL};
}
return c.WriteToUnix(b, a);
}
// DialUDP connects to the remote address raddr on the network net,
// which must be "unix" or "unixdgram". If laddr is not nil, it is used
// as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err os.Error) {
fd, e := unixSocket(net, laddr, raddr, "dial"); fd, e := unixSocket(net, laddr, raddr, "dial");
if e != nil { if e != nil {
return nil, e return nil, e
} }
return newConnUnix(fd, raddr), nil; return newUnixConn(fd), nil;
} }
// ListenerUnix is a Unix domain socket listener. // UnixListener is a Unix domain socket listener.
// Clients should typically use variables of type Listener // Clients should typically use variables of type Listener
// instead of assuming Unix domain sockets. // instead of assuming Unix domain sockets.
type ListenerUnix struct { type UnixListener struct {
fd *netFD; fd *netFD;
laddr string path string;
} }
// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. // ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
// Net can be either "unix" (stream sockets) or "unix-dgram" (datagram sockets). // Net must be "unix" (stream sockets).
func ListenUnix(net, laddr string) (l *ListenerUnix, err os.Error) { func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) {
fd, e := unixSocket(net, laddr, "", "listen"); if net != "unix" && net != "unixgram" {
return nil, UnknownNetworkError(net);
}
if laddr != nil {
laddr = &UnixAddr{laddr.Name, net == "unixgram"}; // make our own copy
}
fd, e := unixSocket(net, laddr, nil, "listen");
if e != nil { if e != nil {
if pe, ok := e.(*os.PathError); ok { if pe, ok := e.(*os.PathError); ok {
e = pe.Error; e = pe.Error;
} }
// Check for socket ``in use'' but ``refusing connections,'' return nil, e;
// which means some program created it and exited
// without unlinking it from the file system.
// Clean up on that program's behalf and try again.
// Don't do this for Linux's ``abstract'' sockets, which begin with @.
if e != os.EADDRINUSE || laddr[0] == '@' {
return nil, e;
}
fd1, e1 := unixSocket(net, "", laddr, "dial");
if e1 == nil {
fd1.Close();
}
if pe, ok := e1.(*os.PathError); ok {
e1 = pe.Error;
}
if e1 != os.ECONNREFUSED {
return nil, e;
}
syscall.Unlink(laddr);
fd1, e1 = unixSocket(net, laddr, "", "listen");
if e1 != nil {
return nil, e;
}
fd = fd1;
} }
e1 := syscall.Listen(fd.fd, 8); // listenBacklog()); e1 := syscall.Listen(fd.fd, 8); // listenBacklog());
if e1 != 0 { if e1 != 0 {
syscall.Close(fd.fd); syscall.Close(fd.fd);
return nil, &OpError{"listen", "unix", laddr, os.Errno(e1)}; return nil, &OpError{"listen", "unix", laddr, os.Errno(e1)};
} }
return &ListenerUnix{fd, laddr}, nil; return &UnixListener{fd, laddr.Name}, nil;
} }
// AcceptUnix accepts the next incoming call and returns the new connection // AcceptUnix accepts the next incoming call and returns the new connection
// and the remote address. // and the remote address.
func (l *ListenerUnix) AcceptUnix() (c *ConnUnix, raddr string, err os.Error) { func (l *UnixListener) AcceptUnix() (c *UnixConn, err os.Error) {
if l == nil || l.fd == nil || l.fd.fd < 0 { if l == nil || l.fd == nil || l.fd.fd < 0 {
return nil, "", os.EINVAL return nil, os.EINVAL
} }
fd, e := l.fd.accept(); fd, e := l.fd.accept(sockaddrToUnix);
if e != nil { if e != nil {
return nil, "", e return nil, e
} }
return newConnUnix(fd, fd.raddr), raddr, nil c = newUnixConn(fd);
return c, nil
} }
// Accept implements the Accept method in the Listener interface; // Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn. // it waits for the next call and returns a generic Conn.
func (l *ListenerUnix) Accept() (c Conn, raddr string, err os.Error) { func (l *UnixListener) Accept() (c Conn, err os.Error) {
// TODO(rsc): Should return l.AcceptUnix() be okay here? c1, err := l.AcceptUnix();
// There is a type conversion -- the first return arg of if err != nil {
// l.AcceptUnix() is *ConnUnix and it gets converted to Conn return nil, err;
// in the explicit assignment. }
c, raddr, err = l.AcceptUnix(); return c1, nil;
return;
} }
// Close stops listening on the Unix address. // Close stops listening on the Unix address.
// Already accepted connections are not closed. // Already accepted connections are not closed.
func (l *ListenerUnix) Close() os.Error { func (l *UnixListener) Close() os.Error {
if l == nil || l.fd == nil { if l == nil || l.fd == nil {
return os.EINVAL return os.EINVAL
} }
...@@ -176,8 +391,8 @@ func (l *ListenerUnix) Close() os.Error { ...@@ -176,8 +391,8 @@ func (l *ListenerUnix) Close() os.Error {
// is at least compatible with the auto-remove // is at least compatible with the auto-remove
// sequence in ListenUnix. It's only non-Go // sequence in ListenUnix. It's only non-Go
// programs that can mess us up. // programs that can mess us up.
if l.laddr[0] != '@' { if l.path[0] != '@' {
syscall.Unlink(l.laddr); syscall.Unlink(l.path);
} }
err := l.fd.Close(); err := l.fd.Close();
l.fd = nil; l.fd = nil;
...@@ -185,6 +400,26 @@ func (l *ListenerUnix) Close() os.Error { ...@@ -185,6 +400,26 @@ func (l *ListenerUnix) Close() os.Error {
} }
// Addr returns the listener's network address. // Addr returns the listener's network address.
func (l *ListenerUnix) Addr() string { func (l *UnixListener) Addr() Addr {
return l.fd.addr(); return l.fd.laddr;
}
// ListenUnixgram listens for incoming Unix datagram packets addressed to the
// local address laddr. The returned connection c's ReadFrom
// and WriteTo methods can be used to receive and send UDP
// packets with per-packet addressing. The network net must be "unixgram".
func ListenUnixgram(net string, laddr *UnixAddr) (c *UDPConn, err os.Error) {
switch net {
case "unixgram":
default:
return nil, UnknownNetworkError(net)
}
if laddr == nil {
return nil, &OpError{"listen", "unixgram", nil, errMissingAddress}
}
fd, e := unixSocket(net, laddr, nil, "listen");
if e != nil {
return nil, e
}
return newUDPConn(fd), nil
} }
...@@ -74,6 +74,7 @@ var ( ...@@ -74,6 +74,7 @@ var (
EADDRINUSE Error = Errno(syscall.EADDRINUSE); EADDRINUSE Error = Errno(syscall.EADDRINUSE);
ECONNREFUSED Error = Errno(syscall.ECONNREFUSED); ECONNREFUSED Error = Errno(syscall.ECONNREFUSED);
ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG); ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG);
EAFNOSUPPORT Error = Errno(syscall.EAFNOSUPPORT);
) )
// PathError records an error and the operation and file path that caused it. // PathError records an error and the operation and file path that caused it.
......
...@@ -129,7 +129,7 @@ func DialHTTP(network, address string) (*Client, os.Error) { ...@@ -129,7 +129,7 @@ func DialHTTP(network, address string) (*Client, os.Error) {
err = os.ErrorString("unexpected HTTP response: " + resp.Status); err = os.ErrorString("unexpected HTTP response: " + resp.Status);
} }
conn.Close(); conn.Close();
return nil, &net.OpError{"dial-http", network, address, err}; return nil, &net.OpError{"dial-http", network+" "+address, nil, err};
} }
// Dial connects to an RPC server at the specified network address. // Dial connects to an RPC server at the specified network address.
......
...@@ -355,7 +355,7 @@ func (server *serverType) input(conn io.ReadWriteCloser) { ...@@ -355,7 +355,7 @@ func (server *serverType) input(conn io.ReadWriteCloser) {
func (server *serverType) accept(lis net.Listener) { func (server *serverType) accept(lis net.Listener) {
for { for {
conn, _, err := lis.Accept(); conn, err := lis.Accept();
if err != nil { if err != nil {
log.Exit("rpc.Serve: accept:", err.String()); // TODO(r): exit? log.Exit("rpc.Serve: accept:", err.String()); // TODO(r): exit?
} }
......
...@@ -59,7 +59,7 @@ func startServer() { ...@@ -59,7 +59,7 @@ func startServer() {
if e != nil { if e != nil {
log.Exitf("net.Listen tcp :0: %v", e); log.Exitf("net.Listen tcp :0: %v", e);
} }
serverAddr = l.Addr(); serverAddr = l.Addr().String();
log.Stderr("Test RPC server listening on ", serverAddr); log.Stderr("Test RPC server listening on ", serverAddr);
go Accept(l); go Accept(l);
...@@ -69,7 +69,7 @@ func startServer() { ...@@ -69,7 +69,7 @@ func startServer() {
log.Stderrf("net.Listen tcp :0: %v", e); log.Stderrf("net.Listen tcp :0: %v", e);
os.Exit(1); os.Exit(1);
} }
httpServerAddr = l.Addr(); httpServerAddr = l.Addr().String();
log.Stderr("Test HTTP RPC server listening on ", httpServerAddr); log.Stderr("Test HTTP RPC server listening on ", httpServerAddr);
go http.Serve(l, nil); go http.Serve(l, nil);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment