Golang聊天室
一、前期准备
- 前期准备
- 需要 import "net"包
- IP 类型,其中一个重要的方法是 IP.ParseIP(ipaddr string)来判断是否是合法的 IP 地址
- TCP Client
func (c *TCPConn) Write(b []byte) (n int, err os.Error)
用于发送数据,返回发送的数据长度或者返回错误,是TCPConn的方法func (c *TCPConn) Read(b []byte) (n int, err os.Error)
用于接收数据,返回接收的长度或者返回错误,是 TCPConn 的方法- TCPAddr 类型,保存 TCP 的地址信息,包括地址和端口
type TCPAddr struct {
IP IP
Port int
}
func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error)
获取一个 TCPAddr,参数都是 string 类型,net 是个 const string,包括 tcp4,tcp6,tcp 一般使用 tcp,兼容 v4 和 v6,addr 表示 ip 地址,包括端口号,如www.google.com:80之类的func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error)
用来连接(connect)到远程服务器上,net 表示协议方式,tcp,tcp4 或者 tcp6,laddr 表示本机地址,一般为 nil,raddr 表示远程地址,这里的 laddr 和 raddr 都是 TCPAddr 类型的,一般是上一个函数的返回值。- 作为一个 TCP 的客户端,基本的操作流程如下:
service="www.google.com:80"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
conn, err := net.DialTCP("tcp", nil, tcpAddr)
_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
_, err = conn.Read(b) / result, err := ioutil.ReadAll(conn)
- TCP Server
func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error)
用来监听端口,net 表示协议类型,laddr 表示本机地址,是 TCPAddr 类型,注意,此处的 laddr 包括端口,返回一个*TCPListener类型或者错误func (l *TCPListener) Accept() (c Conn, err os.Error)
用来返回一个新的连接,进行后续操作,这是 TCPListener 的方法,一般 TCPListener 从上一个函数返回得来。- 服务器的基本操作流程为:
service:=":9090"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
l,err := net.ListenTCP("tcp",tcpAddr)
conn,err := l.Accept()
go Handler(conn) //此处使用go关键字新建线程处理连接,实现并发
二、聊天室需求
实现一个公共聊天服务器。
- 服务器接收客户端的信息
- 接收完以后将客户端的信息发送到所有的客户端上
- 客户端使用
/quit
退出聊天 - 只使用一套代码,通过命令行参数启动服务器还是客户端
主要知识点如下:
代码中包括了服务器和客户端的内容,如果是服务器,直接输入go run main.go
server 9090
即可,客户端也很简单,输入go run main.goclient :9090
就好;如果是客户端,其实就包括了两部分内容,一部分是 chatSend 函数,接受用户的输入;另一部分是connect 到 server,接受相关信息;
如果是 server,稍微复杂一点,有三个部分组成。第一部分就是不停地 accept 各个客户端;第二个就是为每一个客户端创立 Handler 函数,接受客户端发来的信息;第三个就是 echoHandler 函数,它的作用就是将从某一用户接受过来的信息广播给其他所有的客户端,就是这么简单。
代码实现:
package main import (
"os"
"fmt"
"net"
) /**
主程序 启动客户端和服务端
参数说明:
启动服务器端: go run main.go server [port] eg: go run main.go server 9090
启动客户端: go run main.go [Server Ip Addr]:[Server Port] eg: go run main.go client :9090
*/
func main() {
if len(os.Args) != {
fmt.Println("wrong params")
os.Exit()
}
if os.Args[] == "server" {
StartServer(os.Args[])
}
if os.Args[] == "client" {
StartClient(os.Args[])
}
} /**
启动服务器
参数:port 端口号
*/
func StartServer(port string) {
service := ":" + port
tcpAddr,err := net.ResolveTCPAddr("tcp4", service)
checkError(err, "ResolveTCPAddr")
l,err := net.ListenTCP("tcp", tcpAddr)
checkError(err ,"ListenTCP")
conns := make(map[string]net.Conn)
messages := make(chan string, ) //启动服务器广播线程 :向所有客户端发送消息
go echoHandler(&conns, messages) for {
fmt.Println("Listening ...")
conn,err := l.Accept()//返回一个新的连接
checkError(err , "l.Accept")
fmt.Println("Accepting ...")
conns[conn.RemoteAddr().String()] = conn
//启动一个接受客户端发送消息的线程
go Handler(conn, messages)
}
}
/**
服务器发送数据的线程:向所有客户端发送消息
参数
连接字典 conns
数据通道 messages
*/
func echoHandler(conns *map[string]net.Conn, messages chan string) {
for {
msg := <-messages
fmt.Println(msg) for key,con := range *conns {
fmt.Println("connection is connected from ...", key)
_,err := con.Write([]byte(msg))
if err != nil {
fmt.Println(err)
delete(*conns, key)
}
} }
} /**
服务器端接收客户端数据线程
参数:
据连接 conn
通讯通道 messages
*/
func Handler(conn net.Conn, messages chan string) {
fmt.Println("connection is connected from ...", conn.RemoteAddr().String()) buf := make([]byte, )
for {
lenght,err := conn.Read(buf)
if checkError(err, "Connection") == false {
conn.Close()
break
}
if lenght > {
buf[lenght] =
}
reciveStr := string(buf[:lenght])
messages <- reciveStr
}
} /**
客户端启动函数
参数:
程ip地址和端口 tcpaddr
*/
func StartClient(tcpaddr string) {
tcpAddr,err := net.ResolveTCPAddr("tcp4", tcpaddr)
checkError(err, "ResolveTCPAddr")
conn,err := net.DialTCP("tcp",nil,tcpAddr)
checkError(err, "DialTCP") //启动客户端发送数据线程
go chatSend(conn) //接收服务端发送来的消息
buf := make([]byte, )
for {
lenght,err := conn.Read(buf)
if checkError(err, "Connection") == false {
conn.Close()
fmt.Println("Server is dead ...ByeBye")
os.Exit()
}
fmt.Println(string(buf[:lenght]))
}
} /**
客户端发送数据线程
参数:
发送连接 conn
*/
func chatSend(conn net.Conn) {
var input string
username := conn.LocalAddr().String()
for {
fmt.Scanln(&input)
if input == "/quit" {
fmt.Println("ByeBye..")
conn.Close()
os.Exit()
}
lens,err := conn.Write([]byte(username + "say ::: " + input))
fmt.Println(lens)
if err != nil {
fmt.Println(err.Error())
conn.Close()
break
}
}
} //错误信息
func checkError(err error, info string) (res bool) {
if err != nil {
fmt.Println(info + ",err:" + err.Error())
return false
}
return true
}
Golang聊天室的更多相关文章
- websocket+golang聊天室
原文地址: http://www.niu12.com/article/3 websocket+golang聊天室 main.go和index.html放在同一目录下 main.go package m ...
- golang 聊天室
近期看了一些关于golang的资料.发现它的WEBSOCKT实现真起来真的非常easy.以下是代码 go.net/websocket是须要安装的,这个自己装上即可 package main impor ...
- Golang语言快速上手到综合实战高并发聊天室
需要的联系我:QQ:1844912514 Go是Google开发的一种编译型,可并行化,并具有垃圾回收功能的编程语言.2015,Go迎来了全迸发的一年.时隔一年,回头再看,Go已跻身主流编程语言行列. ...
- golang简易版聊天室
功能需求: 创建一个聊天室,实现群聊和单聊的功能,直接输入为群聊,@某人后输入为单聊 效果图: 群聊: 单聊: 服务端: package main import ( "fmt" ...
- go 聊天室简单版总结
/* * 思路:在登录成功时将用户的id存进在线用户列表中的key value中链接的ws为空,并保存用户的信息. * 当跳转到聊天室时,将用户和聊天室链接的ws存进在线用户列表中的 * 问题:如何在 ...
- 网络编程-基于Websocket聊天室(IM)系统
目录 一.HTML5 - Websocket协议 二.聊天室(IM)系统的设计 2.1.使用者眼中的聊天系统 2.2.开发者眼中的聊天系统 2.3.IM系统的特性 2.4.心跳机制:解决网络的不确定性 ...
- 利用Node.js的Net模块实现一个命令行多人聊天室
1.net模块基本API 要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用.NET模块API分为两大类:Server和Socket类.工厂方法. Server类 ...
- php+websocket搭建简易聊天室实践
1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...
- 基于select的python聊天室程序
python网络编程具体参考<python select网络编程详细介绍>. 在python中,select函数是一个对底层操作系统的直接访问的接口.它用来监控sockets.files和 ...
随机推荐
- asp.net 添加错误日志
在开发程序中,错误日志很有必要.今天就把使用到的添加错误日志,记录下来,方便以后查看 利用的asp.net错误处理机制 Application_Error 贴出代码 protected void Ap ...
- cesium随笔 — 隐藏三维场景下方版权信息
上图中的版权信息相信很多人都想去掉,那么下面我将介绍一种简单粗暴的方法将其隐藏起来: .cesium-widget-credits { display: none!important; visibil ...
- Websphere中获取项目下.properties路径
一:如果容器为Websphere,那下面为红色的地方不能加"/",如果为tomcat,则加上"/", String path = this.class.get ...
- 深入了解java虚拟机(JVM) 第十章 字节码指令
一.字节码指令的含义 Java字节码指令由一个字节长度的,代表某种特定操作含义的数字(操作码)以及其后的零至多个代表此操作所需参数(操作数).此外字节码指令是面向操作数栈的,这里操作数栈在功能上对应实 ...
- C#取得控制台应用程序的根目录方法
如有雷同,不胜荣幸,若转载,请注明 取得控制台应用程序的根目录方法1:Environment.CurrentDirectory 取得或设置当前工作目录的完整限定路径2:AppDomain.Curren ...
- vue + ElementUI 表格筛选框的高度设置,超出一定高度,显示滚动条
相信有很多小伙伴遇到过这个问题,首先还是来看图片,筛选框我做了处理,所以和官网的有点小差别 官方网站和个人网站对比图如下: 代码如下:(F12找到该元素的class,设置高度) .el-table-f ...
- Haproxy搭建Web群集
一.Haproxy与LVS LVS不支持正则处理,不能实现动静分离,对于大型网站,LVS的实施配置复杂,维护成本相对较高 Harpoxy是一款可提供高可用性,负载均衡.及基于TCP和HTTP应用的代理 ...
- numpy 常用方法2
Python之Numpy基础 一个栗子 >>> import numpy as np >>> a = np.arange(15).reshape(3, 5) & ...
- C++基础知识-inline、const、mutable、this、static
一.在类定义中实现成员函数inline 类内的成员函实现其实也叫作类内的成员函数定义. 这种直接在类的定义中实现的函数,会被当做inline内联函数来处理. 二.成员函数末尾的const const: ...
- 后序线索化二叉树(Java版)
前面介绍了前序线索化二叉树.中序线索化二叉树,本文将介绍后序线索化二叉树.之所以用单独的一篇文章来分析后序线索化二叉树,是因为后序线索化二叉树比前序.中序要复杂一些:另外在复习线索化二叉树的过程中,大 ...