Go语言学习笔记(十二)Tcp实现Rpc

1.Tcp实现Rpc:

既然可以直接利用Tcp传递消息,相对于Http来说便不在需要相关处理..同时利用Http实现Rpc时,服务端可以自动接管网络连接.然后负责对象连接管理.Tcp只能自行处理.

1.1服务端:

go 复制代码
package main

import (
	"errors"
	"fmt"
	"net"
	"net/rpc"
)

func main() {
	rpc.Register(new(UserHandler))
	listen, err := net.Listen("tcp", "127.0.0.1:8090")

	if err != nil {
		fmt.Println("监听tcp端口出错")
		return
	}

	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("建立tcp连接失败")
			continue
		}

		go func(conn net.Conn) {
			rpc.ServeConn(conn)
		}(conn)
	}
}

type UserHandler struct {
}

type Userdetail struct {
	Id       int
	Username string
	Nickname string
}

// 获取用户信息的方法绑定到结构体
func (handler *UserHandler) GetUserInfo(name string, user *Userdetail) (err error) {
	fmt.Println("try to get user info by %s", name)

	//校验入参.
	if len(name) == 0 {
		return errors.New("the user name is empty")
	}

	user.Id = 001
	user.Username = "itbo"
	user.Nickname = "尊敬的itbo"

	return nil
}

1.2客户端:

go 复制代码
package main

import (
	"fmt"
	"net/rpc"
)

func main() {
	//利用Tcp连接远程服务端.
	client, err := rpc.Dial("tcp", "127.0.0.1:8090")
	if err != nil {
		fmt.Println("与服务端建立连接出错")
		return
	}

	defer client.Close()

	user := new(Userdetail)
	client.Call("UserHandler.GetUserInfo", "golang", user)

	if err != nil {
		fmt.Println("获取用户信息出错", err)
		return
	}
	//打印获取的信息
	fmt.Printf("用户id:%+v\n", user.Id)
	fmt.Printf("用户名:%+v\n", user.Username)
	fmt.Printf("用户昵称:%+v\n", user.Nickname)

}

1.3执行结果:

1.4Http和Tcp实现Rpc区别:

Tcp实现的服务端代码失去了rpc.HandleHttp和http.Serve这两个函数的调用.其中rpc.HandleHttp用于注册默认的url和处理函数的映射关系.

打开http.service源码可以看到.

通过这里接收连接然后去处理.因为没有调用这个函数.所以tcp需要自己去处理连接.

2.Json格式实现Rpc通信:

2.1服务端:

go 复制代码
package main

import (
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"
)

func main() {
	rpc.Register(new(UserHandler))
	listen, err := net.Listen("tcp", "127.0.0.1:8090")

	http.Serve(listen, nil)
	if err != nil {
		fmt.Println("监听tcp端口出错")
		return
	}

	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("建立tcp连接失败")
			continue
		}

		go func(conn net.Conn) {
			//利用jsonRpc处理连接.
			jsonrpc.ServeConn(conn)
		}(conn)
	}
}

type UserHandler struct {
}

type Userdetail struct {
	Id       int
	Username string
	Nickname string
}

// 获取用户信息的方法绑定到结构体
func (handler *UserHandler) GetUserInfo(name string, user *Userdetail) (err error) {
	fmt.Println("try to get user info by %s", name)

	//校验入参.
	if len(name) == 0 {
		return errors.New("the user name is empty")
	}

	user.Id = 001
	user.Username = "itbo"
	user.Nickname = "尊敬的itbo"

	return nil
}

2.2jsonrpc.ServeConn源码:

scss 复制代码
func ServeConn(conn io.ReadWriteCloser) {
	rpc.ServeCodec(NewServerCodec(conn))
}

2.3NewServerCodec源码:

可以看到底层有一个编码器和解码器.

go 复制代码
func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
	return &serverCodec{
		dec:     json.NewDecoder(conn),
		enc:     json.NewEncoder(conn),
		c:       conn,
		pending: make(map[uint64]*json.RawMessage),
	}
}

2.4客户端:

go 复制代码
package main

import (
	"fmt"
	"net/rpc"
	"net/rpc/jsonrpc"
)

func main() {
	//利用Tcp连接远程服务端.
	//client, err := rpc.Dial("tcp", "127.0.0.1:8090")
	client, err := jsonrpc.Dial("tcp", "127.0.0.1:8090")
	if err != nil {
		fmt.Println("与服务端建立连接出错")
		return
	}

	defer client.Close()

	user := new(Userdetail)
	client.Call("UserHandler.GetUserInfo", "golang", user)

	if err != nil {
		fmt.Println("获取用户信息出错", err)
		return
	}
	//打印获取的信息
	fmt.Printf("用户id:%+v\n", user.Id)
	fmt.Printf("用户名:%+v\n", user.Username)
	fmt.Printf("用户昵称:%+v\n", user.Nickname)
}
相关推荐
陈随易9 小时前
FFmpeg 9.0 发布,代号 Lei,音视频处理再升级
前端·后端·程序员
凌虚12 小时前
面向 MySQL 用户的 PostgreSQL 快速上手指南
数据库·后端·架构
IT_陈寒14 小时前
Vite静态资源引用这个坑我踩得有点疼
前端·人工智能·后端
前端开发张小七14 小时前
Java 学习笔记 · 第一课:基础语法与核心概念
java·后端
海盗123414 小时前
微软技术周报 ——2026-08-03
后端·python·microsoft·c#·.netcore
沙湖遇雨14 小时前
6. Pipeline 的生命周期
后端
青青子衿悠悠我心14 小时前
TRAE Work让财务月底对账3小时变5分钟
后端
创安电气研究所15 小时前
用TypeScript给Modbus设备做一层适配器
后端
妙码生花15 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(五十):增加管理员角色组管理
前端·后端·ai编程
妙码生花15 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(五十一):管理员和角色组的关联
前端·后端·ai编程