简介
rpc可以作为http或tcp层面的服务对外提供功能调用,主要分为server端和client端的代码, 今天我们来实现一个通过clinent端传送一个路径给rpc server,获取server的文件夹信息, 实现一个简单的tcp file server的功能
使用的包介绍
在go中,有2个rpc包, 一个为rpc, 一个为json rpc.
rpc包提供的rpc服务用gob编码传输, 只能使用go rpc client调用
json rpc包可以提供一个通用的json rpc服务端,方便跨编程语言的调用
因为我们今天的server和client都是go编写,所以我们采用rpc包来编写
Server端代码
首先我们需要定义3个结构体, 一个为rpc服务, 一个为rpc请求, 一个为rpc响应
type Listener struct{}
type GetFileRequest struct {
Path string
}
type FileResult struct {
Type string
Data []map[string]interface{}
}
type Listener struct{}
type GetFileRequest struct {
Path string
}
type FileResult struct {
Type string
Data []map[string]interface{}
}
type Listener struct{} type GetFileRequest struct { Path string } type FileResult struct { Type string Data []map[string]interface{} }
这三个结构体作用分别是:
- 第一个:作为rpc服务, 我们所有的rpc服务函数都需要绑定这个结构体,如获取文件, 上传文件
- 第二个:作为请求体, 在rpc客户端调用我们的rpc服务函数时,需要实例化这个结构体,传入参数给我们, 我们也会在rpc服务函数中接收到这个参数
- 第三个:作为响应体, 在rpc客户端调用我们的rpc服务函数时,会接到我们返回的rpc响应结构体,我们在rpc服务函数中也需要实例化一个结构体,传入对应的参数, 返回给客户端
接着我们写一个处理函数, 处理客户端发来的获取文件请求
func (l *Listener) GetFile(req GetFileRequest, resp *FileResult) (err error) {
defer func() {
if _e := recover(); _e != nil {
err = fmt.Errorf("%s", _e)
}
}()
path := req.Path
var pathResult []map[string]interface{}
if _, _err := os.Stat(path); _err != nil {
err = fmt.Errorf("%s", err)
}
if err != nil {
resp.Data = nil
return err
}
fileInfo, _ := os.Stat(path)
if fileInfo.IsDir() {
resp.Type = "path"
fsSlice, _ := ioutil.ReadDir(path)
pathResult = []map[string]interface{}{}
if path != "/" {
pathResult = append(pathResult, map[string]interface{}{
"name": "..",
"size": "0",
"isDir": true,
"key": time.Now().Nanosecond(),
"owner": "",
"path": filepath.Dir(path),
})
}
if len(fsSlice) == 0 {
resp.Data = pathResult
return nil
}
for _, file := range fsSlice {
name := file.Name()
pathObj := map[string]interface{}{}
pathObj["name"] = name
pathObj["size"] = file.Size()
pathObj["isDir"] = file.IsDir()
pathObj["key"] = time.Now().Nanosecond()
abspath, _ := filepath.Abs(path)
if strings.HasSuffix(abspath, "/") {
pathObj["path"] = abspath + name
} else {
pathObj["path"] = abspath + "/" + name
}
sysinfo := file.Sys().(*syscall.Stat_t)
u := strconv.FormatUint(uint64(sysinfo.Uid), 10)
g := strconv.FormatUint(uint64(sysinfo.Gid), 10)
usr, _ := user.LookupId(u)
group, _ := user.LookupGroupId(g)
pathObj["owner"] = usr.Username + "/" + group.Name
pathObj["time"] = file.ModTime().Format("2006-01-02 15:04:05")
pathResult = append(pathResult, pathObj)
resp.Data = pathResult
}
} else {
resp.Type = "file"
file, _ := os.Open(path)
content, err := ioutil.ReadAll(file)
if err != nil {
return err
}
result := []map[string]interface{}{}
contentmap := map[string]interface{}{}
contentmap["content"] = content
result = append(result, contentmap)
resp.Data = result
}
return nil
}
func (l *Listener) GetFile(req GetFileRequest, resp *FileResult) (err error) {
defer func() {
if _e := recover(); _e != nil {
err = fmt.Errorf("%s", _e)
}
}()
path := req.Path
var pathResult []map[string]interface{}
if _, _err := os.Stat(path); _err != nil {
err = fmt.Errorf("%s", err)
}
if err != nil {
resp.Data = nil
return err
}
fileInfo, _ := os.Stat(path)
if fileInfo.IsDir() {
resp.Type = "path"
fsSlice, _ := ioutil.ReadDir(path)
pathResult = []map[string]interface{}{}
if path != "/" {
pathResult = append(pathResult, map[string]interface{}{
"name": "..",
"size": "0",
"isDir": true,
"key": time.Now().Nanosecond(),
"owner": "",
"path": filepath.Dir(path),
})
}
if len(fsSlice) == 0 {
resp.Data = pathResult
return nil
}
for _, file := range fsSlice {
name := file.Name()
pathObj := map[string]interface{}{}
pathObj["name"] = name
pathObj["size"] = file.Size()
pathObj["isDir"] = file.IsDir()
pathObj["key"] = time.Now().Nanosecond()
abspath, _ := filepath.Abs(path)
if strings.HasSuffix(abspath, "/") {
pathObj["path"] = abspath + name
} else {
pathObj["path"] = abspath + "/" + name
}
sysinfo := file.Sys().(*syscall.Stat_t)
u := strconv.FormatUint(uint64(sysinfo.Uid), 10)
g := strconv.FormatUint(uint64(sysinfo.Gid), 10)
usr, _ := user.LookupId(u)
group, _ := user.LookupGroupId(g)
pathObj["owner"] = usr.Username + "/" + group.Name
pathObj["time"] = file.ModTime().Format("2006-01-02 15:04:05")
pathResult = append(pathResult, pathObj)
resp.Data = pathResult
}
} else {
resp.Type = "file"
file, _ := os.Open(path)
content, err := ioutil.ReadAll(file)
if err != nil {
return err
}
result := []map[string]interface{}{}
contentmap := map[string]interface{}{}
contentmap["content"] = content
result = append(result, contentmap)
resp.Data = result
}
return nil
}
func (l *Listener) GetFile(req GetFileRequest, resp *FileResult) (err error) { defer func() { if _e := recover(); _e != nil { err = fmt.Errorf("%s", _e) } }() path := req.Path var pathResult []map[string]interface{} if _, _err := os.Stat(path); _err != nil { err = fmt.Errorf("%s", err) } if err != nil { resp.Data = nil return err } fileInfo, _ := os.Stat(path) if fileInfo.IsDir() { resp.Type = "path" fsSlice, _ := ioutil.ReadDir(path) pathResult = []map[string]interface{}{} if path != "/" { pathResult = append(pathResult, map[string]interface{}{ "name": "..", "size": "0", "isDir": true, "key": time.Now().Nanosecond(), "owner": "", "path": filepath.Dir(path), }) } if len(fsSlice) == 0 { resp.Data = pathResult return nil } for _, file := range fsSlice { name := file.Name() pathObj := map[string]interface{}{} pathObj["name"] = name pathObj["size"] = file.Size() pathObj["isDir"] = file.IsDir() pathObj["key"] = time.Now().Nanosecond() abspath, _ := filepath.Abs(path) if strings.HasSuffix(abspath, "/") { pathObj["path"] = abspath + name } else { pathObj["path"] = abspath + "/" + name } sysinfo := file.Sys().(*syscall.Stat_t) u := strconv.FormatUint(uint64(sysinfo.Uid), 10) g := strconv.FormatUint(uint64(sysinfo.Gid), 10) usr, _ := user.LookupId(u) group, _ := user.LookupGroupId(g) pathObj["owner"] = usr.Username + "/" + group.Name pathObj["time"] = file.ModTime().Format("2006-01-02 15:04:05") pathResult = append(pathResult, pathObj) resp.Data = pathResult } } else { resp.Type = "file" file, _ := os.Open(path) content, err := ioutil.ReadAll(file) if err != nil { return err } result := []map[string]interface{}{} contentmap := map[string]interface{}{} contentmap["content"] = content result = append(result, contentmap) resp.Data = result } return nil }
这个函数的功能, 如果客户端传输过来的req.Path是文件夹,那么我们遍历这个文件夹,把里面的文件属性传送给客户端, 如果是文件,那么我们把文件内容直接返回给客户端
然后我们就再编写main函数, 开始启动rpc服务
func main() {
rpc.Register(new(Listener))
listen, err := net.Listen("tcp", ":10000")
if err != nil {
log.Fatal(err)
}
defer listen.Close()
for {
conn, err := listen.Accept()
if err != nil {
log.Printf("[-] err client: %s", err.Error())
continue
}
go rpc.ServeConn(conn)
}
}
func main() {
rpc.Register(new(Listener))
listen, err := net.Listen("tcp", ":10000")
if err != nil {
log.Fatal(err)
}
defer listen.Close()
for {
conn, err := listen.Accept()
if err != nil {
log.Printf("[-] err client: %s", err.Error())
continue
}
go rpc.ServeConn(conn)
}
}
func main() { rpc.Register(new(Listener)) listen, err := net.Listen("tcp", ":10000") if err != nil { log.Fatal(err) } defer listen.Close() for { conn, err := listen.Accept() if err != nil { log.Printf("[-] err client: %s", err.Error()) continue } go rpc.ServeConn(conn) } }
客户端
package main
import (
"fmt"
"net/rpc"
)
type GetFileRequest struct {
Path string
}
type FileResult struct {
Type string
Data []map[string]interface{}
}
func main() {
conn, err := rpc.Dial("tcp", "10.0.9.201:10000")
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
req := GetFileRequest{Path: "/tmp"}
var result FileResult
err = conn.Call("Listener.GetFile", req, &result)
if err != nil {
fmt.Println(err)
return
}
if result.Type == "path" {
for _, i := range result.Data {
fmt.Println(i)
}
} else {
fmt.Println(string((result.Data[0]["content"].([]byte))))
}
}
package main
import (
"fmt"
"net/rpc"
)
type GetFileRequest struct {
Path string
}
type FileResult struct {
Type string
Data []map[string]interface{}
}
func main() {
conn, err := rpc.Dial("tcp", "10.0.9.201:10000")
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
req := GetFileRequest{Path: "/tmp"}
var result FileResult
err = conn.Call("Listener.GetFile", req, &result)
if err != nil {
fmt.Println(err)
return
}
if result.Type == "path" {
for _, i := range result.Data {
fmt.Println(i)
}
} else {
fmt.Println(string((result.Data[0]["content"].([]byte))))
}
}
package main import ( "fmt" "net/rpc" ) type GetFileRequest struct { Path string } type FileResult struct { Type string Data []map[string]interface{} } func main() { conn, err := rpc.Dial("tcp", "10.0.9.201:10000") if err != nil { fmt.Println(err) return } defer conn.Close() req := GetFileRequest{Path: "/tmp"} var result FileResult err = conn.Call("Listener.GetFile", req, &result) if err != nil { fmt.Println(err) return } if result.Type == "path" { for _, i := range result.Data { fmt.Println(i) } } else { fmt.Println(string((result.Data[0]["content"].([]byte)))) } }
获取文件夹运行结果:

获取文件运行结果

可以看到在go中编写rpc服务的套路, 定义rpc服务结构体, 以及不同的rpc请求或rpc相应结构体, 编写不同的处理函数,接着注册rpc服务, 启动服务, 接收并处理客户端请求
发表回复