当前位置: 首页 > news >正文

一个seo良好的网站其主要流量往往来自中国建设银行网站首页u盾登入

一个seo良好的网站其主要流量往往来自,中国建设银行网站首页u盾登入,如何修改wordpress颜色,宁波产城生态建设集团网站欢迎来到Golang的世界!在当今快节奏的软件开发领域,选择一种高效、简洁的编程语言至关重要。而在这方面,Golang(又称Go)无疑是一个备受瞩目的选择。在本文中,带领您探索Golang的世界,一步步地了…

欢迎来到Golang的世界!在当今快节奏的软件开发领域,选择一种高效、简洁的编程语言至关重要。而在这方面,Golang(又称Go)无疑是一个备受瞩目的选择。在本文中,带领您探索Golang的世界,一步步地了解这门语言的基础知识和实用技巧。

在这篇文章中,我们将用Go语言实现一个简易网络聊天应用,重点探讨Socket编程、map结构用于管理用户、goroutines与channels实现并发通信、select语句处理超时与主动退出,以及timer定时器的应用。这些概念将帮助我们构建高效且实用的聊天系统。让我们开始吧!

目录

socket-server建立

创建msg广播通道

查询用户与重命名

用户主动退出聊天

用户超时退出聊天


socket-server建立

socket-server的作用是实现网络通信的基础,允许不同设备(如客户端和服务器)通过网络交换数据,下面我们模拟TCP服务器能够接收多个客户端的连接请求,并在每个连接上启动一个新的goroutine进行数据处理。每当有数据从客户端发送到服务器时,服务器会读取并打印这些数据:

package mainimport ("fmt""net"
)func main() {// 01 创建服务器listener, err := net.Listen("tcp", ":8080")if err != nil {fmt.Println("net.listen err:", err)return} else {fmt.Println("服务器启动成功...")}for {fmt.Println("主go程监听中...")// 02 监听服务器connect, err := listener.Accept()if err != nil {fmt.Println("listener.accept err:", err)return}fmt.Println("建立连接成功...")// 03 启动处理业务的go程go handler(connect)}}func handler(conn net.Conn) {for {fmt.Println("启动处理业务")// TODO// 读取客户端发送的数据buf := make([]byte, 1024)cnt, err := conn.Read(buf)if err != nil {fmt.Println("conn.read err:", err)return} else {fmt.Println("服务器接收客户端发送过来的数据为:", string(buf[:cnt-1]), "cnt:", cnt)}}}

这种设计使得服务器具有并发处理能力,可以同时处理多个客户端的请求,这里我们借助nc工具来模拟请你,不了解工具的可以参考我之前的文章:地址 ,具体如下所示:

创建msg广播通道

要知道我们程度当中是有很多用户的,当一个用户发送消息能让所有的用户看到的话是需要有一个进行全局广播的管道:message,如下所示全局广播的message获取到“hello”,然后遍历所有的用户并向用户msg管道发送hello,在go程中每一个用户连接一个需要再启动一个go程,读取message数据之后发送给客户端:

接下来我们开始创建User结构,用于管理每次创建用户的结构:

// User 定义用户结构体
type User struct {id   stringname stringmsg  chan string
}// 创建全局的map结构,用于保存所有的用户
var allUsers = make(map[string]User)

然后我们再每次创建go程的时候以连接的key作为唯一添加到用户的map结构当中:

接下来我们定义全局的管道,用于接收任何人发送过来的消息:

// 定义一个message全局通道,用于接收任何人发送过来的消息
var message = make(chan string, 10)

接下来再每次创建新用户上线的时候,写入message:

接下来创建一个全局唯一的广播通道用于通知用户消息,然后在main函数中调用一次下面的go程即可:

// 向所有的用户广播消息,启动全局唯一go程
func broadcast() {fmt.Println("启动广播go程...")defer fmt.Println("broadcast程序结束...") // 程序结束,关闭广播go程for {fmt.Println("广播go程监听中...")// 01 从message通道中读取消息info := <-message// 02 遍历map结构,向每个用户发送消息for _, user := range allUsers {// 03 向每个用户发送消息user.msg <- info}}
}

接下来每个用户应该还有一个用来监听自己msg管道的go程,负责将数据返回给客户端:

// 每个用户监听自己的msg通道,负责将数据返回给客户端
func writeBackToClient(user *User, conn net.Conn) {fmt.Println("启动用户", user.name, "的writeBackToClient go程...")for data := range user.msg {fmt.Printf("user: %s 写回给客户端的数据为: %s\n", user.name, data)_, _ = conn.Write([]byte(data))}
}

查询用户与重命名

查询用户:当用户输入查询命令who,则将当前所有登录的用户展示出来,id与name返回给当前用户:

// 01 查询当前所有的用户 who
if len(buf[:cnt-1]) == 3 && string(buf[:cnt-1]) == "who" {var userInfos []string// 遍历map结构,获取所有的用户信息for _, user := range allUsers {userInfo := fmt.Sprintf("userid:%s, username: %s", user.id, user.name)userInfos = append(userInfos, userInfo)}// 最终写到管道中message <- strings.Join(userInfos, "\n")
}

重命名:这里我们可以设置一个规则:rename | Duke,使用竖线进行分割获取竖线后面的部分作为名字,通过设置 newUser.name = Duke,然后通知客户端更新名字成功,为了避免想输入命令作为消息,这里我们对命令做一个处理:

// 01 查询当前所有的用户 who
if len(buf[:cnt-1]) == 4 && string(buf[:cnt-1]) == "\\who" {var userInfos []string// 遍历map结构,获取所有的用户信息for _, user := range allUsers {userInfo := fmt.Sprintf("userid:%s, username: %s", user.id, user.name)userInfos = append(userInfos, userInfo)}// 最终写到管道中newUser.msg <- strings.Join(userInfos, "\n")
} else if len(buf[:cnt-1]) > 9 && string(buf[:7]) == "\\rename" {// 更新名字newUser.name = strings.Split(string(buf[:cnt-1]), "|")[1]allUsers[newUser.id] = newUser // 更新map结构中的用户信息// 通知客户端更新成功newUser.msg <- fmt.Sprintf("改名成功, 新的名字为: %s", newUser.name)
} else {message <- string(buf[:cnt-1])
}

用户主动退出聊天

接下来我们通过使用ctrl+c的方式进行退出程序,用户退出还需要做一下清理工作,需要从map当中删除用户信息,还需要将对应的conn连接进行close,具体如下所示:

// 启动一个go程,负责监听退出信号,通知所有go程退出
func watch(user *User, conn net.Conn, isQuit chan bool) {fmt.Println("启动用户", user.name, "的watch go程...")defer fmt.Println("watch程序结束...") // 程序结束,关闭监听go程for {select {case <-isQuit: // 收到退出信号,通知所有go程退出delete(allUsers, user.id)fmt.Println("删除当前用户:", user.name)message <- fmt.Sprintf("[%s][%s]下线了", user.id, user.name)_ = conn.Close()}}
}

在handler中启动go watch并传入对应信息:

然后在read之后,通过读取cnt判断用户是否退出,向isQuit中写入信息:

最终实现的效果如下所示:

用户超时退出聊天

这里我们可以设置使用定时器来进行超时管理,如果60s内没有发送任何消息的情况下就直接将这个连接关闭掉:

// 启动一个go程,负责监听退出信号,通知所有go程退出
func watch(user *User, conn net.Conn, isQuit chan bool, resTimer chan bool) {fmt.Println("启动用户", user.name, "的watch go程...")defer fmt.Println("watch程序结束...") // 程序结束,关闭监听go程for {select {case <-isQuit: // 收到退出信号,通知所有go程退出delete(allUsers, user.id)fmt.Println("删除当前用户:", user.name)message <- fmt.Sprintf("[%s][%s]下线了\n", user.id, user.name)_ = conn.Close()returncase <-time.After(10 * time.Second):fmt.Println("删除当前用户:", user.name)delete(allUsers, user.id)message <- fmt.Sprintf("[%s]用户超时下线了\n", user.name)_ = conn.Close()returncase <-resTimer:fmt.Printf("连接%s 重置计数器!\n", user.name)}}
}

这里我们定义一个重置的管道,只要用户不断输入就不会超时,如果用户没有输入超过10s就会触发超时退出的操作:

// 创建一个用于重置计算器的管道,用于告知watch函数当前用户正在输入
var resTimer = make(chan bool)
// 启动go程,负责监听用户退出
go watch(&newUser, conn, isQuit, resTimer)

完整代码如下所示:

package mainimport ("fmt""net""strings""time"
)// User 定义用户结构体
type User struct {id   stringname stringmsg  chan string
}// 创建全局的map结构,用于保存所有的用户
var allUsers = make(map[string]User)// 定义一个message全局通道,用于接收任何人发送过来的消息
var message = make(chan string, 10)func main() {// 01 创建服务器listener, err := net.Listen("tcp", ":8080")if err != nil {fmt.Println("net.listen err:", err)return} else {fmt.Println("服务器启动成功...")// 启动全局唯一go程,用于广播消息go broadcast()}for {fmt.Println("主go程监听中...")// 02 监听服务器connect, err := listener.Accept()if err != nil {fmt.Println("listener.accept err:", err)return}fmt.Println("建立连接成功...")// 03 启动处理业务的go程go handler(connect)}}func handler(conn net.Conn) {fmt.Println("启动处理业务")// 客户端与服务器建立连接的时候,会有ip与port,可以当成user的idclientAddr := conn.RemoteAddr().String()fmt.Println("客户端地址为:", clientAddr)// 创建UsernewUser := User{id:   clientAddr,            // id,不会被修改,作为mao中的keyname: clientAddr,            // 可以修改,会提供rename命令修改,建立连接时初始值与id相同msg:  make(chan string, 10), // 消息通道,注意分配空间}// 添加user到map结构中allUsers[newUser.id] = newUser// 定义一个退出信号,用于通知所有go程退出var isQuit = make(chan bool)// 创建一个用于重置计算器的管道,用于告知watch函数当前用户正在输入var resTimer = make(chan bool)// 启动go程,负责监听用户退出go watch(&newUser, conn, isQuit, resTimer)// 启动用户自己的writeBackToClient go程go writeBackToClient(&newUser, conn)// 向message写入消息,用于通知所有人有用户上线message <- fmt.Sprintf("[%s][%s]上线了", newUser.id, newUser.name)for {buf := make([]byte, 1024)// 读取客户端发送的数据cnt, err := conn.Read(buf)if cnt == 0 {fmt.Println("客户端主动关闭ctrl+c,准备退出")// 在这里不进行真正的退出动作,只是通知所有go程退出isQuit <- true}if err != nil {fmt.Println("conn.read err:", err, "cnt", cnt)return} else {fmt.Println("服务器接收客户端发送过来的数据为:", string(buf[:cnt-1]), "cnt:", cnt)// -------业务逻辑处理开始-------// 01 查询当前所有的用户 whoif len(buf[:cnt-1]) == 4 && string(buf[:cnt-1]) == "\\who" {var userInfos []string// 遍历map结构,获取所有的用户信息for _, user := range allUsers {userInfo := fmt.Sprintf("userid:%s, username: %s", user.id, user.name)userInfos = append(userInfos, userInfo)}// 最终写到管道中newUser.msg <- strings.Join(userInfos, "\n")} else if len(buf[:cnt-1]) > 9 && string(buf[:7]) == "\\rename" {// 更新名字newUser.name = strings.Split(string(buf[:cnt-1]), "|")[1]allUsers[newUser.id] = newUser // 更新map结构中的用户信息// 通知客户端更新成功newUser.msg <- fmt.Sprintf("改名成功, 新的名字为: %s", newUser.name)} else {message <- string(buf[:cnt-1])}resTimer <- true // 发送一个信号,告知watch函数当前用户正在输入// -------业务逻辑处理结束-------}}
}// 向所有的用户广播消息,启动全局唯一go程
func broadcast() {fmt.Println("启动广播go程...")defer fmt.Println("broadcast程序结束...") // 程序结束,关闭广播go程for {fmt.Println("广播go程监听中...")// 01 从message通道中读取消息info := <-messagefmt.Println("广播消息为:", info)// 02 遍历map结构,向每个用户发送消息for _, user := range allUsers {// 03 向每个用户发送消息user.msg <- info}}
}// 每个用户监听自己的msg通道,负责将数据返回给客户端
func writeBackToClient(user *User, conn net.Conn) {fmt.Println("启动用户", user.name, "的writeBackToClient go程...")for data := range user.msg {fmt.Printf("user: %s 写回给客户端的数据为: %s\n", user.name, data)_, _ = conn.Write([]byte(data))}
}// 启动一个go程,负责监听退出信号,通知所有go程退出
func watch(user *User, conn net.Conn, isQuit chan bool, resTimer chan bool) {fmt.Println("启动用户", user.name, "的watch go程...")defer fmt.Println("watch程序结束...") // 程序结束,关闭监听go程for {select {case <-isQuit: // 收到退出信号,通知所有go程退出delete(allUsers, user.id)fmt.Println("删除当前用户:", user.name)message <- fmt.Sprintf("[%s][%s]下线了\n", user.id, user.name)_ = conn.Close()returncase <-time.After(10 * time.Second):fmt.Println("删除当前用户:", user.name)delete(allUsers, user.id)message <- fmt.Sprintf("[%s]用户超时下线了\n", user.name)_ = conn.Close()returncase <-resTimer:fmt.Printf("连接%s 重置计数器!\n", user.name)}}
}
http://www.yayakq.cn/news/134523/

相关文章:

  • 做网站首次备案需要哪些资料vi系统整套设计
  • 外贸seo网站做外贸好的网站有哪些
  • 果乐宝的网站建设网站开发 微盘
  • 网站导流应该怎么做网页设计与制作教程欧静美
  • 网站建设网页怎么排列顺序网站备案号有效期
  • 深圳外贸公司哪里集中网站建设与优化及覆盖率方案
  • 中山网站建设收费标准成都网站建设培训
  • 网站搭建h5是什么西安软件优化网站建设
  • 上海网站建设到诺然内江市建设信息网站
  • 网站做收付款接口带分销功能的小程序
  • 磁业 东莞网站建设wordpress外链本地化
  • 有没有网站开发团队去除网址中 wordpress
  • 顾家家居网站是哪个公司做的天津优化网站哪家好用
  • 可做外链的网站品牌运营岗位职责
  • 公司网站源码 带wap手机站郑州百姓网招聘信息官网
  • 做网站需要编程嘛wordpress 待办事项
  • 建设部网站黑臭水体公布站长之家 wordpress 流量统计
  • 买网站送域名有哪些免费做网站
  • 罗湖商城网站设计推荐网站优化排名首页
  • 网站的建设与推广帮做论文网站吗
  • 人物设计网站wordpress主题 淘客
  • 凡科互动游戏修改器一流的镇江网站优化
  • 四川手机网站怎样自创广告网站
  • 做网站哪家公司专业南宁小程序开发网站建设公司
  • 易企网站建设公司霸屏网站开发
  • 网站一级页面二级页面怎么做哪个网站可以做彩经专家
  • 平面设计素材网站哪个好服务器 wordpress 邮件
  • 网站用什么语言开发网站开发ceil(5.5)
  • 穿衣搭配的网站如何做三亚百度推广公司
  • 哪些网站建设公司浅议我国旅游景点网站的建设