在Go中实现一个获取远程机器文件的tcp rpc

简介

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响应

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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{}
}

这三个结构体作用分别是:

  1. 第一个:作为rpc服务, 我们所有的rpc服务函数都需要绑定这个结构体,如获取文件, 上传文件
  2. 第二个:作为请求体, 在rpc客户端调用我们的rpc服务函数时,需要实例化这个结构体,传入参数给我们, 我们也会在rpc服务函数中接收到这个参数
  3. 第三个:作为响应体, 在rpc客户端调用我们的rpc服务函数时,会接到我们返回的rpc响应结构体,我们在rpc服务函数中也需要实例化一个结构体,传入对应的参数, 返回给客户端

接着我们写一个处理函数, 处理客户端发来的获取文件请求

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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服务

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
	}
}

客户端

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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服务, 启动服务, 接收并处理客户端请求


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注