利用go并发与网络通信编写聊天室服务端

利用go并发与网络通信编写聊天室服务端

图片引用第三方
在这里插入图片描述

上代码:

package main

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

//注意结构体定义是type,不是var
type Client struct {
	C    chan string
	Name string
	Addr string
}

//用户在线列表 key表示ip和端口号,value表示Client结构体
var onlineClient map[string]Client

//全局channel,用于通信
var message = make(chan string)

//用于监听用户自带channel中是否有信息
func WriteMsgToClient(client Client, conn net.Conn) {
	//由于client中的channel是无缓冲的,当channel中无数据时,channel会阻塞,相当于死循环一直在监听
	for msg := range client.C {
		//客户端接收该信息写到聊天面板中
		conn.Write([]byte(msg + "\n"))
	}
}

//用于对获取到的数据加以处理
func makeMsg(client Client, msg string) (buf string) {
	buf = "[" + client.Addr + "] " + client.Name + ":" + msg
	return
}

//处理客户端请求
func HandleClient(conn net.Conn) {
	defer conn.Close()
	//获取用户的IP地址和端口号转换成string类型
	addr := conn.RemoteAddr().String()
	fmt.Println(addr + "用户登录成功")
	//创建一个用户,初始姓名都是用户的IP地址和端口号
	client := Client{
		C:    make(chan string),
		Name: addr,
		Addr: addr,
	}
	//将新增的用户加入到在线用户列表中
	onlineClient[addr] = client

	//创建一个go程给自己发送短信,显示在聊天面板中
	go WriteMsgToClient(client, conn)
	//将用户登录信息加载到全局channel中
	message <- makeMsg(client, " login...")

	//创建匿名go程来持续监听并获取用户发送的信息
	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := conn.Read(buf)
			//读取用户发送的信息
			if n == 0 {
				//如果n为0,表示用户没有输入,则退出在线
				fmt.Println("检测到用户退出登录")
				return
			}
			errFunc("Conn Read:", err)
			//减一是因为读取到的字符串中最后会有一个”\n“,切记,Windows系统为”\r\n“
			msg := string(buf[:n-1])
			if msg == "who" && len(msg) == 3 {
				conn.Write([]byte("online user list:"))
				for _,i := range onlineClient  {
					str := "["+i.Addr+"]"+i.Name+"\n"
					conn.Write([]byte(str))
				}
			}else if len(msg)>8 && msg[:7] == "rename|" {
				str := strings.Split(msg, "|")[1]
				client.Name = str
				s := string([]byte(client.Addr + "改名成功: " + str))
				message <- s
			}else if len(msg) == 4 && msg == "exit" {
				delete(onlineClient,client.Addr)
				message <- makeMsg(client,"logout")
			}else{
				message <- makeMsg(client, msg)
			}
		}
	}()
	//保持线程不会结束
	for {
		;
		//select 用于监听通道的数据流通,可以用于超时强踢
	}
}

//创建管理者线程,用于将写入到全局channel中的信息广播给每一个用户私人channel中
func manager() {
	//初始化用户在线列表
	onlineClient = make(map[string]Client)
	//循环监听全局channel中是否有数据产生
	for {
		msg := <-message
		//将产生的数据传输给每一个用户
		for _, client := range onlineClient {
			client.C <- msg
		}
	}
}

//用来处理err错误,简化代码量
func errFunc(str string, err error) {
	if err != nil {
		fmt.Println(str, err)
		return
	}
}

//主函数,用来模拟服务器
func main() {
	//创建监听套接字(主go程)
	//参数一:指定协议,参数二:指定IP地址以及端口号
	listen, err := net.Listen("tcp", "127.0.0.1:8001")
	errFunc("监听套接字连接失败", err)
	defer listen.Close()

	fmt.Println("服务器打开成功,等待客户端连接请求!")

	//启动管理者进程
	go manager()
	for {
		//循环监听客户端请求
		conn, err := listen.Accept()
		errFunc("客户端请求连接失败", err)
		//defer conn.Close()   此处细节:defer不放在for循环中,而是放到go程中,以免发生不必要的错误

		//启动go程,处理客户端请求
		go HandleClient(conn)
	}
}

可通过安装Nacap验证,打开终端,输入指定的IP和port
在这里插入图片描述
打开多个终端,模拟多用户在线

即可同步看到消息。

该代码包括的功能点:

  1. 用户上线消息广播
  2. 输入who查看用户在线列表
  3. 输入rename| 更改用户名
  4. 输入exit退出客户端

更多功能只需增加相应代码即可

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值