Go实战--实现一个简单聊天室chatroom(The way to go)
来源:互联网 发布:网信办网络安全局 编辑:程序博客网 时间:2024/04/30 22:36
生命不止,继续 go go go !!!
上一篇博客跟大家介绍了,如何实现一个简单的tcp服务器和客户端,那么在此基础上,继续深耕一点点,介绍一下如何创建一个聊天室。
当然,还是还是一个要有一个服务端和若干个客户端。
server
net.Listen
Accept
这两个方法就不介绍了,之前都有提到过。
声明一个net.Conn数组,用于存放连接服务器的客户端:
var clients []net.Conn
大部分跟之前提到的tcp的server/client没有区别,最大的区别就是服务端收到消息后需要通知各个客户端,所以可以通过遍历数组cliQue来完成消息的通知。这里用到了for…range循环:
func notify(conn net.Conn, msg string) { for _, con := range clients { if con.RemoteAddr() != conn.RemoteAddr() { con.Write([]byte(msg)) } }}
顺便介绍一下RemoteAddr
func (c *IPConn) RemoteAddr() Addr
作用:
RemoteAddr returns the remote network address. The Addr returned is shared by all invocations of RemoteAddr, so do not modify it.
有一个客户端离开聊天室的时,要通知所有的客户端:
func disconnect(conn net.Conn, name string) { for index, con := range clients { if con.RemoteAddr() == conn.RemoteAddr() { disMsg := name + " has left the room." fmt.Println(disMsg) clients = append(clients[:index], clients[index+1:]...) notify(conn, disMsg) } }}
Go语言函数中有三个点…表示为可变参数,可以接受任意个数的参数。
append主要用于给某个切片(slice)追加元素
如果该切片存储空间(cap)足够,就直接追加,长度(len)变长;如果空间不足,就会重新开辟内存,并将之前的元素和新的元素一同拷贝进去
第一个参数为切片,后面是该切片存储元素类型的可变参数
client
客户端的代码就相对简单了很多。net.Dial也不再赘述。
注意就是从conn读数据,这里readStr[:length]用到了切片。
func read(conn net.Conn) { for { length, err := conn.Read(readStr) if err != nil { fmt.Printf("Error when read from server. Error:%s\n", err) os.Exit(0) } fmt.Println(string(readStr[:length])) }}
完整代码
服务端:
package mainimport ( "fmt" "net" "os")var clients []net.Connfunc main() { var ( host = "localhost" port = "8000" remote = host + ":" + port data = make([]byte, 1024) ) fmt.Println("Initiating server...") lis, err := net.Listen("tcp", remote) defer lis.Close() if err != nil { fmt.Printf("Error when listen: %s, Err: %s\n", remote, err) os.Exit(-1) } for { var res string conn, err := lis.Accept() if err != nil { fmt.Println("Error accepting client: ", err.Error()) os.Exit(0) } clients = append(clients, conn) go func(con net.Conn) { fmt.Println("New connection: ", con.RemoteAddr()) // Get client's name length, err := con.Read(data) if err != nil { fmt.Printf("Client %v quit.\n", con.RemoteAddr()) con.Close() disconnect(con, con.RemoteAddr().String()) return } name := string(data[:length]) comeStr := name + " entered the room." notify(con, comeStr) // Begin recieve message from client for { length, err := con.Read(data) if err != nil { fmt.Printf("Client %s quit.\n", name) con.Close() disconnect(con, name) return } res = string(data[:length]) sprdMsg := name + " said: " + res fmt.Println(sprdMsg) res = "You said:" + res con.Write([]byte(res)) notify(con, sprdMsg) } }(conn) }}func notify(conn net.Conn, msg string) { for _, con := range clients { if con.RemoteAddr() != conn.RemoteAddr() { con.Write([]byte(msg)) } }}func disconnect(conn net.Conn, name string) { for index, con := range clients { if con.RemoteAddr() == conn.RemoteAddr() { disMsg := name + " has left the room." fmt.Println(disMsg) clients = append(clients[:index], clients[index+1:]...) notify(conn, disMsg) } }}
客户端:
package mainimport ( "bufio" "fmt" "net" "os")var writeStr, readStr = make([]byte, 1024), make([]byte, 1024)func main() { var ( host = "localhost" port = "8000" remote = host + ":" + port reader = bufio.NewReader(os.Stdin) ) con, err := net.Dial("tcp", remote) defer con.Close() if err != nil { fmt.Println("Server not found.") os.Exit(-1) } fmt.Println("Connection OK.") fmt.Printf("Enter your name: ") fmt.Scanf("%s", &writeStr) in, err := con.Write([]byte(writeStr)) if err != nil { fmt.Printf("Error when send to server: %d\n", in) os.Exit(0) } fmt.Println("Now begin to talk!") go read(con) for { writeStr, _, _ = reader.ReadLine() if string(writeStr) == "quit" { fmt.Println("Communication terminated.") os.Exit(1) } in, err := con.Write([]byte(writeStr)) if err != nil { fmt.Printf("Error when send to server: %d\n", in) os.Exit(0) } }}func read(conn net.Conn) { for { length, err := conn.Read(readStr) if err != nil { fmt.Printf("Error when read from server. Error:%s\n", err) os.Exit(0) } fmt.Println(string(readStr[:length])) }}
运行结果:
连接
聊天
离开
- Go实战--实现一个简单聊天室chatroom(The way to go)
- Go实战--实现一个简单的tcp服务端和客户端(The way to go)
- Go实战--实现简单的restful api(The way to go)
- Go实战--实现一个自己的网络请求日志httplogger(The way to go)
- Go实战--实现一个单向链表(The way to go)
- Go实战--实现一个并发时钟服务器(The way to go)
- Go实战--go中编码转换(The way to go)
- Go实战--go中使用libphonenumber(The way to go)
- Go实战--go中使用cookie(The way to go)
- Go实战--go中使用rpc(The way to go)
- Go实战--golang新手入门常见错误(The way to go)
- Go实战--golang生成uuid(The way to go)
- The way to go !
- Go实战--go语言中执行shell脚本(The way to go)
- Go实战--go语言操作sqlite数据库(The way to go)
- Go实战--go中使用google/protobuf(The way to go)
- Go实战--go中使用base64加密(The way to go)
- Go实战--go中使用hmac sha256(The way to go)
- 基于强化学习的文本生成技术
- 待看
- 互联网运营起步 |《从零开始做运营》读书笔记
- IO端口和IO内存
- haproxy http模式案例
- Go实战--实现一个简单聊天室chatroom(The way to go)
- 安卓开发——ScrollView中设置子控件填充满ScrollView
- 跟我学JavaScript--运算符
- 求数组子数组之和最大值
- CCF NOI1047 寻找鞍点
- POJ 3006 Dirichlet's Theorem on Arithmetic Progressions
- 用Anaconda安装TensorFlow
- 【Oracle】dbms_output
- 用模重复平方法求b^n mod m