ch8/ch8-10 #170
Replies: 7 comments 5 replies
-
实在无法理解,client 不是一个只能写入的string通道吗?然后书中代码就map那用了下,为何下面操作又变成双向?何必呢?还请有能理解的踢下我,谢谢
|
Beta Was this translation helpful? Give feedback.
-
package main
import (
"bufio"
"fmt"
"log"
"net"
"time"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go broadcaster()
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
type client chan<- string // an outgoing message channel
// 8.12
type clientChan struct {
client
name string
}
var (
entering = make(chan clientChan)
leaving = make(chan clientChan)
messages = make(chan string) // all incoming client messages
)
func broadcaster() {
clients := make(map[clientChan]bool) // all connected clients
for {
select {
case msg := <-messages:
// Broadcast incoming message to all
// clients' outgoing message channels.
for cli := range clients {
select {
case cli.client <- msg:
fmt.Println("send to " + cli.name)
default:
fmt.Println("pass " + cli.name)
}
}
case cli := <-entering:
clients[cli] = true
str := "Online users:\n"
for cli := range clients {
str += (cli.name + "\n")
}
// only send to cli
cli.client <- str
case cli := <-leaving:
delete(clients, cli)
close(cli.client)
}
}
}
func handleConn(conn net.Conn) {
ch := make(chan string) // outgoing client messages
go clientWriter(conn, ch)
ch <- "Input your name:"
input := bufio.NewScanner(conn)
var who string
for input.Scan() {
who = input.Text()
break
}
// who := conn.RemoteAddr().String()
ch <- "You are " + who
// messages <- who + " has arrived"
cc := clientChan{ch, who}
entering <- cc
tick := time.NewTicker(70 * time.Second)
tickchan := make(chan struct{})
go func() {
for {
select {
case <-tickchan:
tick = time.NewTicker(70 * time.Second)
case <-tick.C:
conn.Close()
return
}
}
}()
// input := bufio.NewScanner(conn)
for input.Scan() {
tickchan <- struct{}{}
messages <- who + ": " + input.Text()
}
// NOTE: ignoring potential errors from input.Err()
leaving <- cc
messages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
// time.Sleep(5 * time.Second)
fmt.Fprintln(conn, msg) // NOTE: ignoring network errors
}
} |
Beta Was this translation helpful? Give feedback.
-
//实现客户端 list通知 import ( func main() { type client chan<- string // an outgoing message channel type user struct { var ( func broadcaster() { func handleConn(conn net.Conn) {
} func clientWriter(conn net.Conn, ch <-chan string) { |
Beta Was this translation helpful? Give feedback.
-
实现客户端 list通知客户端10秒不发消息,就退出
|
Beta Was this translation helpful? Give feedback.
-
这节的示例还是挺有意思的,通过chan来抽象代理连接,通过chan <- msg 来代替conn.Write(msg),核心其实就是 func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg) // NOTE: ignoring network errors
}
} 向chan内写数据的时候,会被for msg := range ch消费到,然后再通过fmt.Fprintln(conn, msg)真实的调conn写出去。 练习8.12 修改client,用结构体包一下,包含客户端信息,在enter的时候将这些客户端信息写出即可: package main
import (
"bufio"
"fmt"
"log"
"net"
)
type client struct {
who string
conn chan<- string
}
var (
entering = make(chan client)
leaving = make(chan client)
message = make(chan string)
)
func main() {
server, err := net.Listen("tcp", ":44567")
if err != nil {
log.Fatal(err)
}
//广播
go broadcaster()
for {
conn, _ := server.Accept()
go handleConn(conn)
}
}
// 广播
func broadcaster() {
//所有的客户端
clients := make(map[client]bool)
for {
select {
case msg := <-message:
for client := range clients {
client.conn <- msg
}
case cli := <-entering:
for others := range clients {
cli.conn <- others.who
}
clients[cli] = true
case cli := <-leaving:
delete(clients, cli)
close(cli.conn)
}
}
}
func handleConn(conn net.Conn) {
defer conn.Close()
ch := make(chan string)
who := conn.RemoteAddr().String()
go clientWriter(conn, ch)
//先给大家发消息,再将自己加入clients 避免发给自己
message <- who + "has arrived"
client := client{who: who, conn: ch}
entering <- client
//单发给自己消息
ch <- "welcome " + who
input := bufio.NewReader(conn)
//一旦读到客户端发来的消息,就通过message 广播出去
var buf [64 * 1024]byte
for {
len, err := input.Read(buf[:])
if err != nil {
break
}
message <- who + ": " + string(buf[:len])
}
//先离开 再发消息给大家避免发给自己
leaving <- client
message <- who + "has leaf"
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
} 练习8.15 ,可以广播写出的时候加个最长等待时间,要是还没写出就不管了,直接写下一个客户端: for {
select {
case msg := <-message:
for client := range clients {
select {
case client.conn <- msg:
//do nothing
case <-time.After(5 * time.Second):
fmt.Println("write to" + client.who + "timeout")
}
}
case cli := <-entering:
for others := range clients {
cli.conn <- others.who
}
clients[cli] = true
case cli := <-leaving:
delete(clients, cli)
close(cli.conn)
}
} |
Beta Was this translation helpful? Give feedback.
-
8.12 、8.13 import ( //!+broadcaster var ( func broadcaster() {
} func getCurConn() string{ func routineIdle(client* client){
} //!+handleConn
} func clientWriter(conn net.Conn, ch* client) { //!-handleConn //!+main |
Beta Was this translation helpful? Give feedback.
-
8.12 + 8.13 package main
import (
"bufio"
"fmt"
"log"
"net"
"time"
)
type Client struct {
Name string
Ch chan<- string
}
var (
entering = make(chan Client)
leaving = make(chan chan<- string)
messages = make(chan string)
)
func broadcaster() {
clients := make(map[chan<- string]string)
for {
select {
case msg := <-messages:
for cli := range clients {
cli <- msg
}
case cli := <-entering:
clients[cli.Ch] = cli.Name
for ch, name := range clients {
if ch != cli.Ch {
cli.Ch <- name + " is online"
}
}
case ch := <-leaving:
_, ok := clients[ch]
if ok {
delete(clients, ch)
close(ch)
}
}
}
}
func handleConn(conn net.Conn) {
ch := make(chan string)
go clientWriter(conn, ch)
who := conn.RemoteAddr().String()
ch <- "You are " + who
messages <- who + " has arrived"
entering <- Client{Name: who, Ch: ch}
input := bufio.NewScanner(conn)
timer := time.NewTimer(5 * time.Minute)
defer timer.Stop()
done := make(chan struct{})
go func() {
select {
case <-timer.C:
conn.Close()
case <-done:
return
}
}()
for input.Scan() {
if !timer.Stop() {
<-timer.C
break
}
timer.Reset(5 * time.Minute)
messages <- who + ": " + input.Text()
}
close(done)
leaving <- ch
messages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err, "is me")
}
go broadcaster()
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err, "连接断开")
continue
}
go handleConn(conn)
}
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
ch8/ch8-10
中文版
https://gopl-zh.github.io/ch8/ch8-10.html
Beta Was this translation helpful? Give feedback.
All reactions