300行代码实现go语言即时通讯聊天室

学了2年Java,因为工作原因需要转Golang,3天时间学习了下go的基本语法,做这样一个聊天室小项目来巩固串联一下语法。

实现的功能:公聊,私聊,修改用户名

只用到了四个类:

  • main.go:用来启动服务器
  • server.go:服务器相关代码
  • client.go:客户端相关代码,用户可以直接操作的可视化界面
  • user.go:用户类,用来封装用户的业务逻辑

架构图

完整代码

server.go

package main

import (
	"fmt"
	"io"
	"net"
	"sync"
	"time"
)

type Server struct {
	Ip   string
	Port int
	//在线用户列表
	OnlineMap map[string]*User
	mapLock   sync.RWMutex
	//消息广播的Channel
	Message chan string
}

func NewServer(ip string, port int) *Server {
	server := &Server{
		Ip:        ip,
		Port:      port,
		OnlineMap: make(map[string]*User),
		Message:   make(chan string),
	}
	return server
}

func (s *Server) Handler(conn net.Conn) {
	//业务逻辑
	//fmt.Println("链接建立成功")
	user := NewUser(conn, s)

	user.Online()

	//监听用户是否活跃
	isLive := make(chan bool)

	go func() {
		buf := make([]byte, 4096)
		for {
			n, error := conn.Read(buf)
			if n == 0 {
				user.Offline()
				return
			}
			if error != nil && error != io.EOF {
				fmt.Println("read error")
			}
			msg := string(buf[:n-1])

			user.DoMessage(msg)

			//表示用户活跃
			isLive <- true
		}
	}()

	for {
		select {
		case <-isLive:
			//当前用户活跃,不做任何时,激活select,重置定时器

		case <-time.After(time.Second * 300):
			//超时,将user强制关闭
			user.SendMsg("你被踢了")
			close(user.C)
			conn.Close()
			return
		}
	}
}

func (s *Server) ListenMessager() {
	for {
		msg := <-s.Message

		s.mapLock.Lock()
		for _, user := range s.OnlineMap {
			user.C <- msg
		}
		s.mapLock.Unlock()
	}
}

func (s *Server) BroadCast(user *User, msg string) {
	sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
	s.Message <- sendMsg
}

func (s *Server) Start() {
	listener, error := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Ip, s.Port))
	if error != nil {
		fmt.Println("listener error...")
		return
	}
	defer listener.Close()

	go s.ListenMessager()
	for {
		conn, error := listener.Accept()
		if error != nil {
			fmt.Println("accept error...")
			continue
		}
		go s.Handler(conn)
	}

}

client.go

package main

import (
	"flag"
	"fmt"
	"io"
	"net"
	"os"
)

type Client struct {
	ServerIp   string
	ServerPort int
	Name       string
	conn       net.Conn
	flag       int
}

func NewClient(serverIp string, serverPort int) *Client {
	client := &Client{
		ServerIp:   serverIp,
		ServerPort: serverPort,
		flag:       9999,
	}
	conn, error := net.Dial("tcp", fmt.Sprintf("%s:%d", serverIp, serverPort))
	if error != nil {
		fmt.Println("net dial error...")
		return nil
	}
	client.conn = conn
	return client
}

func (c *Client) menu() bool {
	var flag int

	fmt.Println("1.公聊模式")
	fmt.Println("2.私聊模式")
	fmt.Println("3.修改用户名")
	fmt.Println("0.退出")

	fmt.Scanln(&flag)
	if flag >= 0 && flag <= 3 {
		c.flag = flag
		return true
	} else {
		fmt.Println(">>>>请输入合法数字<<<<")
		return false
	}
}

//修改用户名
func (c *Client) UpdateName() bool {
	fmt.Println(">>>>请输入用户名")
	fmt.Scanln(&c.Name)

	sendMsg := "rename|" + c.Name + "\n"
	_, error := c.conn.Write([]byte(sendMsg))
	if error != nil {
		fmt.Println("conn.write error...")
		return false
	}
	return true
}

//公聊
func (c *Client) PublicChat() {
	var chatMsg string
	fmt.Println(">>>>请输入聊天内容,输入exit退出")
	fmt.Scanln(&chatMsg)

	for chatMsg != "exit" {
		if len(chatMsg) != 0 {
			msg := chatMsg + "\n"
			_, error := c.conn.Write([]byte(msg))
			if error != nil {
				fmt.Println("conn.Write error....")
				break
			}
		}

		chatMsg = ""
		fmt.Println(">>>>请输入聊天内容,输入exit退出")
		fmt.Scanln(&chatMsg)
	}
}

//私聊
func (c *Client) PrivateChat() {
	var remoteUser string
	var chatMsg string

	c.SelectUsers()

	fmt.Println(">>>>请输入聊天对象的用户名,输入exit退出")
	fmt.Scanln(&remoteUser)
	for remoteUser != "exit" {

		fmt.Println(">>>>请输入聊天内容,输入exit退出")
		fmt.Scanln(&chatMsg)

		for chatMsg != "exit" {
			if len(chatMsg) != 0 {
				msg := "to|" + remoteUser + "|" + chatMsg + "\n\n"
				_, error := c.conn.Write([]byte(msg))
				if error != nil {
					fmt.Println("conn.Write error....")
					break
				}
			}

			chatMsg = ""
			fmt.Println(">>>>请输入聊天内容,输入exit退出")
			fmt.Scanln(&chatMsg)
		}

		c.SelectUsers()
		remoteUser = ""
		fmt.Println(">>>>请输入聊天对象的用户名,输入exit退出")
		fmt.Scanln(&remoteUser)
	}

}

//查询在线用户
func (c *Client) SelectUsers() {
	sendMsg := "who\n"
	_, error := c.conn.Write([]byte(sendMsg))
	if error != nil {
		fmt.Println("conn.Write error....")
		return
	}
}

//处理server返回的消息
func (c *Client) DealResponse() {
	io.Copy(os.Stdout, c.conn)
}
func (c *Client) Run() {
	for c.flag != 0 {
		for c.menu() != true {

		}
		switch c.flag {
		case 1:
			//公聊
			c.PublicChat()
		case 2:
			//私聊
			c.PrivateChat()
		case 3:
			//修改用户名
			c.UpdateName()
		}
	}
}

var serverIp string
var serverPort int

func init() {
	flag.StringVar(&serverIp, "ip", "127.0.0.1", "设置服务器IP地址(默认为127.0.0.1)")
	flag.IntVar(&serverPort, "port", 8888, "设置服务器端口(默认为8888)")
}
func main() {
	flag.Parse()
	client := NewClient(serverIp, serverPort)
	if client == nil {
		fmt.Println(">>>>链接服务器失败")
		return
	}

	go client.DealResponse()

	fmt.Println(">>>>链接服务器成功")
	client.Run()
}

user.go

package main

import (
	"net"
	"strings"
)

type User struct {
	Name   string
	Addr   string
	C      chan string
	conn   net.Conn
	server *Server
}

func NewUser(conn net.Conn, server *Server) *User {
	userAddr := conn.RemoteAddr().String()
	user := &User{
		Name:   userAddr,
		Addr:   userAddr,
		C:      make(chan string),
		conn:   conn,
		server: server,
	}
	go user.ListenMessage()
	return user
}

//用户上线
func (u *User) Online() {
	u.server.mapLock.Lock()
	u.server.OnlineMap[u.Name] = u
	u.server.mapLock.Unlock()

	u.server.BroadCast(u, "上线")
}

//用户下线
func (u *User) Offline() {
	u.server.mapLock.Lock()
	delete(u.server.OnlineMap, u.Name)
	u.server.mapLock.Unlock()

	u.server.BroadCast(u, "下线")
}

//给当前user的客户端发送消息
func (u *User) SendMsg(msg string) {
	u.conn.Write([]byte(msg))
}

//处理消息
func (u *User) DoMessage(msg string) {

	if msg == "who" {
		//查询当前在线用户
		u.server.mapLock.Lock()
		for _, user := range u.server.OnlineMap {
			onlineMsg := "[" + user.Addr + "]" + user.Name + ":在线...\n"
			u.SendMsg(onlineMsg)
		}
		u.server.mapLock.Unlock()

	} else if len(msg) > 7 && msg[:7] == "rename|" {
		//修改用户名 rename|xxx
		newName := strings.Split(msg, "|")[1]
		//判断名字是否已经存在
		_, ok := u.server.OnlineMap[newName]
		if ok {
			u.SendMsg("用户名已存在\n")
		} else {

			u.server.mapLock.Lock()
			delete(u.server.OnlineMap, u.Name)
			u.server.OnlineMap[newName] = u
			u.server.mapLock.Unlock()

			u.Name = newName
			u.SendMsg("用户名成功修改为:" + newName + "\n")
		}

	} else if len(msg) > 4 && msg[:3] == "to|" {
		//私聊  to|zhangsan|你好

		//获取对方用户名
		remoteName := strings.Split(msg, "|")[1]
		if remoteName == "" {
			u.SendMsg("用户名格式不对\n")
			return
		}
		//获取对方user
		remoteUser, ok := u.server.OnlineMap[remoteName]
		if !ok {
			u.SendMsg("用户不存在\n")
			return
		}
		//获取消息
		msg := strings.Split(msg, "|")[2]
		if msg == "" {
			u.SendMsg("无消息内容,重新发送\n")
		}
		//发送消息
		remoteUser.SendMsg(u.Name + "对您说:" + msg)

	} else {
		u.server.BroadCast(u, msg)
	}
}

func (u *User) ListenMessage() {
	for {
		msg := <-u.C
		u.conn.Write([]byte(msg + "\n"))
	}
}

main.go

package main

func main() {
	server := NewServer("127.0.0.1", 8888)
	server.Start()
}

到此这篇关于300行代码实现go语言即时通讯聊天室的文章就介绍到这了,更多相关go语言即时通讯聊天室内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go语言多人聊天室项目实战

    本文为大家分享了Go语言多人聊天室项目实战,供大家参考,具体内容如下 功能需求 实现单撩 实现群撩 实现用户上线的全网通知 实现用户昵称 实现聊天日志的存储和查看 服务端实现 type Client struct { conn net.Conn name string addr string } var ( //客户端信息,用昵称为键 //clientsMap = make(map[string]net.Conn) clientsMap = make(map[string]Client) ) f

  • 利用GO语言实现多人聊天室实例教程

    前言 运用go里面的net包中的相关方法来实现一个基于tcp的简单多人聊天室,用一个服务器来管理,主要反馈客户端是否连接成功并显示客户端输入的内容,并且发送给每一个在服务器上连接的客服端,下面话不多说了,来一起看看详细的介绍吧. 示例代码 服务器代码 // server package main import ( "fmt" "net" ) var ConnMap map[string]*net.TCPConn func checkErr(err error) in

  • Go语言实现一个简单的并发聊天室的项目实战

    目录 写在前面 并发聊天服务器 具体代码 服务端 客户端 总结 写在前面 Go语言在很多方面天然的具备很多便捷性,譬如网络编程,并发编程.而通道则又是Go语言实现并发编程的重要工具,因为其承担着通道之间互相通信的重任.并且因为其本身就是并发安全的,所以在某些场景下是非常好用的. 并发聊天服务器 这里主要是实现一个简单的并发聊天服务器.首先,客户端可以在服务器中注册自己的信息(登录以及退出),客户端发出的所有的信息由服务器向各个客户端进行转发,或者换句话说是广播. 具体代码 服务端 说的再多,没有

  • 300行代码实现go语言即时通讯聊天室

    学了2年Java,因为工作原因需要转Golang,3天时间学习了下go的基本语法,做这样一个聊天室小项目来巩固串联一下语法. 实现的功能:公聊,私聊,修改用户名 只用到了四个类: main.go:用来启动服务器 server.go:服务器相关代码 client.go:客户端相关代码,用户可以直接操作的可视化界面 user.go:用户类,用来封装用户的业务逻辑 架构图 完整代码 server.go package main import ( "fmt" "io" &q

  • 300行代码让外婆实现语音搜索购物功能

    "阿强,手写板怎么又不见了?" 最近,程序员阿强的那位勇于尝试新事物的外婆,又迷上了网购.在不太费劲儿地把购物软件摸得门儿清之后,没想到,本以为顺畅的网购之路,卡在了搜索物品上. 在手写输入环节,要么误操作,无意中更换到不熟悉的输入法:要么误按了界面上抽象的指令字符--于是阿强也经常收到外婆发来的求助. 其实,不止是购物应用,时下智能手机里装载的大部APP,都是倾斜于年轻群体的交互设计,老年人想要体验学会使用,很难真香. 在一次次耐心指导外婆完成操作后,阿强,这个成熟coder给自己提

  • Python小游戏之300行代码实现俄罗斯方块

    前言 本文代码基于 python3.6 和 pygame1.9.4. 俄罗斯方块是儿时最经典的游戏之一,刚开始接触 pygame 的时候就想写一个俄罗斯方块.但是想到旋转,停靠,消除等操作,感觉好像很难啊,等真正写完了发现,一共也就 300 行代码,并没有什么难的. 先来看一个游戏截图,有点丑,好吧,我没啥美术细胞,但是主体功能都实现了,可以玩起来. 现在来看一下实现的过程. 外形 俄罗斯方块整个界面分为两部分,一部分是左边的游戏区域,另一部分是右边的显示区域,显示得分.速度.下一个方块样式等.

  • 如何利用微信小程序和php实现即时通讯聊天功能

    目录 一.PHP7安装Swoole扩展 1.自定义安装 2.宝塔面板安装PHP swoole扩展 二.配置nginx反向代理 三.微信小程序socket合法域名配置 四.效果演示和代码 1.小程序端代码 2.服务端代码(PHP代码) 五.代码已经编写完了 总结 一.PHP7安装Swoole扩展 PHP swoole 扩展下载地址 Github:https://github.com/swoole/swoole-src/tags php官方扩展库:http://pecl.php.net/packag

  • 不到20行代码用Python做一个智能聊天机器人

    伴随着自然语言技术和机器学习技术的发展,越来越多的有意思的自然语言小项目呈现在大家的眼前,聊天机器人就是其中最典型的应用,今天小编就带领大家用不到20行代码,运用两种方式搭建属于自己的聊天机器人. 1.神器wxpy库 首先,小编先向大家介绍一下本次运用到的python库,本次项目主要运用到的库有wxpy和chatterbot. wxpy是在 itchat库 的基础上,通过大量接口优化,让模块变得简单易用,并进行了功能上的扩展.什么是接口优化呢,简单来说就是用户直接调用函数,并输入几个参数,就可以

  • C#制作简单的多人在线即时交流聊天室

    实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询.长连接+长轮询.基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSocket,当然HTML5目前在PC端并没有被所有浏览器支持,所以我的这个聊天室仍是基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室,这个聊天室其实是我上周周末完成的,功能简单,可能有些不足,但可以满足在线即时聊天需求,分享也是给大家提供一个思路,大家可以基于此来实现更好的在线即时聊

  • Python3 socket即时通讯脚本实现代码实例(threading多线程)

    Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法. 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发. ------------------------------------------------服务端代码-------------------------------------- __author__ = "

  • 神级程序员JavaScript300行代码搞定汉字转拼音

    一.汉字转拼音的现状 首先应该说,汉字转拼音是个强需求,比如联系人按拼音字母排序/筛选:比如目的地(典型如机票购买) 按拼音首字母分类等等.但是这个需求的解决方案,但好像没听过什么巧妙的实现(特别是浏览器端),大概都需要一个庞大的字典. 具体到JavaScript,查查github和npm,比较优秀的处理汉字转拼音的库有pinyin 和pinyinjs,可以看到,两者都自带了庞大的字典. 这些字典动辄几十上百KB(有的甚至几MB),想在浏览器端使用还是需要一些勇气的.所以当我们碰到汉字转拼音的需

  • C语言实现简易网络聊天室

    本文实例为大家分享了C语言实现网络聊天室的具体代码,供大家参考,具体内容如下 业务逻辑: 1.客户端注册名字 2.告诉所有在线的客户端,XXX进入聊天室 3.新建一个线程为该客户端服务,随时接收客户端发送来的消息 4.当接收到一个客户端的消息时,向每一个客户端转发一份(群聊) 5.同时在线人数最多50人 任何客户端可以随意随时进入或退出客户端 服务端代码server.c #include <stdio.h> #include <stdlib.h> #include <stri

随机推荐