在 Go 中使用 HTTP 连接时,可能存在开发人员需要访问底层网络的场景插座。 net/http 包提供了一种处理 HTTP 请求和响应的全面方法,但它不直接公开底层套接字。以下是如何在 Go 程序中检索套接字。
随着 Go 1.13 的发布,net/http 包引入了存储请求上下文的功能请求上下文中的 net.Conn。这提供了一种方便而干净的方式来访问套接字:
<code class="go">package main import ( "context" "net/http" "net" "log" ) type contextKey struct { key string } var ConnContextKey = &contextKey{"http-conn"} func SaveConnInContext(ctx context.Context, c net.Conn) (context.Context) { return context.WithValue(ctx, ConnContextKey, c) } func GetConn(r *http.Request) (net.Conn) { return r.Context().Value(ConnContextKey).(net.Conn) } func main() { http.HandleFunc("/", myHandler) server := http.Server{ Addr: ":8080", ConnContext: SaveConnInContext, } server.ListenAndServe() } func myHandler(w http.ResponseWriter, r *http.Request) { conn := GetConn(r) ... }</code>
对于侦听 TCP 端口的服务器,net.Conn.RemoteAddr().String( ) 对于每个连接都是唯一的。因此,它可以用作全局连接映射的键:
<code class="go">package main import ( "net/http" "net" "fmt" "log" ) var conns = make(map[string]net.Conn) func ConnStateEvent(conn net.Conn, event http.ConnState) { if event == http.StateActive { conns[conn.RemoteAddr().String()] = conn } else if event == http.StateHijacked || event == http.StateClosed { delete(conns, conn.RemoteAddr().String()) } } func GetConn(r *http.Request) (net.Conn) { return conns[r.RemoteAddr] } func main() { http.HandleFunc("/", myHandler) server := http.Server{ Addr: ":8080", ConnState: ConnStateEvent, } server.ListenAndServe() } func myHandler(w http.ResponseWriter, r *http.Request) { conn := GetConn(r) ... }</code>
对于在 UNIX 套接字上侦听的服务器,net.Conn.RemoteAddr() .String() 始终返回“@”。要使其正常工作,您可以重写 net.Listener.Accept() 并使用它来重写 net.Conn.RemoteAddr().String()。 下面是一个示例:
<code class="go">package main import ( "net/http" "net" "os" "golang.org/x/sys/unix" "fmt" "log" ) func main() { http.HandleFunc("/", myHandler) listenPath := "/var/run/go_server.sock" l, err := NewUnixListener(listenPath) if err != nil { log.Fatal(err) } defer os.Remove(listenPath) server := http.Server{ ConnState: ConnStateEvent, } server.Serve(NewConnSaveListener(l)) } func myHandler(w http.ResponseWriter, r *http.Request) { conn := GetConn(r) if unixConn, isUnix := conn.(*net.UnixConn); isUnix { f, _ := unixConn.File() pcred, _ := unix.GetsockoptUcred(int(f.Fd()), unix.SOL_SOCKET, unix.SO_PEERCRED) f.Close() log.Printf("Remote UID: %d", pcred.Uid) } } var conns = make(map[string]net.Conn) type connSaveListener struct { net.Listener } func NewConnSaveListener(wrap net.Listener) (net.Listener) { return connSaveListener{wrap} } func (self connSaveListener) Accept() (net.Conn, error) { conn, err := self.Listener.Accept() ptrStr := fmt.Sprintf("%d", &conn) conns[ptrStr] = conn return remoteAddrPtrConn{conn, ptrStr}, err } func GetConn(r *http.Request) (net.Conn) { return conns[r.RemoteAddr] } func ConnStateEvent(conn net.Conn, event http.ConnState) { if event == http.StateHijacked || event == http.StateClosed { delete(conns, conn.RemoteAddr().String()) } } type remoteAddrPtrConn struct { net.Conn ptrStr string } func (self remoteAddrPtrConn) RemoteAddr() (net.Addr) { return remoteAddrPtr{self.ptrStr} } type remoteAddrPtr struct { ptrStr string } func (remoteAddrPtr) Network() (string) { return "" } func (self remoteAddrPtr) String() (string) { return self.ptrStr } func NewUnixListener(path string) (net.Listener, error) { if err := unix.Unlink(path); err != nil & os.IsNotExist(err) { return nil, err } mask := unix.Umask(0777) defer unix.Umask(mask) l, err := net.Listen("unix", path) if err != nil { return nil, err } if err := os.Chmod(path, 0660); err != nil { l.Close() return nil, err } return l, nil }</code>
可以使用上述方法来访问 http.ResponseWriter 的底层套接字。首选方法取决于具体要求和所使用的 Go 版本。
以上是如何访问 net/http 响应的底层套接字?的详细内容。更多信息请关注PHP中文网其他相关文章!