首页 > 编程 > Golang > 正文

go语言实现聊天服务器的示例代码

2020-04-01 18:55:02
字体:
来源:转载
供稿:网友

看了两天 go 语言,是时候练练手了。

go 的 routine(例程) 和 chan(通道) 简直是神器,实现多线程(在 go 里准确的来说是 多例程)简直不要太轻松。

于是动手码了一个傻瓜版的黑框聊天器。

server 端:

监听 TCP 连接;支持自定义客户端命令;支持消息分发;理论上支持广播;...

package mainimport (  "fmt"  "net"  "io"  "strconv"  "time"  "strings")const (  NORMAL_MESSAGE = iota  LIST_MESSAGE)var clientSenders = make(map[string] chan string)func send (addr string, conn *net.Conn){  senderChan := clientSenders[addr]  for s := range senderChan{    (*conn).Write([]byte(s))  }}func sendUsersInfo(addr string){  senderChan := clientSenders[addr]  if nil != senderChan{    ls := strconv.Itoa(LIST_MESSAGE)    cs := strconv.Itoa(NORMAL_MESSAGE) + "已登录客户端列表:/n"    i := 1    for k := range clientSenders{      a := ""      if k == addr {        a = "(我)"      }      cs = cs + strconv.Itoa(i) + ")" + k + a + "/n"      ls += k + "/n"      i ++    }    cs += "发送消息,可使用 1<-这是给1号客户端的消息/n(请使用英文以获取最佳体验)/n"    senderChan <- cs    time.Sleep(time.Millisecond * 300)    senderChan <- ls    // 发送格式化的列表    fmt.Println("已发送“登录用户信息”", addr)  } else{    fmt.Println("客户端接受通道不存在", addr)  }}func serve (conn *net.Conn){  connect := *conn  addr := connect.RemoteAddr().String()  fmt.Println(addr, "接入服务")  senderChan := make(chan string, 3)  clientSenders[addr] = senderChan  // 启动发送  go send(addr, conn)  // 发送当前用户信息  go sendUsersInfo(addr)  buff := make([]byte, 10240)  for {    n, err := connect.Read(buff)    if err != nil {      if err == io.EOF {        fmt.Println("客户端断开链接,", addr)        delete(clientSenders, addr)        return      } else{        fmt.Println(err)      }    }    msg := string(buff[:n])    // 刷新客户端列表    if msg == "ls/n" {      go sendUsersInfo(addr)      continue    }    // 提取数据    msgs := strings.Split(msg, "<-")    if len(msg) < 2{      senderChan <- string("数据格式不正确,请联系开发者")      continue    }    aimAddr := msgs[0]    aimSender := clientSenders[aimAddr]    if aimSender == nil {      senderChan <- string("客户端已下线,使用 ls 命令获取最新的客户端列表")      continue    }    aimSender <- strconv.Itoa(NORMAL_MESSAGE) + "[from:" + addr + "]:" + strings.Join(msgs[1:], "<-")  }}func main(){  addr := ":8080"  listener, err := net.Listen("tcp", addr)  if err != nil{    fmt.Println(err)    return  }  // 启动消息调度器  defer listener.Close()  // 启动连接监听  for {    conn, err := listener.Accept()    if err != nil {      fmt.Println(err)      continue    }    go serve(&conn)  }}

客户端:

支持断线重连;支持给特定其他客户端发信息

package mainimport (  "net"  "fmt"  "io"  "os"  "bufio"  "sync"  "time"  "strings"  "strconv")var conn *net.Connvar addrs []stringconst (  NORMAL_MESSAGE = iota  LIST_MESSAGE)func read(conn2 *net.Conn){  defer func() {    fmt.Println("尝试重连")    go connectServer()  }()  connect := *conn2  buff := make([]byte, 20140)  for {    n, err := connect.Read(buff)    if err != nil {      if err == io.EOF{        fmt.Println("结束")        (*conn2).Close()        conn = nil        return      } else{        fmt.Println(err)      }    }    msg := string(buff[:n])    t, err := strconv.Atoi(string(msg[0]))    msg = msg[1:]    switch t {    case NORMAL_MESSAGE:      fmt.Print(msg)      break    case LIST_MESSAGE:      // 解析客户端列表数据      addrs = strings.Split(msg, "/n")      fmt.Println("已接收客户端列表。/n")      break    default:      fmt.Print(msg)      break    }  }}func connectServer(){  addr := "192.168.99.236:8080"  fmt.Println("等待服务器开启中")  conn2, err := net.Dial("tcp", addr)  if err != nil {    fmt.Print(err)    fmt.Println("连接失败,10s后尝试")    time.Sleep(10 * time.Second)    go connectServer()    return  }  fmt.Println("已连接")  conn = &conn2  go read(&conn2)}func send (){  inputReader := bufio.NewReader(os.Stdout)  for {    input, err := inputReader.ReadString('/n')    if err != nil {      if err == io.EOF{        return      } else{        fmt.Println(err)      }    }    if input == "ls/n" {      (*conn).Write([]byte(input))      continue    }    msgs := strings.Split(input, "<-")    if len(msgs) < 2 {      fmt.Println("发送的姿势不正确,应该像这样 1<-给1号发送消息/n")      continue    }    index, err := strconv.Atoi(msgs[0])    if err != nil {      fmt.Println("发送的姿势不正确,应该像这样 1<-给1号发送消息/n")      continue    }    if len(addrs) <= index {      fmt.Println("不存在第" + strconv.Itoa(index) + "个客户端/n")      continue    }    addr := addrs[index-1]    input = addr + "<-" + strings.Join(msgs[1:], "<-")    if nil != conn {      (*conn).Write([]byte(input))    }  }}func main (){  var wg sync.WaitGroup  wg.Add(2)  go connectServer()  go send()  wg.Wait()  defer func() {    if nil != conn {      (*conn).Close()    }  }()}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表