文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Golang pipe在不同场景下怎么远程交互

2023-07-05 10:08

关注

这篇“Golang pipe在不同场景下怎么远程交互”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Golang pipe在不同场景下怎么远程交互”文章吧。

Pipe介绍

pipe实现从一个进程重定向至另一个进程,它是双向数据通道,用于实现进行间通信。

io.Pipe函数创建内存同步通道,用于连接io.Reader和io.Writer. 本文示例使用环境为:

go version
go version go1.19.3 linux/amd64

Go pipe简单示例

在实现远程交互之前,先看下面简单示例,演示如何使用io.Pipe函数:

package mainimport (    "fmt"    "io"    "log"    "os")func main() {    r, w := io.Pipe()    go func() {        fmt.Fprint(w, "Hello there\n")        w.Close()    }()    _, err := io.Copy(os.Stdout, r)    if err != nil {        log.Fatal(err)    }}

首先创建pipe,然后在协程中给管道的writer写数据,然后使用io.Copy函数从管道Reader中拷贝数据至标准输出:

go func() {    fmt.Fprint(w, "Hello there\n")    w.Close()}()

在协程中写数据是因为每次写PipeWriter都阻塞直到PipeReader完全消费了数据。

运行程序:

go run main.go 
Hello there

通过这个简单示例,展示了管道重定向能力,了解这个基本原理后,下面先看Shell命令的管道,最终我们的目标是通过WEB方式实现远程命令行交互。

Go cmd StdoutPipe

当命令启动时,Cmd的StdoutPipe返回管道连接命令的标准输出:

package mainimport (    "bufio"    "fmt"    "log"    "os"    "os/exec")func main() {    cmd := exec.Command("ping", "www.baidu.com")    stdout, err := cmd.StdoutPipe()    if err != nil {        log.Fatal(err)    }    cmd.Start()    buf := bufio.NewReader(stdout)     num := 0    for {        line, _, _ := buf.ReadLine()        if num > 3 {            os.Exit(0)        }        num += 1        fmt.Println(string(line))    }}

上面代码启动ping命令,然后从其输出中读取4行. 这行代码启动ping命令:

    cmd := exec.Command("ping", "www.baidu.com")
    stdout, err := cmd.StdoutPipe()
    buf := bufio.NewReader(stdout) 

接着获取命令的标准输出,并保存输出之buf中。下面从缓冲中读取4行:

for {    line, _, _ := buf.ReadLine()    if num > 3 {        os.Exit(0)    }    num += 1    fmt.Println(string(line))}

读取4行并输出到控制台,运行程序,输出结果如下:

go run main.go
PING www.a.shifen.com (180.101.50.188) 56(84) bytes of data.
64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=1 ttl=53 time=12.0 ms
64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=2 ttl=53 time=11.2 ms
64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=3 ttl=53 time=10.5 ms

通过这个示例,成功地把命令的执行结果捕获到buffer中,并能够增加处理逻辑再输出到控制台。

http请求处理中使用管道

下面示例展示在http请求处理中使用管道。运行date命令,通过HTTP输出结果,可以实现元从查看命令执行结果。

package mainimport (    "fmt"    "io"    "net/http"    "os/exec")func handler(w http.ResponseWriter, r *http.Request) {    cmd := exec.Command("date")    pr, pw := io.Pipe()    defer pw.Close()    cmd.Stdout = pw    cmd.Stderr = pw    go io.Copy(w, pr)    cmd.Run()}func main() {    http.HandleFunc("/", handler)    fmt.Println("server started on port 8080")    http.ListenAndServe(":8080", nil)}

关键代码为创建管道,并把PipeWriter赋给命令的标准输出和标准错误。

cmd := exec.Command("date")
pr, pw := io.Pipe()
defer pw.Close()
cmd.Stdout = pw
cmd.Stderr = pw

go io.Copy(w, pr)

然后在协程中拷贝PipeReader至http.ResponseWriter.最后运行程序查看结果:

$ go run handler.go 
server started on port 8080

使用curl或浏览器访问地址:localhost:8080,可以看到:

2023年 02月 22日 星期三 17:06:11 CST

修改上面程序,把命令作为参数,即可实现远程交互。下面我们看看如何利用管道给输入端写入数据,包括http请求和命令的标准输入。

利用管道提交post请求json数据

下面示例给https://httpbin.org/post请求地址提交json数据作为请求体。

package mainimport (    "encoding/json"    "fmt"    "io"    "io/ioutil"    "log"    "net/http")type PayLoad struct {    Content string}func main() {    r, w := io.Pipe()    go func() {        defer w.Close()        err := json.NewEncoder(w).Encode(&PayLoad{Content: "Hello there!"})        if err != nil {            log.Fatal(err)        }    }()    resp, err := http.Post("https://httpbin.org/post", "application/json", r)    if err != nil {        log.Fatal(err)    }    body, err := ioutil.ReadAll(resp.Body)    if err != nil {        log.Fatal(err)    }    fmt.Println(string(body))}

上面示例实现给post请求提交json数据,并读取响应内容。

首先定义管道,然后在协程中给管道Writer写入json数据:

go func() {    defer w.Close()    err := json.NewEncoder(w).Encode(&PayLoad{Content: "Hello there!"})    if err != nil {        log.Fatal(err)    }}()

然后把管道Reader作为参数传入请求:

resp, err := http.Post("https://httpbin.org/post", "application/json", r)

最后读取响应内容:

body, err := ioutil.ReadAll(resp.Body)if err != nil {    log.Fatal(err)}fmt.Println(string(body))

运行程序输出结果:

go run main.go
{
  "args": {}, 
  "data": "{\"Content\":\"Hello there!\"}\n", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "Transfer-Encoding": "chunked", 
    "User-Agent": "Go-http-client/2.0", 
    "X-Amzn-Trace-Id": "Root=1-63f5c8c6-4a14ee9a2dc14e352f234fae"
  }, 
  // 省略...
}

通过管道读标准输入

下面示例利用管道从标准输入读取数据,并打印数据及数据字节数、块数:

package mainimport (    "bufio"    "fmt"    "io"    "log"    "os")func main() {    nBytes, nChunks := int64(0), int64(0)    r := bufio.NewReader(os.Stdin)    buf := make([]byte, 0, 4*1024)    for {        n, err := r.Read(buf[:cap(buf)])        buf = buf[:n]        if n == 0 {            if err == nil {                continue            }            if err == io.EOF {                break            }            log.Fatal(err)        }        nChunks++        nBytes += int64(len(buf))        fmt.Println(string(buf))        if err != nil && err != io.EOF {            log.Fatal(err)        }    }    fmt.Println("Bytes:", nBytes, "Chunks:", nChunks)}

首先定义包装标准输入Reader:

r := bufio.NewReader(os.Stdin)buf := make([]byte, 0, 4*1024)n, err := r.Read(buf[:cap(buf)])buf = buf[:n]nChunks++nBytes += int64(len(buf))fmt.Println(string(buf))

然后创建4kb缓冲区,从标准输入读数据至缓冲区。然后计算块数和字节数,最后答应缓冲区内容。

date | go run main.go
2023年 02月 22日 星期三 16:08:17 CST

Bytes: 43 Chunks: 1

这里通过|操作传递date命令的输出结果,显示内容与预期一致。

Go Stat

Stat函数返回FileInfo结构体,描述文件信息。我们可以利用其检查数据是否来自终端。

package mainimport (    "bufio"    "fmt"    "log"    "os")func main() {    stat, _ := os.Stdin.Stat()    if (stat.Mode() & os.ModeCharDevice) == 0 {        var buf []byte        scanner := bufio.NewScanner(os.Stdin)        for scanner.Scan() {            buf = append(buf, scanner.Bytes()...)        }        if err := scanner.Err(); err != nil {            log.Fatal(err)        }        fmt.Printf("Hello %s!\n", buf)    } else {        fmt.Print("Enter your name: ")        var name string        fmt.Scanf("%s", &name)        fmt.Printf("Hello %s!\n", name)    }}

这个示例数据可能来自终端或管道。为了判断,通过下面代码获取stat:

stat, _ := os.Stdin.Stat()

获取到标准输入的FileInfo结构体后进行判断:

if (stat.Mode() & os.ModeCharDevice) == 0 {

这行判断数据来自管道,反之则为终端。即如果没有管道提供数据,则提示用户输入数据。运行程序:

$ echo "golang" | go run main.go
Hello golang!

$go run main.go
Enter your name: java
Hello java!

以上就是关于“Golang pipe在不同场景下怎么远程交互”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯