Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package main
- import (
- "bufio"
- "errors"
- "fmt"
- "net"
- "strings"
- "time"
- )
- type Client struct {
- name string
- incoming chan string
- outgoing chan string
- leave chan bool
- reader *bufio.Reader
- writer *bufio.Writer
- conn net.Conn
- }
- func (client *Client) Read() {
- for {
- line, err := client.reader.ReadString('\n')
- if err != nil {
- // fmt.Println("\nsocket read err: ", err)
- break
- }
- fmt.Print("r")
- client.incoming <- line
- }
- fmt.Print("R")
- client.leave <- true
- }
- func (client *Client) Write() {
- for data := range client.outgoing {
- _, err := client.writer.WriteString(data)
- if err != nil {
- // fmt.Println("\nsocket write err: ", err)
- break
- }
- fmt.Print("w")
- client.writer.Flush()
- }
- fmt.Print("W")
- client.leave <- true
- }
- func (client *Client) ReadName() (string, error) {
- // Get client's name
- _, err := client.writer.WriteString("please enter you nickname\r\n")
- if err != nil {
- return "", errors.New("write error")
- }
- client.writer.Flush()
- name, err := client.reader.ReadString('\n')
- if err != nil {
- return "", errors.New("read error")
- }
- name = strings.TrimSpace(name)
- client.name = name
- return name, nil
- }
- func (client *Client) Listen() {
- go client.Read()
- go client.Write()
- }
- func (client *Client) String() string {
- return fmt.Sprintf("%p", client.conn)
- }
- func NewClient(conn net.Conn) *Client {
- writer := bufio.NewWriter(conn)
- reader := bufio.NewReader(conn)
- client := &Client{
- incoming: make(chan string),
- outgoing: make(chan string),
- leave: make(chan bool),
- reader: reader,
- writer: writer,
- conn: conn,
- }
- return client
- }
- type ChatRoom struct {
- clients []*Client
- joins chan net.Conn
- leaves chan *Client
- incoming chan string
- outgoing chan string
- }
- func (chatRoom *ChatRoom) Broadcast(data string) {
- fmt.Print(len(chatRoom.clients))
- for _, client := range chatRoom.clients {
- client.outgoing <- data
- }
- }
- func (chatRoom *ChatRoom) Join(conn net.Conn) {
- client := NewClient(conn)
- chatRoom.clients = append(chatRoom.clients, client)
- go func() {
- name, err := client.ReadName()
- if err != nil {
- chatRoom.leaves <- client
- fmt.Printf("Client %v quit.\n", conn.RemoteAddr())
- return
- }
- comeStr := name + " entered the room.\r\n"
- chatRoom.incoming <- comeStr
- // 开始正常的聊天读写流程
- client.Listen()
- for {
- select {
- case data := <-client.incoming:
- if strings.Contains(data, "stat") {
- count := len(chatRoom.clients)
- stat := fmt.Sprintf("conn count: %d detail:%s \r\n", count, chatRoom.clients)
- client.outgoing <- stat
- } else {
- // client.outgoing <- data
- chatRoom.incoming <- client.name + ": " + data
- }
- case <-client.leave:
- chatRoom.leaves <- client
- return
- }
- }
- }()
- }
- // 退出要求:
- // 1. 结束读、写循环
- // 2. 从列表中移除
- // 3. 关闭conn
- // 4. 读、写均可能调用destory,需要判断去重
- func (chatRoom *ChatRoom) Leaves(client *Client) (success bool) {
- // 查找
- index := -1
- s := chatRoom.clients
- for i, _v := range s {
- if _v == client {
- index = i
- break
- }
- }
- if index == -1 {
- return false
- }
- // 从列表中移除
- i := index
- s[i], s[len(s)-1], s = s[len(s)-1], nil, s[:len(s)-1]
- chatRoom.clients = s
- // 宣告退出,这里必须go,否则会死锁
- go func() {
- chatRoom.incoming <- client.name + " quit.\r\n"
- }()
- // 关闭资源
- close(client.outgoing)
- client.conn.Close()
- // client.conn = nil
- return true
- }
- func (chatRoom *ChatRoom) Listen() {
- go func() {
- for {
- select {
- case data := <-chatRoom.incoming:
- chatRoom.Broadcast(data)
- case conn := <-chatRoom.joins:
- chatRoom.Join(conn)
- fmt.Print("-")
- case client := <-chatRoom.leaves:
- chatRoom.Leaves(client)
- fmt.Print("x")
- }
- }
- }()
- }
- func NewChatRoom() *ChatRoom {
- chatRoom := &ChatRoom{
- clients: make([]*Client, 0),
- joins: make(chan net.Conn),
- leaves: make(chan *Client),
- incoming: make(chan string),
- outgoing: make(chan string),
- }
- chatRoom.Listen()
- return chatRoom
- }
- func main() {
- chatRoom := NewChatRoom()
- l, err := net.Listen("tcp", ":6666")
- if err != nil {
- fmt.Println("\nfailed to listen on port 6666")
- return
- }
- defer l.Close()
- for {
- conn, _ := l.Accept()
- // conn.SetReadDeadline(time.Now().Add(20 * time.Second)) // set 20 seconds timeout
- // conn.SetWriteDeadline(time.Now().Add(20 * time.Second)) // set 20 seconds timeout
- chatRoom.joins <- conn
- }
- // debug:
- time.Sleep(2 * time.Second)
- <-time.After(5 * time.Second)
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement