package main

import (
"net"
"fmt"
"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 WriteMsgToClient(cli Client, conn net.Conn) {
for msg := range cli.C {
//给当前客户端发送信息
conn.Write([]byte(msg + "\n"))
}
} func MakeMsg(cli Client, msg string) (buf string) {
buf = "[" + cli.Addr + "]" + cli.Name + ": login"
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") var isQuit = make(chan bool) hasData := make(chan bool) go func() {
buf := make([]byte, 2048)
for true {
n, err := conn.Read(buf)
if n == 0 { //对方断开,或者出问题
isQuit <- true
fmt.Println("conn.read err = ", err)
return
}
msg := string(buf[:n-1])
if len(msg) == 3 && msg == "who" {
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" {
//rename|mike
name := strings.Split(msg,"|")[1]
cli.Name = name
onlineMap[cliAddr] = cli
conn.Write([]byte("user list :\n"))
}else {
message <- MakeMsg(cli, msg)
}
}
}() for true {
select {
case <- isQuit:
delete(onlineMap, cliAddr) //当前用户从map移除
message <- MakeMsg(cli, "log out") //广播谁下线了
return
case <- hasData:
//不做操作
case time.After(60*time.Second): //60秒都没有操作了,超时
delete(onlineMap, cliAddr) //当前用户从map移除
message <- MakeMsg(cli, "tiem out") //广播谁下线了
return
}
}
} func Manager() {
//给map分配空间map
onlineMap = make(map[string]Client) for true {
msg := <-message //没有消息前,这里会阻塞
//遍历map,给map每个成员都发送此消息
for _, cli := range onlineMap{
cli.C <- msg
}
} } func main() {
//创建监听
listener, err := net.Listen("tcp", ":8000")
if err != nil {
fmt.Println("net.Listen err = ", err)
return
} //新开一个协程,用于转发消息,只要有消息到达 ,那就遍历map然后给map每个成员都发送消息
go Manager() //主协程,循环阻塞等待用户连接
for true {
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener.Accept err = ", err)
continue //如果这个不发,説不定还有下个发呢
}
//处理用户的连接
go HandleConn(conn)
}
}

执行的结果,当有任何一个新用户登录了,其他所有的用户都会收到登录提醒,这里以ip加端口号的试做为一个唯一标识

[GO]并发实现聊天室服务器的更多相关文章

  1. Python Socket 编程——聊天室示例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...

  2. Linux聊天室项目 -- ChatRome(select实现)

    序 项目简介:采用I/O复用技术select实现socket通信,采用多线程负责每个客户操作处理,完成Linux下的多客户聊天室! OS:Ubuntu 15.04 IDE:vim gcc make D ...

  3. 基于LINUX的多功能聊天室

    原文:基于LINUX的多功能聊天室 基于LINUX的多功能聊天室 其实这个项目在我电脑已经躺了多时,最初写完项目规划后,我就认认真真地去实现了它,后来拿着这个项目区参加了面试,同样面试官也拿这个项目来 ...

  4. 【C】——网络编程-聊天室

    功能介绍: 此demo是基于TCP套接字编程,目的是实现一个聊天室效果.类似于QQ群效果,如果上线可以通知其他好友,下线也会通知其他好友. 需要用的技术: 一.socket编程. 1> sock ...

  5. Netty网络聊天室之会话管理

    写过web的同学们应该对Session这个东西很熟悉.浏览器第一次与服务器建立连接的时候,服务器就会自动为之分配一个Session.Session可以用来判断用户是否经过登录验证,也可以保存用户的各种 ...

  6. ASP.NET SingalR + MongoDB 实现简单聊天室(三):实现用户群聊,总结完善

    前两篇已经介绍的差不多了,本篇就作为收尾. 使用hub方法初始化聊天室的基本步骤和注意事项 首先确保页面已经引用了jquery和singalR.js还有对应的hubs文件,注意,MVC框架有时会将jq ...

  7. 基于EPOLL模型的局域网聊天室和Echo服务器

    一.EPOLL的优点 在Linux中,select/poll/epoll是I/O多路复用的三种方式,epoll是Linux系统上独有的高效率I/O多路复用方式,区别于select/poll.先说sel ...

  8. 利用html 5 websocket做个山寨版web聊天室(手写C#服务器)

    在之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了web ...

  9. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(二) 之 ChatServer搭建,连接服务器,以及注意事项。

    上篇:ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(一) 之 基层数据搭建,让数据活起来(数据获取) 上一篇我们已经完成了初步界面的搭建工作,本篇将介绍IM的核心内容 ...

随机推荐

  1. java结构

    package com.hanqi;// 包名,必须在第一行,和namespace类似 //import java.io.*;//引用,和using类似 //import java.lang.*;// ...

  2. zabbix 报警方式之 微信公众号报警(5)

    一.条件 首先你得有一个微信公众号,并且是可以有发送消息的接口.然后你得有个脚本去调用微信的api. 这里感谢一下微信.使我们运维人员的报警方式多了一种... (同事们不要怪我哈.) 之后可以参考下z ...

  3. MySQL的安装和基本管理

    ---恢复内容开始--- MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle(甲骨文)旗下公司.MySQL最流行的关系型数据库管理系统.在web应用方面MySQ ...

  4. spring 声明式事务的坑 @Transactional 注解

    1.首先环境搭建,jar 我就不写了,什么一些spring-core.jar spring-beans.jar spring-content.jar 等等一些包 省略..... 直接上图: sprin ...

  5. 这样学Linux基本命令,事半功倍

    命令基本格式 (1)命令提示符 如下是命令行的命令提示符,以此为例,讲解含义. 其中: root 当前登录用户名 localhost 主机名 ~ 当前所在的目录(即家目录,用户登录的初始位置) # 超 ...

  6. Tkinter grid() 方法

        Tkinter grid() 方法:这个的几何管理器组织在父部件的表状结构中的部件.   这的几何管理器组织表状结构中的小部件的父部件. 语法: widget.grid( grid_optio ...

  7. 用Python将word文件转换成html(转)

    用Python将word文件转换成html   序 最近公司一个客户大大购买了一堆医疗健康方面的科普文章,希望能放到我们正在开发的健康档案管理软件上.客户大大说,要智能推送!要掌握节奏!要深度学习!要 ...

  8. 【源码阅读】Java集合之二 - LinkedList源码深度解读

    Java 源码阅读的第一步是Collection框架源码,这也是面试基础中的基础: 针对Collection的源码阅读写一个系列的文章; 本文是第二篇LinkedList. ---@pdai JDK版 ...

  9. SYN Flood 防范

    简介: SYN Flood 是 DoS( 拒绝服务攻击 )与 DDoS( 分布式拒绝服务攻击 )的方式之一,这是一种利用 TCP 协议缺陷,发送大量伪造 TCP 连接请求,从而使得服务器资源耗尽( C ...

  10. 用Eclipse进行远程Debug代码

    在新的公司,由于项目很大,在本机运行会很慢,所以都是在本地开发,在远程虚拟机上运行.这样就让我痛苦了,我怎么在本地Eclipse上进行debug调试呢,但是在公司前辈的指导下让我知道了本地Eclips ...