go语言应用:并发聊天服务器

思路:onlineMap保存在线用户        var onlineMap[string]ClientClient的结构:C chan string(存储需要发送给当前用户的信息),Name string,Addr stringmessage存放所有需要广播的消息1.主协程:处理用户连接1)将用户加到了map中2)告诉所有在线的用户,谁上线了(message<-某...
摘要由CSDN通过智能技术生成

思路:

onlineMap保存在线用户        var onlineMap[string]Client

Client的结构:C chan string(存储需要发送给当前用户的信息),Name string,Addr string

message存放所有需要广播的消息

1.主协程:处理用户连接

1)将用户加到了map中

2)告诉所有在线的用户,谁上线了(message<-某人上线了)

2.协程:专门发送消息,传递参数cli

for msg:=range cli.C{
       write(msg)
}

3.协程:

for{
    msg:=<-message//如果有内容
//遍历onlineMap,若有多个成员
    for _,cli:=range onlineMap{
        cli.C<-msg
    }
}

4.协程:接受用户的请求,把用户发过来的数据转发

用户发过来的数据放到buf

buf<-message

对方下线,把当前用户从map中移除

package main

import (
	"fmt"
	"net"
	"strings"
	"time"
)

type Client struct {
	C    chan string
	Name string
	Addr string
}

var onlineMap map[string]Client
var message = make(chan string)

func Messager() {
//给map分配空间
	onlineMap = make(map[string]Client)
	for {
		msg := <-message//没有消息的时候就阻塞了
		for _, cli := range onlineMap {
			cli.C <- msg
		}
	}
}
//给当前用户发送消息,当前用户的消息在cli.C 里面
func WriteMsgToClient(cli Client, conn net.Conn) {
	for msg := range cli.C {
		conn.Write([]byte(msg + "\n"))
	}
}
//把msg格式化一下
func MakeMsg(cli Client, msg string) (buf string) {
	buf = "[" + cli.Addr + "]" + cli.Name + ":" + msg
	return
}
//处理用户链接
func HandleConn(conn net.Conn) {
	defer conn.Close()
//获取客户端的网络地址
	cliAddr := conn.RemoteAddr().String()
//创建一个结构体,默认用户名和网络地址相同
	cli := Client{make(chan string), cliAddr, cliAddr}
//将结构体添加到map中
	onlineMap[cliAddr] = cli
//新开一个协程,专门给当前客户端发送消息
	go WriteMsgToClient(cli, conn)
//广播某个人上线了
	message <- MakeMsg(cli, "login")
//提示是谁
	cli.C <- MakeMsg(cli, "I am here")
	isQuit := make(chan bool)//对方是否主动退出
	hasData := make(chan bool)//对方是否有数据发送
//新开一个协程接收用户发送过来的数据
	go func() {
		buf := make([]byte, 2048)
		for {
			n, err := conn.Read(buf)
			if n == 0 {//对方断开或者出问题
				isQuit <- true
				fmt.Println("conn.Read err=", err)
				return
			}
			msg := string(buf[:n-1])//Windows,多一个换行,所以-1
			if len(msg) == 3 && msg == "who" {
                //遍历map,给当前用户发送所有成员
				conn.Write([]byte("user list:\n"))
				for _, tmp := range onlineMap {
					msg = tmp.Addr + ":" + tmp.Name + "\n"
					conn.Write([]byte(msg))
				}
			} else if len(msg) >= 8 && msg[:6] == "rename" {
				name := strings.Split(msg, "|")[1]
				cli.Name = name
				onlineMap[cliAddr] = cli
				conn.Write([]byte("rename successfully\n"))
			} else {
                //转发此内容
				message <- MakeMsg(cli, msg)
			}
			hasData <- true//代表有数据
		}
	}()
	for {
        //通过select检测channel的流动
		select {
		case <-isQuit:
			delete(onlineMap, cliAddr)//将当前用户从map中移除
			message <- MakeMsg(cli, "login out")//广播谁下线了
			return
		case <-hasData:
		case <-time.After(60 * time.Second)://超时
			delete(onlineMap, cliAddr)
			message <- MakeMsg(cli, "time out")
			return
		}
	}
}
func main() {
//监听,指明协议,ip地址,端口
	listener, err := net.Listen("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.Listen err=", err)
		return
	}
	defer listener.Close()
	//新开一个协程,转发消息,只要有消息来了,遍历整个map,给每个成员发送消息
    go Messager()
//主协程,循环阻塞等待用户链接
	for {
		conn, err1 := listener.Accept()
		if err1 != nil {
			fmt.Println("listener.Accept err=", err1)
			continue
		}
		go HandleConn(conn)//处理用户链接
	}
}

结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值