并发聊天服务器编写

news2025/1/21 15:43:32

并发聊天服务器

在这里插入图片描述

package main

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

// 结构体
type Client struct {
	C    chan string //用户发送数据的管道
	Name string      //用户名
	Addr string      //网络地址
}

// 保存在线用户 cliAddr -->client
var onlineMap map[string]Client

// 创建一个通信的管道
var message = make(chan string)

func MakeMsg(cli Client, msg string) (buf string) {
	buf = "[" + cli.Addr + "]" + cli.Name + ": " + msg
	return
}

// WriteMsgToClient 专门给当前客户端发送信息
func WriteMsgToClient(cli Client, conn net.Conn) {
	for msg := range cli.C { //给当前客户端发送信息
		_, err := conn.Write([]byte(msg + "\n"))
		if err != nil {
			fmt.Println("conn write err =", err)
			return
		}
	}
}

// HandleConn //处理用户链接
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 <- "[" + cli.Addr + "]" + cli.Name + ": login"
	message <- MakeMsg(cli, "login")
	//提示,我是谁
	cli.C <- MakeMsg(cli, "I am hello")

	isQuit := make(chan bool) //对方是否主动退出
	isData := make(chan bool) //对方是否有数据发送
	//新建一个协程,接收用户发送过来的数据
	go func() {
		buf := make([]byte, 2048)
		for {
			readSize, err := conn.Read(buf)
			//if err != nil {
			//	if err == io.EOF {
			//		fmt.Println("写入完毕")
			//	}
			//	return
			//}

			if readSize == 0 { //对象断开,或者出问题
				isQuit <- true
				fmt.Println("conn read err = ", err)
				return
			}

			msg := string(buf[:readSize-1])

			if len(msg) == 3 && msg == "who" { //查询在线用户
				//遍历map,get当前用户发送所有成员
				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 ok\n"))
			} else {
				//转发此内容
				fmt.Println("成朋")
				message <- MakeMsg(cli, msg)
			}

			isData <- true //代表有数据
		}
	}()

	for true {
		//通过select检测channel的流动
		select {
		case <-isQuit:
			delete(onlineMap, cliAddr)           //当前用户从map移除
			message <- MakeMsg(cli, "login out") //广播谁下线了
			return
		case <-isData:
		case <-time.After(30 * time.Second):
			delete(onlineMap, cliAddr)
			message <- MakeMsg(cli, "time out leave out") //超时了
			return
		}
	}

}

// Manager 新开一个协程 转发消息,只要有消息来了,遍历map,给map每个成员都发送消息
func Manager() {
	//给map分配空间
	onlineMap = make(map[string]Client)
	for true {
		meg := <-message //没有消息前,这里会阻塞
		//遍历map,给map每个成员都发送消息
		for _, client := range onlineMap {
			client.C <- meg //把消息给这个管道
		}
	}
}
func main() {

	//监听
	listener, err := net.Listen("tcp", ":8000")
	if err != nil {
		fmt.Println("listener err = ", err)
		return
	}

	defer listener.Close()

	//新开一个协程 转发消息,只要有消息来了,遍历map,给map每个成员都发送消息
	go Manager()
	//主协程。循环阻塞等待用户连接
	for true {
		conn, err := listener.Accept() //建立连接
		if err != nil {
			fmt.Println("conn err = ", err)
			continue
		}

		go HandleConn(conn) //处理用户链接
	}
}

调试工具netcat

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/991126.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Wireshark技巧[监听串口包]

监听串口包 本文摘录于&#xff1a;https://blog.csdn.net/qq_20405005/article/details/79652927只是做学习备份之用&#xff0c;绝无抄袭之意&#xff0c;有疑惑请联系本人&#xff01; 这里要保证安装了USBpcap: 打开USBpcap后一半都要输入过滤条件,否则USB太多数据了,比如…

输入验证在防止安全漏洞方面的重要性

在当今快速发展的数字环境中&#xff0c;技术推动创新和便利&#xff0c;确保我们数字资产的安全仍然是一个关键问题。创建强大的应用程序安全性的核心在于输入验证的基本且最重要的概念。 在这篇博文中&#xff0c;我们将介绍输​​入验证的重要性及其对加强我们的数字防御以…

python可视化模块—快速利用matplot绘制图表

文章目录 一、Matplotlib基本介绍二、两种绘图方式区别&#xff08;plt.*** 和ax.***&#xff09;三、如何使用Matplotlib绘图1、画布—绘画的画板2、配置—更个性化的绘图全局配置局部配置面向对象绘图过程&#xff1a;ax代表子图变量过程式绘图过程 四、常用绘图图形如何选择…

Java集合学习(2023年超详细)

java集合学习目录 一、基本概要0. 辅助工具类0.1 Collection 和 Collections 有什么区别&#xff1f;0.2 comparable 和 comparator的区别&#xff1f; 1.什么是集合2.集合的分类2.1 Collection接口2.2 Map接口 二、集合框架底层数据结构1. &#x1f60a;Collection1.1 ❤List1…

【数据结构】3000字剖析链表及双向链表

文章目录 &#x1f490; 链表的概念与结构&#x1f490;链表的介绍&#x1f490;链表的模拟实现 &#x1f490;双向链表&#x1f490;双向链表的模拟实现 &#x1f490;链表常用的方法&#x1f490;链表及顺序表的遍历&#x1f490;ArrayList和LinkedList的差异 &#x1f490; …

Python常用库(五):图像处理【Pillow】

1. Pillow 1.1 介绍 Pillow 是第三方开源的 Python 图像处理库&#xff0c;它支持多种图片格式&#xff0c;包括 BMP、GIF、JPEG、PNG、TIFF 等。Pillow 库包含了大量的图片处理函数和方法&#xff0c;可以进行图片的读取、显示、旋转、缩放、裁剪、转换等操作。在后续的深度学…

python开发基础篇2——登陆机制

文章目录 一、管理平台页面布局二、登录页面2.1 token登录2.2. kubeconfig登录2.3 添加装饰器 一、管理平台页面布局 应用名称&#xff1a; dashboard&#xff1a;存放公共 k8s&#xff1a; Node&#xff1a;K8s集群计算节点。Namespaces&#xff1a;命名空间&#xff0c;用于…

ABAP WS_DELIVERY_UPDATE 报错 BS013

在使用 WS_DELIVERY_UPDATE 创建内向交货单时 报错&#xff1a;System status ESTO is active (EQU ***) 可以用事务代码IE03--->历史去看下 你可以手动将它的状态去改回EDEL 或者 SLOR IE02--> Special serial no. functions --> Manual transaction.

C#,《小白学程序》第十七课:随机数(Random)第四,移动平均值(Moving Average)的计算方法与代码

1 文本格式 /// <summary> /// 《小白学程序》第十七课&#xff1a;随机数&#xff08;Random&#xff09;第四&#xff0c;移动平均值的计算方法与代码 /// 继续学习数据统计&#xff0c;移动平均值的计算方法 /// 移动平均值就是一定步长内数值的平均值&#xff0c;用…

Llama 2 论文《Llama 2: Open Foundation and Fine-Tuned Chat Models》阅读笔记

文章目录 Llama 2: Open Foundation and Fine-Tuned Chat Models1.简介2.预训练2.1 预训练数据2.2 训练详情2.3 LLAMA 2 预训练模型评估 3. 微调3.1 supervised Fine-Tuning(SFT)3.2 Reinforcement Learning with Human Feedback (RLHF)3.2.1 人类偏好数据收集3.2.2 奖励模型训…

Matlab 如何选择窗函数和 FFT 的长度

Matlab 如何选择窗函数和 FFT 的长度 1、常用的四种窗函数 对于实际信号序列&#xff0c;如何选取窗函数呢&#xff1f;一般来说&#xff0c;选择第一旁瓣衰减大&#xff0c;旁瓣峰值衰减快的窗函数有利于緩解截断过程中产生的頻泄漏问题。但具有这两个特性的窗函数&#xff0…

[BFS] 广度优先搜索

1. 数字操作 常见的模板 // 使用一个数组判断元素是否入过队 int inqueue[N] {0}; // 层数或者可以称为深度 int step 0; // 判断是否可以入队的条件 int isvalid(){ } BFS(int x){ // 将初始的元素压入队列 // 注意每次压队的时候都要将inque[x] 1,表明入队过…

python实现adb辅助点击屏幕工具

#!/usr/bin/env python # -*- coding: utf-8 -*-import re import os import time import subprocess import tkinter as tk from tkinter import messagebox from PIL import Image, ImageTk# 设置ADB路径&#xff08;根据你的系统和安装路径进行调整&#xff09; ADB_PATH C…

Ubuntu快速搭建内网NTP Server

文章目录 安装NTP服务配置NTP配置NTP 同步源配置NTP 允许客户端访问重启NTP服务使得配置生效 推荐阅读 NTP(Network Time Protocol)------网络时间协议-----应用层协议&#xff0c;用来在分布式时间服务器和客户端之间进行时间同步。 是对网络内所有具有时钟的设备进行时钟同步…

图解 LeetCode 算法汇总——链表

本文首发公众号&#xff1a;小码A梦 一般数据主要存储的形式主要有两种&#xff0c;一种是数组&#xff0c;一种是链表。数组是用来存储固定大小的同类型元素&#xff0c;存储在内存中是一片连续的空间。而链表就不同于数组。链表中的元素不是存储在内存中可以是不连续的空间。…

酒店类型的软文怎么写?

马上就放长假了&#xff0c;有不少酒店行业来找盒子做推广&#xff0c;其实酒店行业想要写好软文只要掌握三种类型就好了&#xff0c;今天就让盒子告诉大家酒店类型的软文怎么写才能吸引用户。 一、以故事打动用户 故事型软文大多用于民宿酒店&#xff0c;民宿酒店文案除了展现…

Docker认识即安装

Docker及相关概念 Docker和虚拟机方式的区别&#xff1a;虚拟机技术是虚拟出一套硬件后&#xff0c;在其上运行一个完整的操作系统&#xff0c;在该系统上在运行所需应用进程&#xff1b;而容器内的应用进程是直接运行于宿主的内核&#xff0c;容器内没有自己的内核&#xff0…

2023区块链应用操作员认证(4级)报名来弘博创新

区块链应用操作员&#xff0c;是指运用区块链技术及工具&#xff0c;从事政务、金融、医疗、教育、养老等场景系统应用操作的人员。 腾讯作为广东省第一批公布的社会培训评价组织&#xff0c;可开展职业技能等级认定职业(工种)区块链应用操作员(4-3-2-1级)。 证书含金量 证书是…

小节4:input()函数的一些讲究

千万注意&#xff1a;不管用户输入的是什么&#xff0c;input()返回的一律都是字符串&#xff0c;代码如下&#xff1a; user_input input("Please input something: ") print(type(user_input)) 所以&#xff0c;如果直接拿用户输入的内容去做数学运算&#xff0c…

JavaScript 之 常用迭代方法forEach、filter()、map()、reduce()

JavaScript 之 常用迭代方法forEach、filter、map、reduce 1. for、forEach1.1 for 遍历的3种写法1.2 forEach 回调函数 遍历1.3 forEach 箭头函数 遍历 2. filter()2.1 介绍2.2 例子1——简单过滤2.3 例子2——在修改数组时 filter() 方法的行为2.4 例子3——在数组中搜索 3…