I was reading the type declaration for http.Server in the documentation and I came across this.
type Server struct {
Addr string // TCP address to listen on, ":http" if empty
// more...
}
If you call srv.ListenAndServe() and srv.Addr is an empty string, then ListenAndServe will pass ":http" to net.Listen (http/server.go).
What does ":http" mean?
Sometimes ports have aliases. http is 80.You can see more names here: http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
Related
I was looking into grips with go and have a question regarding the helloworld grpc demo in go. Link to repo here
The server implementation calls one of the grpc autogenerated function to start the server and listen for incoming messages. However, the code passes a reference " &hwServer{}" of the struct to the function as below.
// Register Greeter on the server.
hwpb.RegisterGreeterServer(s, &hwServer{})
Meanwhile the autogenerated Function has the following signature, which at that position expects an interface but not as a pointer:
func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {
s.RegisterService(&Greeter_ServiceDesc, srv)
}
Therefore my question is, can I use both, a reference and pass by value as long as it satisfies the interface requirement? Otherwise I was used, that is the func arg expected a value, I cannot pass a pointer.
I want to develop a UDP server based on Linux. There are some IP set on that host machine (such as 1.1.1.1,1.1.1.2, 2001::1:1:1:1), and I want server listen on all IP as follows (9090 as sample)
udp6 0 0 :::9090 :::*
The server code as follows
package main
import (
"fmt"
"net"
)
func main() {
udpAddr, err := net.ResolveUDPAddr("udp", ":9090")
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
fmt.Println(err)
return
}
var data [1024]byte
n, addr, err := conn.ReadFromUDP(data[:])
if err != nil {
fmt.Println(err)
}
fmt.Println(n)
fmt.Println(addr)
// this is not my wanted result. it will print [::]:9090
fmt.Println(conn.LocalAddr())
}
When the client dial this server (dst_string is 1.1.1.1:9090);
Actual result:
the server will print conn.LocalAddr() with
[::]:9090
excepted result:
the server should print
1.1.1.1:9090
How to achieve that?
BTW: I know if UDP server only listen 1.1.1.1:9090 can make that. But server has many IP, I want the server listen all IP and LocalAddr() can print 1.1.1.1:9090
Let's cite this post from a PowerDNS developer:
There are two ways to listen on all addresses, one of which is to enumerate all interfaces, grab all their IP addresses, and bind to all of them. Lots of work, and non-portable work too. We really did not want to do that. You also need to monitor new addresses arriving.
Secondly, just bind to 0.0.0.0 and ::! This works very well for TCP and other connection-oriented protocols, but can fail silently for UDP and other connectionless protocols. How come? When a packet comes in on 0.0.0.0, we don’t know which IP address it was sent to. And this is a problem when replying to such a packet – what would the correct source address be? Because we are connectionless (and therefore stateless), the kernel doesn’t know what to do.
So it picks the most appropriate address, and that may be the wrong one. There are some heuristics that make some kernels do the right thing more reliably, but there are no guarantees.
When receiving packets on datagram sockets, we usually use recvfrom(2), but this does not provide the missing bit of data: which IP address the packet was actually sent to. There is no recvfromto(). Enter the very powerful recvmsg(2). Recvmsg() allows for the getting of a boatload of parameters per datagram, as requested via setsockopt().
One of the parameters we can request is the original destination IP address of the packet.
<…>
IPv4
<..> For Linux, use the setsockopt() called IP_PKTINFO, which will get you a parameter over recvmsg() called IP_PKTINFO, which carries a struct in_pktinfo, which has a 4 byte IP address hiding in its ipi_addr field.
It appears, that the closest to recvmsg() thing there exists in the net package is net.IPConn.ReadMsgIP, and its documentation states that
The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be used to manipulate IP-level socket options in oob.
Hence, looks like a way forward.
I'd also make the following points explicit (they are not obvious from the text above):
It seems, the net package of the Go's stdlib does not have a standard and easy-to-use way to have what you want.
It appears that the approach to getting the destination address of a datagram when receiving them on a wildcard address is not really standardized, and hence implementations vary between different kernels.
While it looks that net.IPConn.ReadMsgIP wraps recvmsg(2), I'd first verify that in the source code of the Go standard library. Pay attention to the fact that the stdlib contains code for all platforms it supports, so make sure you understand what build constraints are.
https://godoc.org/golang.org/x/net/ may help. And so do the syscall package and https://godoc.org/golang.org/x/sys — if the stock one falls short.
Thanks response of kostix very much.
And according to IP_PKTINFO prompt, I found the following code can resolve my ipv4 issue directly
https://gist.github.com/omribahumi/5da8517497042152691e
But for ipv6, the result is still NOT excepted
package main
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"syscall"
)
func main() {
serverAddr, _ := net.ResolveUDPAddr("udp", ":9999")
sConn, _ := net.ListenUDP("udp", serverAddr)
file, _ := sConn.File()
syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IP_PKTINFO, 1)
data := make([]byte, 1024)
oob := make([]byte, 2048)
sConn.ReadMsgUDP(data, oob)
oob_buffer := bytes.NewBuffer(oob)
msg := syscall.Cmsghdr{}
binary.Read(oob_buffer, binary.LittleEndian, &msg)
if msg.Level == syscall.IPPROTO_IPV6 && msg.Type == syscall.IP_PKTINFO {
packet_info := syscall.Inet6Pktinfo{}
binary.Read(oob_buffer, binary.LittleEndian, &packet_info)
fmt.Println(packet_info)
// the ipv6 address is not my wanted result
fmt.Println(packet_info.Addr)
}
}
The result as follows
root#test-VirtualBox:/home/test/mygo/src/udp# go run s2.go
{[64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 0}
[64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
the tcpdump sniffer as follows
22:20:47.009712 IP6 ::1.43305 > ::1.1025: UDP, length 6
I resolve this ipv6 issue by Setting the source IP for a UDP socket via set ipv6 options
err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1)
thanks everyone
Here is my struct and instantiation:
type mxSet struct {
domainLock sync.RWMutex
connections []chan *decodejson.RcptEntity
}
var connMap = make(map[string]*mxSet)
How do I make a buffered channel when it's inside a struct inside a map? These channels correspond to TCP connections I have open, so I need to be able to dynamically open/close them.
How do I also instantiate the sync.RWMutex at the same time?
More context: What I am trying to do is maintain a map of TCP connections open to a domain. The []chan is how I send data into a specific TCP connections for a specific domain.
Please offer a conceptual explanation of what needs to happen here as well as code.
If no value is in the map, then create the value and add to the map.
mx, ok := connMap[key]
if !ok {
mx = &mxSet{}
connMap[key] = mx
}
... use mx as needed needed.
The zero values of the sync.RwMutex and the slice of channels are ready to use. No code is required to set them up.
I'm trying an example related to struct embedding of interfaces
// https://talks.golang.org/2014/go4java.slide#52
// Struct embedding of interfaces
// https://play.golang.org/p/SYiZ7M1OEhU
package main
import (
"bytes"
"fmt"
"net"
)
// net.Conn has Read and Write
type loopBack struct {
net.Conn
buf bytes.Buffer
}
func (c *loopBack) Read(b []byte) (int, error) {
fmt.Println("loopBack Read")
return 0, nil
}
func main() {
loop := loopBack{}
loop.Read(nil)
loop.Write(nil)
}
and the Write method is undefined, so I get this runtime error
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0xe28ca]
goroutine 1 [running]:
main.main()
/tmp/sandbox812386031/main.go:28 +0x6a
Exist some way to validate it at compile time?
link to code
https://play.golang.org/p/SYiZ7M1OEhU
What you are doing is not the same as saying "loopBack implements net.Conn".
To get compile-time error(s) about the missing method – and the mismatched Read(), too – declare loop's type:
(And don't embed net.Conn in loopBack)
func main() {
var loop net.Conn = loopBack{}
Golang doesn't require exlicit interface implementations. What you do here:
type loopBack struct {
net.Conn
buf bytes.Buffer
}
Is the similar to:
type loopBack struct{
Conn net.Conn
buf bytes.Buffer
}
net.Conn being an interface type, the first field of your loopBack type can be anything that implements the net.Conn interface, which is of course more than Read and Write alone (see here).
The advantage of embedding types is that the fields and receiver functions (name conflicts aside) can be accessed directly on the types that embeds them.
With an embedded net.Conn field, you can indeed write:
loop.Write(nil)
If the Conn field is initialised (otherwise, its value is nil). Changing the declaration to the second version, loop.Write won't work, you'll have to write:
loop.Conn.Write(nil)
Type embedding is very powerful, but there's a number of gotcha's when you first get started. Thankfully, there's an entire paragraph explaining embedding on the effective go doc
Anyway, as stated, you are able to call functions, and access fields (if you're embedding a struct type instead of an interface). There is one thing, though: the field must be initialised correctly!
And that's where you went wrong: You still have to initialise your Conn part of the loopBack variable, otherwise what you're doing is the same as:
net.Conn(nil).Write(nil)
Which, naturally, results in a panic (nil pointer dereference)...
For example:
conn, err := net.Dial("tcp", "localhost:80")
if err != nil {
log.Fatalf("failed to dial localhost: %+v", err)
}
loop := loopBack{
Conn: conn,
}
loop.Write(nil) // same as conn.Write(nil)
Failing to set the net.Conn embedded field is akin to doing something like this:
s := make([]*int, 10) // make slice of 10 pointers to int
fmt.Println(len(s)) // outputs 10
*s[0]++ // add 1 to first element PANICS
The 10 elements in the slice exist, but they've all been initialised to nil
I'm using this to receive SNMP traps: https://github.com/soniah/gosnmp
Now, lets say I want to programmatically break out of the (taken from here):
err := tl.Listen("0.0.0.0:9162")
What are my best approaches to this?
I'm somewhat new to Golang and didnt find a way to break out of a goroutine that I have no way of modifying ("3rd party").
Thanks,
Short answer: You can't. There's no way to kill a goroutine (short of killing the entire program) from outside the goroutine.
Long answer: A goroutine can listen for some sort of "terminate" signal (via channels, signals, or any other mechanism). But ultimately, the goroutine must terminate from within.
Looking at the library in your example, it appears this functionality is not provided.
Standard https://golang.org/pkg/net/#Conn interface provides special methods SetDeadline (together with SetReadDeadline and SetWriteDeadline) to set a hard connection break time for staled connections. As I see in the source code:
type GoSNMP struct {
// Conn is net connection to use, typically established using GoSNMP.Connect()
Conn net.Conn
...
// Timeout is the timeout for the SNMP Query
Timeout time.Duration
...
net.Conn interface is exported - so you may try to get direct access to it to set up a deadline.
type TrapListener struct {
OnNewTrap func(s *SnmpPacket, u *net.UDPAddr)
Params *GoSNMP
...
}
In its turn TrapListener exports GoSNMP struct so you may have access to it. Try this:
tl := TrapListener{...}
tl.Params.Conn.SetDeadline(time.Now().Add(1*time.Second))
tl.Listen(...)
However this line disensures me - looks like it doesn't use stored connection and its options:
func (t *TrapListener) Listen(addr string) (err error) {
...
conn, err := net.ListenUDP("udp", udpAddr)
....
}
But you may try :)