Created
February 9, 2023 07:05
-
-
Save CarsonSlovoka/f51164f8fc7ccef71e5687678d1fab2c to your computer and use it in GitHub Desktop.
回答discordgo用
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "bufio" | |
| "log" | |
| "os" | |
| "time" | |
| ) | |
| type User struct { | |
| name string | |
| msg chan []byte | |
| Active bool | |
| buf []byte | |
| start int // buf用 | |
| end int // buf用 | |
| off int // 紀錄Read當前讀到哪個位子 | |
| } | |
| const startBufSize = 1024 | |
| func NewUser(name string) *User { | |
| u := &User{name: name, | |
| msg: make(chan []byte), | |
| buf: make([]byte, 0), | |
| Active: true, | |
| } | |
| go u.listenMsg() | |
| return u | |
| } | |
| func (u *User) Read(b []byte) (n int, err error) { | |
| n = copy(b, u.buf[u.off:]) | |
| u.off += n | |
| return n, nil | |
| } | |
| func (u *User) listenMsg() { | |
| for { | |
| select { | |
| case msg, isOpen := <-u.msg: | |
| if !isOpen { | |
| return | |
| } | |
| if !u.Active { | |
| break | |
| } | |
| if u.end == len(u.buf) { // buffer full, so, resize | |
| // https://github.com/golang/go/blob/8fb9565832e6dbacaaa057ffabc251a9341f8d23/src/bufio/scan.go#L189-L209 | |
| newSize := len(u.buf) * 2 | |
| if newSize == 0 { | |
| newSize = startBufSize | |
| } | |
| newBuf := make([]byte, newSize) | |
| copy(newBuf, u.buf[u.start:u.end]) | |
| u.buf = newBuf | |
| u.end -= u.start | |
| u.start = 0 | |
| } | |
| n := copy(u.buf[u.end:len(u.buf)], msg) | |
| u.end += n | |
| } | |
| } | |
| } | |
| type Hub struct { | |
| userList map[*User]bool | |
| Register chan *User | |
| Unregister chan *User | |
| Broadcast chan []byte | |
| closed bool | |
| } | |
| func NewHub() *Hub { | |
| return &Hub{ | |
| userList: make(map[*User]bool), | |
| Register: make(chan *User), | |
| Unregister: make(chan *User), | |
| Broadcast: make(chan []byte), | |
| } | |
| } | |
| func (h *Hub) Run() { | |
| for { | |
| if h.closed { | |
| return | |
| } | |
| select { | |
| case user, isOpen := <-h.Register: | |
| if !isOpen { | |
| return | |
| } | |
| h.userList[user] = true | |
| case user, isOpen := <-h.Unregister: | |
| if !isOpen { | |
| return | |
| } | |
| h.deleteUser(user) | |
| case data, isOpen := <-h.Broadcast: | |
| if !isOpen { | |
| return | |
| } | |
| for u := range h.userList { | |
| if !u.Active { | |
| continue | |
| } | |
| select { | |
| case u.msg <- data: | |
| case <-time.After(3 * time.Second): | |
| log.Printf("此資料無法傳給使用者:%s 故將此使用者刪除\n", u.name) | |
| h.deleteUser(u) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| func (h *Hub) deleteUser(u *User) { | |
| u.Active = false | |
| close(u.msg) | |
| delete(h.userList, u) | |
| } | |
| func (h *Hub) Release() { | |
| for u := range h.userList { | |
| h.deleteUser(u) | |
| } | |
| close(h.Unregister) | |
| close(h.Register) | |
| h.closed = true | |
| } | |
| func main() { | |
| hub := NewHub() | |
| defer hub.Release() | |
| userCarson := NewUser("Carson") | |
| userFoo := NewUser("Foo") | |
| go hub.Run() | |
| hub.Register <- userCarson | |
| hub.Register <- userFoo | |
| // Test | |
| go func() { | |
| <-time.After(4 * time.Second) | |
| buf := make([]byte, 5) | |
| _, _ = userCarson.Read(buf) | |
| log.Printf("%s: %s\n", userCarson.name, buf) | |
| <-time.After(8 * time.Second) | |
| buf = make([]byte, 20) | |
| _, _ = userCarson.Read(buf) | |
| log.Printf("%s: %s\n", userCarson.name, buf) | |
| buf = make([]byte, 1024) | |
| _, _ = userFoo.Read(buf) | |
| log.Printf("%s: %s\n", userFoo.name, buf) | |
| log.Println("請輸入退出: -1") | |
| return | |
| }() | |
| scanner := bufio.NewScanner(os.Stdin) | |
| for scanner.Scan() { | |
| text := scanner.Text() | |
| if text == "quit" || text == "exit" || text == "-1" { | |
| return | |
| } | |
| hub.Broadcast <- []byte(text) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment