分享
  1. 首页
  2. 文章

golang 聊天室学习笔记

xingzhehxiang · · 960 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

1聊天室服务器端

package main
import (
  "fmt"
  "net"
  "strings"
  "time"
)
//定义的此结构体为全局map的value值,包括每一个用户的姓名,ip地址和私人管道
type client struct {
  name string
  addr string
  C  chan string
}
/*这个函数是将私人管道中的内容发送给用户,配合全局管道Message使用可以实现广播的功能,
单独使用可以实现私聊的功能*/
func writemsg2client(clinet client, conn net.Conn) {
  for m := range clinet.C {
    conn.Write([]byte(m + "\n"))
  }
}
//这只是一个封装好用来统一(发送信息格式)的小函数,不用在意
func makemsg(name string, addr string, s string) string {
  return "[" + addr + "]" + name + ":" + s
}
//每一个进入聊天室的用户都将启动一个handleconn的go程来处理事件
func handleconn(conn net.Conn) {
  defer conn.Close()
  /*用户连接进来以后要初始化全局map,把自己的信息加入到字典里,相当于进到聊天室里之前要登
   记一下个人信息,注意姓名初始为ip地址。*/
  addr := conn.RemoteAddr().String()
  fmt.Printf("用户%s进入了房间\n", addr)
  client := client{addr, addr, make(chan string)}
  //在这里启动子go程,功能上面已经提及,具体就是会写信息给自己连接的客户端
  go writemsg2client(client, conn)
  onlinemap[addr] = client
  //登录进来一切准备就绪后就给所有人广播上线信息啦
  Message <- makemsg(client.name, addr, "login")
  //下面这三个变量服务于下面一些小功能
  var haschat = make(chan bool)
  var ifquit = make(chan bool)
  var flag bool
  //从这单独开启一个go程来读取用户输入的信息
  go func() {
    buf := make([]byte, 4096)
    for {
      n, _ := conn.Read(buf)
      if n == 0 {
        fmt.Printf("%s离开了房间\n", client.name)
        ifquit <- true
        return
      }
      //改名功能的实现
      if string(buf[:7]) == "Rename|" {
        client.name = strings.Split(string(buf[:n]), "|")[1]
        onlinemap[addr] = client
        conn.Write([]byte("rename success\n"))
      } else if string(buf[:n-1]) == "/who" {
        //查询在线用户信息的功能
        for _, s := range onlinemap {
          conn.Write([]byte(s.name + "online\n"))
        }
      } else if string(buf[:2]) == "m|" && strings.Count(string(buf[:n]), "|") == 2 {
        /*私聊功能的实现,其实私聊功能就是跳过了往全局Message里传输信息,
         改为直接向私人管道里传输信息*/
        flag = true
        slice := strings.Split(string(buf[:n]), "|")
        fmt.Println(slice[1])
        for _, a := range onlinemap {
          //遍历所有在线用户,向指定的用户管道中发送信息
          if a.name == slice[1] {
            flag = false
            a.C <- makemsg(client.name, addr, slice[2])
            conn.Write([]byte("send success"))
          }
        }
        if flag {
          conn.Write([]byte("no such man or not online"))
        }
      } else {
        Message <- makemsg(client.name, addr, string(buf[:n]))
      }
      haschat <- true
    }
  }()
  for {
    select {
    case <-haschat:
    //超时强踢
    case <-time.After(time.Minute * 100):
      delete(onlinemap, addr)
      Message <- makemsg(client.name, addr, "out time to leave")
      close(client.C)
      return
    case <-ifquit:
      //退出处理
      delete(onlinemap, addr)
      Message <- makemsg(client.name, addr, "out time to leave")
      close(client.C)
      return
    }
  }
}
//这个函数用来将全局Message中的内容全部塞到私人管道C里,实现上下线广播和群聊的功能
func Manager() {
  for {
    msg := <-Message
    for _, s := range onlinemap {
      s.C <- msg
    }
  }
}
var Message = make(chan string)
var onlinemap map[string]client = make(map[string]client)
//主函数
func main() {
  listener, _ := net.Listen("tcp", "127.0.0.1:9876")
  defer listener.Close()
  //提前开启全局Message的go程,防止被阻塞
  go Manager()
  for {
    conn, err := listener.Accept()
    if err != nil {
      fmt.Println("accept err", err)
      continue
    }
    //每一个连接进来的用户都会被分配进入一个子go程,用来处理上面我们提到的各种功能
    go handleconn(conn)
  }
}
/*备注
1、 listener, _ := net.Listen("tcp", "127.0.0.1:9876")
监听启动
2、 go Manager()开启全局Message的go程,防止被阻塞,没有消息便被阻塞,有消息便会被唤起,
消息发送完毕后重新等待消息,有消息变发送没消息便阻塞等待(Message 是一个字符串channel )。
接收到消息后,遍历所有在线人员,并把消息发送给client的私人通道。
func Manager() {
  for {
    msg := <-Message
    for _, s := range onlinemap {
      s.C <- msg
    }
  }
}
3、私人通道消息处理
这个函数是将私人管道中的内容发送给用户,配合全局管道Message使用可以实现广播的功能。
单独使用可以实现私聊的功能(m|客户端连接ip加端口|发送消息)(m|127.0.0.1:59700|hello)。
这个函数也是等待消息,收到消息后被唤醒执行,消息执行完毕后等待新消息,没有阻塞,有就处理
func writemsg2client(clinet client, conn net.Conn) {
  for m := range clinet.C {
    conn.Write([]byte(m + "\n"))
  }
}
*/

2、聊天室客户端

package main
import (
  "bufio"
  "fmt"
  "net"
  "os"
  "strings"
)
func readFromServer(conn net.Conn) {
  buf := make([]byte, 4096)
  for {
    n, err := conn.Read(buf)
    if err != nil {
      fmt.Println(err)
      os.Exit(1)
    }
    defer conn.Close()
    fmt.Println("接收到消息:", string(buf[:n]))
    fmt.Println("请输入要发送的消息:")
  }
}
func main() {
  conn, err := net.Dial("tcp", "127.0.0.1:9876")
  if err != nil {
    fmt.Println(err)
    return
  }
  defer conn.Close()
  go readFromServer(conn)
  //fmt.Println("请输入要发送的消息:")
  for {
    //strs :=""
    // fmt.Scanln(&strs) 空格有问题
    //strs := make([]byte, 4096)
    //n, err := os.Stdin.Read(strs)
    str, err := bufio.NewReader(os.Stdin).ReadString('\n')
    if err != nil {
      fmt.Println(err)
    }
    str = strings.TrimSpace(str)
    //fmt.Println("发送前", , "展示")
    //fmt.Println("a", str, "b")
    if str == "Q" {
      fmt.Println("接收到退出命令,退出客户端")
      break
    }
    conn.Write([]byte(str))
  }
}



有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:xingzhehxiang

查看原文:golang 聊天室学习笔记

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
960 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏