Golang中如何通过net/rpc获取客户端的真实IP
Golang中如何通过net/rpc获取客户端的真实IP
我有一个需求,需要RPC服务器获取客户端的真实IP和当前连接的net.Conn。
我过去通过以下示例方法获取:
// example_server.go
type MathService struct{}
type Args struct {
A, B int
}
type Reply struct {
Result int
}
func (m *MathService) Multiply(args *Args, reply *Reply) error {
reply.Result = args.A * args.B
return nil
}
func main() {
// Create an instance of the MathService
mathService := new(MathService)
// Register MathService for RPC
rpc.Register(mathService)
// Create a TCP listener
listener, err := net.Listen("tcp", "0.0.0.0:1234")
if err != nil {
fmt.Println("Error starting server:", err)
return
}
defer listener.Close()
fmt.Println("Server listening on :1234")
for {
// Accept incoming connections
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
continue
}
fmt.Printf("remote ip addr: %s\n", conn.RemoteAddr().String())
// Serve the connection in a new goroutine
go rpc.ServeConn(conn)
}
}
// example_client.go
type Args struct {
A, B int
}
type Reply struct {
Result int
}
func main() {
// Connect to the server
client, err := rpc.Dial("tcp", "127.0.0.1:1234")
if err != nil {
fmt.Println("Error connecting to server:", err)
return
}
defer client.Close()
// Prepare the arguments for the RPC call
args := &Args{A: 5, B: 3}
// Call the Multiply method remotely
var reply Reply
err = client.Call("MathService.Multiply", args, &reply)
if err != nil {
fmt.Println("Error calling Multiply:", err)
return
}
// Print the result
fmt.Printf("Result: %d\n", reply.Result)
}
但是RemoteAddr方法获取的并不一定是客户端的真实IP。
因此,我希望能够手动调用ReportRealIP函数来报告真实IP,但我无法在RPC函数中获取到当前连接的net.Conn。
我搜索了一些资料,但没有找到实现我需求的方法。是否有办法获取到我想要的这两个变量?期待您的回复。
需要真实IP和
net.Conn的原因大致是为了双向通信。我希望能够向特定IP的客户端发送RPC请求以执行某些操作。例如,向某些Agent服务发出RPC命令来执行某个脚本。
更多关于Golang中如何通过net/rpc获取客户端的真实IP的实战教程也可以访问 https://www.itying.com/category-94-b0.html
稍微回顾一下:
moluzhui:
需要真实IP和
net.Conn的原因大致是为了双向通信。我希望能够向特定IP的客户端发送RPC请求以执行某些操作。例如,向某些Agent服务发出RPC命令来执行某个脚本。
你确定RPC是这里适合的工具吗?如果你想要的只是双向通信,或许可以使用WebSocket。好处包括:你可能已经熟悉标准库的Web相关功能,因此上手会容易得多。看看我为了帮同事验证概念而用 gorilla/mux 做的这个演示:
GitHub - DeanPDX/go-websocket-job-runner: A websocket job-runner concept…
一个WebSocket任务运行器概念应用。通过在GitHub上创建账户来为DeanPDX/go-websocket-job-runner的开发做出贡献。
另外,gRPC和协议缓冲区可能也是一个很好的用例:
gRPC
一个高性能、开源的通用RPC框架
然后你可以使用这个方法来获取IP:
Is there any guide to get the client IP address and user-agent?
请让我知道这里是否可以提问,对此表示抱歉。
What version of gRPC are you using?
I’m using v1.12.0
What version of Go are you using (
go version)?I’m using v1.10.2
What operating system (Linux, Windows, …) and version?
I’m using macOS
What did you do?
If possible, provide a recipe for reproducing the error. I’m setting up a serials of grpc calls following your official guides which all work fine. but now i’m required to get the client IP address and user-agent. Unfortunately I don’t see any pages that introducing how to get it out.
What did you expect to see?
I expect to see a page giving an example how to get the client IP address and user-agent.
What did you see instead?
nothing.
无论如何,只是一些想法。我对net/rpc不是特别熟悉。大多数时候当我需要类似功能时,我使用的是gRPC和protobuf。
更多关于Golang中如何通过net/rpc获取客户端的真实IP的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go的net/rpc包中,确实没有直接的方法在RPC方法中获取当前连接的net.Conn。不过,可以通过自定义编解码器来实现这个需求。下面是一个完整的示例:
package main
import (
"encoding/gob"
"fmt"
"io"
"net"
"net/rpc"
"sync"
)
// 自定义连接结构,包装net.Conn并存储客户端IP
type wrappedConn struct {
net.Conn
clientIP string
}
// 自定义RPC请求结构
type request struct {
ClientIP string
ServiceMethod string
Seq uint64
Next interface{}
}
// 自定义编解码器
type serverCodec struct {
dec *gob.Decoder
enc *gob.Encoder
c *wrappedConn
mu sync.Mutex
pending map[uint64]*request
}
func newServerCodec(conn net.Conn) *serverCodec {
wrapped := &wrappedConn{
Conn: conn,
clientIP: conn.RemoteAddr().String(),
}
return &serverCodec{
dec: gob.NewDecoder(conn),
enc: gob.NewEncoder(conn),
c: wrapped,
pending: make(map[uint64]*request),
}
}
func (c *serverCodec) ReadRequestHeader(r *rpc.Request) error {
var req request
if err := c.dec.Decode(&req); err != nil {
return err
}
r.ServiceMethod = req.ServiceMethod
r.Seq = req.Seq
// 存储请求,以便在ReadRequestBody中获取客户端IP
c.mu.Lock()
c.pending[r.Seq] = &req
c.mu.Unlock()
return nil
}
func (c *serverCodec) ReadRequestBody(body interface{}) error {
if body == nil {
return nil
}
// 从pending中获取当前请求的客户端IP
var req *request
c.mu.Lock()
// 这里需要知道当前的Seq,但net/rpc没有直接提供
// 我们可以通过其他方式传递,这里简化处理
for _, r := range c.pending {
req = r
break
}
c.mu.Unlock()
if req != nil {
// 将客户端IP注入到body中(如果body有相应字段)
if withIP, ok := body.(interface{ SetClientIP(string) }); ok {
withIP.SetClientIP(req.ClientIP)
}
if withConn, ok := body.(interface{ SetConn(*wrappedConn) }); ok {
withConn.SetConn(c.c)
}
}
return c.dec.Decode(body)
}
func (c *serverCodec) WriteResponse(r *rpc.Response, body interface{}) error {
c.mu.Lock()
delete(c.pending, r.Seq)
c.mu.Unlock()
if err := c.enc.Encode(r); err != nil {
return err
}
return c.enc.Encode(body)
}
func (c *serverCodec) Close() error {
return c.c.Close()
}
// 客户端编解码器
type clientCodec struct {
dec *gob.Decoder
enc *gob.Encoder
c io.ReadWriteCloser
mu sync.Mutex
pending map[uint64]string
}
func newClientCodec(conn io.ReadWriteCloser) *clientCodec {
return &clientCodec{
dec: gob.NewDecoder(conn),
enc: gob.NewEncoder(conn),
c: conn,
pending: make(map[uint64]string),
}
}
func (c *clientCodec) WriteRequest(r *rpc.Request, body interface{}) error {
// 在请求头中发送客户端IP
req := request{
ClientIP: "真实IP地址", // 这里可以设置真实的客户端IP
ServiceMethod: r.ServiceMethod,
Seq: r.Seq,
Next: body,
}
c.mu.Lock()
c.pending[r.Seq] = req.ClientIP
c.mu.Unlock()
if err := c.enc.Encode(req); err != nil {
return err
}
return c.enc.Encode(body)
}
func (c *clientCodec) ReadResponseHeader(r *rpc.Response) error {
return c.dec.Decode(r)
}
func (c *clientCodec) ReadResponseBody(body interface{}) error {
if body == nil {
return nil
}
return c.dec.Decode(body)
}
func (c *clientCodec) Close() error {
return c.c.Close()
}
// 服务端实现
type MathService struct {
connections map[string]*wrappedConn
mu sync.RWMutex
}
type Args struct {
A, B int
clientIP string
conn *wrappedConn
}
func (a *Args) SetClientIP(ip string) {
a.clientIP = ip
}
func (a *Args) SetConn(conn *wrappedConn) {
a.conn = conn
}
type Reply struct {
Result int
}
func (m *MathService) Multiply(args *Args, reply *Reply) error {
fmt.Printf("客户端真实IP: %s\n", args.clientIP)
fmt.Printf("连接信息: %v\n", args.conn.RemoteAddr())
// 存储连接以便后续使用
m.mu.Lock()
if m.connections == nil {
m.connections = make(map[string]*wrappedConn)
}
m.connections[args.clientIP] = args.conn
m.mu.Unlock()
reply.Result = args.A * args.B
return nil
}
// 向特定客户端发送消息的方法
func (m *MathService) SendToClient(ip string, message string) error {
m.mu.RLock()
conn, exists := m.connections[ip]
m.mu.RUnlock()
if !exists {
return fmt.Errorf("客户端 %s 未连接", ip)
}
// 这里可以通过conn向客户端发送数据
_, err := conn.Write([]byte(message))
return err
}
func main() {
// 创建服务实例
mathService := &MathService{}
rpc.Register(mathService)
// 启动TCP监听
listener, err := net.Listen("tcp", "0.0.0.0:1234")
if err != nil {
fmt.Println("Error starting server:", err)
return
}
defer listener.Close()
fmt.Println("Server listening on :1234")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
continue
}
// 使用自定义编解码器
codec := newServerCodec(conn)
go rpc.ServeCodec(codec)
}
}
客户端代码:
package main
import (
"fmt"
"net/rpc"
)
type Args struct {
A, B int
}
type Reply struct {
Result int
}
func main() {
// 连接到服务器
conn, err := net.Dial("tcp", "127.0.0.1:1234")
if err != nil {
fmt.Println("Error connecting to server:", err)
return
}
defer conn.Close()
// 使用自定义编解码器
client := rpc.NewClientWithCodec(newClientCodec(conn))
defer client.Close()
// 准备参数
args := &Args{A: 5, B: 3}
// 调用远程方法
var reply Reply
err = client.Call("MathService.Multiply", args, &reply)
if err != nil {
fmt.Println("Error calling Multiply:", err)
return
}
fmt.Printf("Result: %d\n", reply.Result)
}
这个实现的关键点:
- 自定义
wrappedConn结构包装原始连接,可以存储客户端真实IP - 自定义
serverCodec和clientCodec编解码器,在RPC消息中传递客户端IP信息 - 在
Args结构中添加SetClientIP和SetConn方法,用于接收服务器端注入的连接信息 - 服务器端维护连接映射,可以通过IP地址找到对应的连接进行双向通信
这样你就可以在RPC方法中获取到客户端的真实IP和当前的net.Conn,并实现向特定客户端发送消息的功能。

