一、前期准备

  • 前期准备
  • 需要 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.go client :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聊天室的更多相关文章

  1. websocket+golang聊天室

    原文地址: http://www.niu12.com/article/3 websocket+golang聊天室 main.go和index.html放在同一目录下 main.go package m ...

  2. golang 聊天室

    近期看了一些关于golang的资料.发现它的WEBSOCKT实现真起来真的非常easy.以下是代码 go.net/websocket是须要安装的,这个自己装上即可 package main impor ...

  3. Golang语言快速上手到综合实战高并发聊天室

    需要的联系我:QQ:1844912514 Go是Google开发的一种编译型,可并行化,并具有垃圾回收功能的编程语言.2015,Go迎来了全迸发的一年.时隔一年,回头再看,Go已跻身主流编程语言行列. ...

  4. golang简易版聊天室

    功能需求: 创建一个聊天室,实现群聊和单聊的功能,直接输入为群聊,@某人后输入为单聊 效果图: 群聊:   单聊: 服务端: package main import ( "fmt" ...

  5. go 聊天室简单版总结

    /* * 思路:在登录成功时将用户的id存进在线用户列表中的key value中链接的ws为空,并保存用户的信息. * 当跳转到聊天室时,将用户和聊天室链接的ws存进在线用户列表中的 * 问题:如何在 ...

  6. 网络编程-基于Websocket聊天室(IM)系统

    目录 一.HTML5 - Websocket协议 二.聊天室(IM)系统的设计 2.1.使用者眼中的聊天系统 2.2.开发者眼中的聊天系统 2.3.IM系统的特性 2.4.心跳机制:解决网络的不确定性 ...

  7. 利用Node.js的Net模块实现一个命令行多人聊天室

    1.net模块基本API 要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用.NET模块API分为两大类:Server和Socket类.工厂方法. Server类 ...

  8. php+websocket搭建简易聊天室实践

    1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...

  9. 基于select的python聊天室程序

    python网络编程具体参考<python select网络编程详细介绍>. 在python中,select函数是一个对底层操作系统的直接访问的接口.它用来监控sockets.files和 ...

随机推荐

  1. autofac JSON文件配置

    autofac是比较简单易用的IOC容器.下面我们展示如何通过json配置文件,来进行控制反转. 需要用到以下程序集.可以通过nugget分别安装 Microsoft.Extensions.Confi ...

  2. jQuery操作标签--样式、文本、属性操作, 文档处理

    1.样式 2.文本 3.属性操作 全选,反选效果 4.文档处理 操作标签 一.样式操作 样式类: addClass(); // 添加指定的css类名 removeClass(); //移除指定的css ...

  3. SQL语句优化 (二) (53)

    接上一部分 (4)如果不是索引列的第一部分,如下例子:可见虽然在money上面建有复合索引,但是由于money不是索引的第一列,那么在查询中这个索引也不会被MySQL采用. mysql> exp ...

  4. AtcoderExaWizards 2019题解

    传送门 \(A\ Regular\ Triangle\) 咕咕 \(B\ Red\ or\ Blue\) 咕咕咕 \(C\ Snuke\ the\ Wizard\) 我可能脑子真的坏掉了-- 容易发现 ...

  5. 【vue】——CDN或全局引入CSS、JS。

    在入口文件index.html中添加 <!DOCTYPE html> <html> <head> <meta charset="utf-8" ...

  6. Web Worker 使用教程(转)

    转自:http://www.ruanyifeng.com/blog/2018/07/web-worker.html 一.概述 JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个 ...

  7. Java常见错误及解决方案

    1.类定义未找到:java.lang.NoClassDefFoundError java类文件没有上传:上传了,服务器没找到,建议将JSP页面重新更新或重启服务器. 2.

  8. leetcode-40-组合总和 II

    题目描述: 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的每个数字在每个组合中只 ...

  9. keycloak ssl-required报错问题处理

       两台主机,网段不同,第一台129.30.108.179/24    第二台172.16.160.92/24 都安装keycloak :    docker run -d --name keycl ...

  10. vue进行路由拼图的使用案例

    实现思路,利用路由进行实现多个组件拼图: Detail.vue <template> <div> <h1>详细展示</h1> <div>鞍山 ...