golang 创建一个简单的广播echo服务器
发布时间:2020-12-16 18:02:54 所属栏目:大数据 来源:网络整理
导读:package main;import ("net""fmt""bufio")//里面的代码部分参考cmu440课程//https://github.com/cmu440/p0//广播服务器接口type MultiEchoServer interface {//开始Start(port int) error;//停止Close();//当前客户端连接数Count() int;}//广播服务器type mu
package main;
import (
"net"
"fmt"
"bufio"
)
//里面的代码部分参考cmu440课程
//https://github.com/cmu440/p0
//广播服务器接口
type MultiEchoServer interface {
//开始
Start(port int) error;
//停止
Close();
//当前客户端连接数
Count() int;
}
//广播服务器
type multiEchoServer struct {
lis *net.TCPListener;
//当前客户端ID
curClientId int;
//所有客户端
clients map[int]*client;
//广播消息
broadcastMsg chan []byte;
}
//客户端
type client struct {
//ID
id int;
//连接
conn net.Conn;
//接收消息
recvMsg chan []byte;
//发送消息
sendMsg chan []byte;
//接收消息是否关闭
isRecvMsgClose chan bool;
//发送消息是否关闭
isSendMsgClose chan bool;
//服务器
mes *multiEchoServer;
}
//返回一个广播服务器
func New() *multiEchoServer {
return &multiEchoServer{
curClientId: 0,clients: make(map[int]*client),broadcastMsg: make(chan []byte,1),};
}
//启动服务器
func (m *multiEchoServer) Start(port int) error {
//获取tcp地址
addr,err := net.ResolveTCPAddr("tcp",fmt.Sprintf(":%d",port));
if err != nil {
return err;
}
//监听端口
m.lis,err = net.ListenTCP("tcp",addr);
if err != nil {
return err;
}
//启一个goroutine处理广播
go m.BroadcastLoop();
//启一个goroutine处理客户端来的连接
go func() {
for {
conn,err := m.lis.Accept();
if err != nil {
continue;
}
cli := &client{
id: m.curClientId,conn: conn,recvMsg: make(chan []byte,sendMsg: make(chan []byte,isRecvMsgClose: make(chan bool,isSendMsgClose: make(chan bool,mes: m,}
//加客户端加入到服务器clients中
clis := m.clients;
clis[m.curClientId] = cli;
m.clients = clis;
m.curClientId++;
//启两个goroutine分别处理客户端的接收与发送消息
go cli.RecvLoop();
go cli.SendLoop();
}
}();
return nil;
}
//停止服务器
func (m *multiEchoServer) Close() {
m.lis.Close();
//循环关闭客户端
for _,client := range m.clients {
client.conn.Close();
//这里只需给一个发送消息就好了
client.isRecvMsgClose <- true;
}
}
//返回当前客户端连接数
func (m *multiEchoServer) Count() int {
return len(m.clients);
}
//处理广播
func (m *multiEchoServer) BroadcastLoop() {
for {
select {
case data := <-m.broadcastMsg:
{
//遍历所有客户端,循环发送消息
for _,client := range m.clients {
client.sendMsg <- data;
}
break;
}
}
}
}
//删除客户端
func (m *multiEchoServer) DelClient(c *client) error {
c.conn.Close();
clis := m.clients;
delete(clis,c.id);
m.clients = clis;
return nil;
}
//处理客户端接收消息
func (c *client) RecvLoop() {
defer func() {
fmt.Println(c.conn.RemoteAddr().String() + " RecvLoop exit");
}();
for {
read := bufio.NewReader(c.conn);
data,err := read.ReadBytes('n');
if err != nil {
c.isSendMsgClose <- true;
//这里直接返回,如果不直接返回
//当客户端退出时,这里会运行2次,导致c.isSendMsgClose<-true执行2次造成阻塞
//没有机会运行后面的select,那么一直无法返回,不能回收。
return;
}
select {
//接收消息是否关闭
case <-c.isRecvMsgClose:
{
c.isSendMsgClose <- true;
return;
}
//广播消息
case c.mes.broadcastMsg <- data:
{
break;
}
}
}
}
//处理客户端发送消息
func (c *client) SendLoop() {
defer func() {
fmt.Println(c.conn.RemoteAddr().String() + " SendLoop exit");
}();
for {
select {
//发送消息关闭,则把客户端从服务中删除
case <-c.isSendMsgClose:
{
c.mes.DelClient(c);
return;
}
//向客户写入要发送的消息
case data := <-c.sendMsg:
{
_,err := c.conn.Write(data);
if err != nil {
return;
}
}
}
}
}
func main() {
mes := New();
mes.Start(8888);
//循环
select {};
}
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |