Channels of channels

The online Go tutorial at golang.org has an example of multiplexing, where channels of channels are used. The idea is that instead of sharing one channel, a new communicator is given their own channel to have a private conversation. That is, a client is sent a channel from a server through a shared channel, and uses that private channel.

This doesn’t work directly with network channels: a channel cannot be sent over a network channel. So we have to be a little more indirect. Each time a client connects to a server, the server builds new network channels and exports them with new names. Then it sends the names of these new channels to the client which imports them. It uses these new channels for communication.

A server is

  1. /* EchoChanServer
  2. */
  3. package main
  4. import (
  5. "fmt"
  6. "os"
  7. "old/netchan"
  8. "strconv"
  9. )
  10. var count int = 0
  11. func main() {
  12. exporter := netchan.NewExporter()
  13. err := exporter.ListenAndServe("tcp", ":2345")
  14. checkError(err)
  15. echo := make(chan string)
  16. exporter.Export("echo", echo, netchan.Send)
  17. for {
  18. sCount := strconv.Itoa(count)
  19. lock := make(chan string)
  20. go handleSession(exporter, sCount, lock)
  21. <-lock
  22. echo <- sCount
  23. count++
  24. exporter.Drain(-1)
  25. }
  26. }
  27. func handleSession(exporter *netchan.Exporter, sCount string, lock chan string) {
  28. echoIn := make(chan string)
  29. exporter.Export("echoIn"+sCount, echoIn, netchan.Send)
  30. echoOut := make(chan string)
  31. exporter.Export("echoOut"+sCount, echoOut, netchan.Recv)
  32. fmt.Println("made " + "echoOut" + sCount)
  33. lock <- "done"
  34. for {
  35. s := <-echoOut
  36. echoIn <- s
  37. }
  38. // should unexport net channels
  39. }
  40. func checkError(err error) {
  41. if err != nil {
  42. fmt.Println("Fatal error ", err.Error())
  43. os.Exit(1)
  44. }
  45. }

and a client is

  1. /* EchoChanClient
  2. */
  3. package main
  4. import (
  5. "fmt"
  6. "old/netchan"
  7. "os"
  8. )
  9. func main() {
  10. if len(os.Args) != 2 {
  11. fmt.Println("Usage: ", os.Args[0], "host:port")
  12. os.Exit(1)
  13. }
  14. service := os.Args[1]
  15. importer, err := netchan.Import("tcp", service)
  16. checkError(err)
  17. fmt.Println("Got importer")
  18. echo := make(chan string)
  19. importer.Import("echo", echo, netchan.Recv, 1)
  20. fmt.Println("Imported in")
  21. count := <-echo
  22. fmt.Println(count)
  23. echoIn := make(chan string)
  24. importer.Import("echoIn"+count, echoIn, netchan.Recv, 1)
  25. echoOut := make(chan string)
  26. importer.Import("echoOut"+count, echoOut, netchan.Send, 1)
  27. for n := 1; n < 10; n++ {
  28. echoOut <- "hello "
  29. s := <-echoIn
  30. fmt.Println(s, n)
  31. }
  32. close(echoOut)
  33. os.Exit(0)
  34. }
  35. func checkError(err error) {
  36. if err != nil {
  37. fmt.Println("Fatal error ", err.Error())
  38. os.Exit(1)
  39. }
  40. }

Conclusion

Network channels are a distributed analogue of local channels. They behave approximately the same, but due to limitations of the model some things have to be done a little differently.