需要帮助来了解libchan的工作原理

时间:2018-06-24 22:00:31

标签: go libchan

我正在尝试使用libchan库通过类似go channel的传输在机器之间发送消息。

根据我收集到的信息,大概是这样的:

  1. 您有一个SPDY客户端,该客户端通过tcp将序列化的命令对象发送到地址。此命令对象包含一个名为Pipe的libchan通道,通过该通道发送响应。
  2. 服务器接收到传入连接时,它将等待命令对象。当它得到一个时,它将通过对象中包含的Pipe发送响应。

这是我的困惑点。为了使通道在两台计算机之间持久存在,它们必须共享内存或至少共享连接它们两者的抽象。从我对libchan代码库的迷惑中,我不知道这怎么可能。

以下是仓库中示例的摘录:

// client

    receiver, remoteSender := libchan.Pipe()
    command := &RemoteCommand{
        Cmd:        os.Args[1],
        Args:       os.Args[2:],
        Stdin:      os.Stdin,
        Stdout:     os.Stdout,
        Stderr:     os.Stderr,
        StatusChan: remoteSender,
    }

    err = sender.Send(command)
    if err != nil {
        log.Fatal(err)
    }
    err = receiver.Receive(response)
    if err != nil {
        log.Fatal(err)
    }

    os.Exit(response.Status)

和服务器:

// server
t := spdy.NewTransport(p)

        go func() {
            for {
                receiver, err := t.WaitReceiveChannel()
                if err != nil {
                    log.Print("receiver error")
                    log.Print(err)
                    break
                }
                log.Print("about to spawn receive proc")
                go func() {
                    for {
                        command := &RemoteReceivedCommand{}
                        err := receiver.Receive(command)
                        returnResult := &CommandResponse{}
                        if res != nil {
                            if exiterr, ok := res.(*exec.ExitError); ok {
                                returnResult.Status = exiterr.Sys(). 
                              (syscall.WaitStatus).ExitStatus()
                            } else {
                                log.Print("res")
                                log.Print(res)
                                returnResult.Status = 10
                            }
                        }
                        err = command.StatusChan.Send(returnResult)

我要磨练的重点在这里:

libchan.Pipe()

根据来源,这将返回一个频道。一个引用保留在客户端上,而另一个则发送到服务器。然后使用此通道将值从后者传递到前者。实际上这是如何工作的?

clientserver的完整代码

1 个答案:

答案 0 :(得分:0)

首先,很高兴知道Pipe()所做的所有事情都是建立一个通道并返回内存中的发送者/接收者对。

来自inmem.go

// Pipe returns an inmemory Sender/Receiver pair.
func Pipe() (Receiver, Sender) {
    c := make(chan interface{})
    return pReceiver(c), pSender(c)
}

然后,您可以在inmem_test.go中查看一个简单的端到端示例。

This struct相当于演示中的RemoteCommand

type InMemMessage struct {
    Data   string
    Stream io.ReadWriteCloser
    Ret    Sender
}

TestInmemRetPipe()中,创建了一个简单的客户端和服务器。

The client使用Pipe()创建本地发送者/接收者对,而the server仅在InMemMessage结构中使用libchan.Sender interface

请注意,客户端和服务器是分别接收发送方或接收方作为参数的函数。在下一个代码片段中对此有更多的了解。

func TestInmemRetPipe(t *testing.T) {
    client := func(t *testing.T, w Sender) {
        ret, retPipe := Pipe()
        message := &InMemMessage{Data: "hello", Ret: retPipe}

        err := w.Send(message)
        if err != nil {
            t.Fatal(err)
        }
        msg := &InMemMessage{}
        err = ret.Receive(msg)
        if err != nil {
            t.Fatal(err)
        }

        if msg.Data != "this better not crash" {
            t.Fatalf("%#v", msg)
        }

    }
    server := func(t *testing.T, r Receiver) {
        msg := &InMemMessage{}
        err := r.Receive(msg)
        if err != nil {
            t.Fatal(err)
        }

        if msg.Data != "hello" {
            t.Fatalf("Wrong message:\n\tExpected: %s\n\tActual: %s", "hello", msg.Data)
        }
        if msg.Ret == nil {
            t.Fatal("Message Ret is nil")
        }

        message := &InMemMessage{Data: "this better not crash"}
        if err := msg.Ret.Send(message); err != nil {
            t.Fatal(err)
        }
    }
    SpawnPipeTestRoutines(t, client, server)

}

SpawnPipeTestRoutines()执行客户端和服务器功能。在此功能中,通过Pipe()实例化了另一个发送方/接收方空气。

在演示应用程序中,Pipe()在此处执行的功能(即促进客户端和服务器实例之间的通信)由网络通信处理。

func SpawnPipeTestRoutines(t *testing.T, s SendTestRoutine, r ReceiveTestRoutine) {
    end1 := make(chan bool)
    end2 := make(chan bool)

    receiver, sender := Pipe()

    go func() {
        defer close(end1)
        s(t, sender)
        err := sender.Close()
        if err != nil {
            t.Fatalf("Error closing sender: %s", err)
        }
    }()

    go func() {
        defer close(end2)
        r(t, receiver)
    }()
    ...

在演示应用程序中,通过在客户端和Transport.NewSendChannel()上分别调用libchan.Senderlibchan.Receiver来调用Transport.WaitReceiveChannel()来促进通信。这些libchan实例通过网络来处理“管道”。

从client.go:

sender, err := transport.NewSendChannel()
...
err = sender.Send(command)

从server.go:

receiver, err := t.WaitReceiveChannel()
...
err := receiver.Receive(command)

在两种情况下,都必须先完成必备的传输配置(即绑定到套接字,使用TLS等)。

the spdy library being used is part of the libchan distribution可能也值得一提,因此它提供了libchan基元。