通常聊天室的架构分为服务器端和客户端:
成都一家集口碑和实力的网站建设服务商,拥有专业的企业建站团队和靠谱的建站技术,十多年企业及个人网站建设经验 ,为成都上1000+客户提供网页设计制作,网站开发,企业网站制作建设等服务,包括成都营销型网站建设,品牌网站建设,同时也为不同行业的客户提供网站设计、成都网站建设的服务,包括成都电商型网站制作建设,装修行业网站制作建设,传统机械行业网站建设,传统农业行业网站制作建设。在成都做网站,选网站制作建设服务商就选成都创新互联。服务器端:
接受来自于客户端的连接请求并建立连接;
所有客户端的连接会放进连接池中,用于广播消息;
客户端:
连接服务器;
向服务器发送消息;
接收服务器的广播消息;
注意事项:
某一个客户端断开连接后需要从连接池中摘除,并不再接收广播消息;
某一个客户端断开连接后不能影响服务器端或别的客户端的连接;
详细的代码如下,文档看注释就好了,不再细说:
服务器:
server.go
package main
import (
"net"
"log"
"fmt"
)
func main() {
port := "9090"
Start(port)
}
// 启动服务器
func Start(port string) {
host := ":" + port
// 获取tcp地址
tcpAddr, err := net.ResolveTCPAddr("tcp4", host)
if err != nil {
log.Printf("resolve tcp addr failed: %v\n", err)
return
}
// 监听
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
log.Printf("listen tcp port failed: %v\n", err)
return
}
// 建立连接池,用于广播消息
conns := make(map[string]net.Conn)
// 消息通道
messageChan := make(chan string, 10)
// 广播消息
go BroadMessages(&conns, messageChan)
// 启动
for {
fmt.Printf("listening port %s ...\n", port)
conn, err := listener.AcceptTCP()
if err != nil {
log.Printf("Accept failed:%v\n", err)
continue
}
// 把每个客户端连接扔进连接池
conns[conn.RemoteAddr().String()] = conn
fmt.Println(conns)
// 处理消息
go Handler(conn, &conns, messageChan)
}
}
// 向所有连接上的乡亲们发广播
func BroadMessages(conns *map[string]net.Conn, messages chan string) {
for {
// 不断从通道里读取消息
msg := <-messages
fmt.Println(msg)
// 向所有的乡亲们发消息
for key, conn := range *conns {
fmt.Println("connection is connected from ", key)
_, err := conn.Write([]byte(msg))
if err != nil {
log.Printf("broad message to %s failed: %v\n", key, err)
delete(*conns, key)
}
}
}
}
// 处理客户端发到服务端的消息,将其扔到通道中
func Handler(conn net.Conn, conns *map[string]net.Conn, messages chan string) {
fmt.Println("connect from client ", conn.RemoteAddr().String())
buf := make([]byte, 1024)
for {
length, err := conn.Read(buf)
if err != nil {
log.Printf("read client message failed:%v\n", err)
delete(*conns, conn.RemoteAddr().String())
conn.Close()
break
}
// 把收到的消息写到通道中
recvStr := string(buf[0:length])
messages <- recvStr
}
}
客户端:
client.go
package main
import (
"net"
"log"
"fmt"
"os"
)
func main() {
Start(os.Args[1])
}
func Start(tcpAddrStr string) {
tcpAddr, err := net.ResolveTCPAddr("tcp4", tcpAddrStr)
if err != nil {
log.Printf("Resolve tcp addr failed: %v\n", err)
return
}
// 向服务器拨号
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
log.Printf("Dial to server failed: %v\n", err)
return
}
// 向服务器发消息
go SendMsg(conn)
// 接收来自服务器端的广播消息
buf := make([]byte, 1024)
for {
length, err := conn.Read(buf)
if err != nil {
log.Printf("recv server msg failed: %v\n", err)
conn.Close()
os.Exit(0)
break
}
fmt.Println(string(buf[0:length]))
}
}
// 向服务器端发消息
func SendMsg(conn net.Conn) {
username := conn.LocalAddr().String()
for {
var input string
// 接收输入消息,放到input变量中
fmt.Scanln(&input)
if input == "/q" || input == "/quit" {
fmt.Println("Byebye ...")
conn.Close()
os.Exit(0)
}
// 只处理有内容的消息
if len(input) > 0 {
msg := username + " say:" + input
_, err := conn.Write([]byte(msg))
if err != nil {
conn.Close()
break
}
}
}
}
测试方法:
编译server.go和client.go;
打开终端,启动server,默认会监听9090端口;
再打开多个终端,启动client,client启动命令:client 服务器IP:9090;
在client中输入字符并回车,可以看到别的终端都会收到消息;
创新互联www.cdcxhl.cn,专业提供香港、美国云服务器,动态BGP最优骨干路由自动选择,持续稳定高效的网络助力业务部署。公司持有工信部办法的idc、isp许可证, 机房独有T级流量清洗系统配攻击溯源,准确进行流量调度,确保服务器高可用性。佳节活动现已开启,新人活动云服务器买多久送多久。