Golang学习笔记(Go语法基础+即时通信系统小项目)
fmt 包
导入fmt包:import "fmt"
输出:Println 、Printf、Print
注释代码:
// 单行注释
/*
多行注释
*/
定义变量:var a = "aaa" 变量定义,必须使用,不然会报错。
Printf 是格式化输出
var a int = 10
var b int = 3
var c int = 5
fmt.Printf("a = %v b = %v c = %v\n", a, b, c);
fmt.Printf("a = %d, a 的类型是 %T\n", a, a)
Go语言关键字
首先先认识一下Go语言中关键字,心里有个印象,让初学者有个眼熟就行。记不住没关系,我会在下面语法反复提到。在这里之所以提出来,就是让你们看一下,看的看的就记住了。
| 关键字 | 作用 | 一级分类 | 二级分类 | 三级分类 |
|---|---|---|---|---|
| var | 变量声明 | 基本结构 | 变量与常量 | - |
| const | 常量声明 | 基本结构 | 变量与常量 | - |
| package | 包声明 | 基本结构 | 包管理 | - |
| import | 包引用 | 基本结构 | 包管理 | - |
| func | 函数声明 | 基本组件 | 函数 | - |
| return | 函数返回 | 基本组件 | 函数 | - |
| interface | 接口 | 基本组件 | 自定义类型 | - |
| struct | 结构体 | 基本组件 | 自定义类型 | - |
| type | 定义类型 | 基本组件 | 自定义类型 | - |
| map | 基本组件 | 引用类型 | - | |
| range | 基本组件 | 引用类型 | - | |
| go | 流程控制 | 并发 | - | |
| select | 流程控制 | 并发 | - | |
| chan | 流程控制 | 并发 | - | |
| if | 流程控制 | 单任务流程控制 | 单分支流程 | |
| else | 流程控制 | 单任务流程控制 | 单分支流程 | |
| switch | 流程控制 | 单任务流程控制 | 多分支流程 | |
| case | 流程控制 | 单任务流程控制 | 多分支流程 | |
| default | 流程控制 | 单任务流程控制 | 多分支流程 | |
| fallthrough | 流程控制 | 单任务流程控制 | 多分支流程 | |
| for | 流程控制 | 单任务流程控制 | 循环流程 | |
| break | 流程控制 | 单任务流程控制 | 循环流程 | |
| continue | 流程控制 | 单任务流程控制 | 循环流程 | |
| goto | 流程控制 | 单任务流程控制 | ||
| defer | 流程控制 | 延时流程控制 |
数据类型
在 Go 编程语言中,数据类型用于声明函数和变量。
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
Go 语言按类别有以下几种数据类型:
| 类型 | 长度 | 默认值 | 说明 |
|---|---|---|---|
| bool | 1 | false | |
| byte | 1 | 0 | uint8 |
| int,uint | 4,8 | 0 | 默认整数类型,依据目标平台,32或64 |
| int8,uint8 | 1 | 0 | -128127,0127 |
| int16,uint16 | 2 | 0 | -215~(215)-1,0~(2^15)-1 |
| int32,uint32 | 4 | 0 | -231~(231)-1,0~(2^31)-1 |
| int64,uint64 | 8 | 0 | -263~(263)-1,0~(2^63)-1 |
| float32 | 4 | 0.0 | |
| float64 | 8 | 0.0 | 默认浮点数类型 |
| string | "" | 字符串,默认值为空字符串,而非NULL | |
| array | 数组 | ||
| struct | 结构体 | ||
| interface | nil | 接口 | |
| function | nil | 函数 | |
| map | nil | 字典,引用类型 | |
| slice | nil | 切片,引用类型 | |
| channel | nil | 通道,引用类型 |
常量
常量的定义:
const PI = 3.1415926535
const PI float64 = 3.1415926535
const(
PI = 3.1415926535
N = 1005
)
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1,并且没有赋值的常量默认会应用上一行的赋值表达式。
变量
var 会自动初始化。
我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,而这个时候再在 Example 4.4.1 的最后一个声明语句写上 var 关键字就显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。
这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。
//方法一:声明一个变量 默认值是0
var a int
//方法二:声明一个变量,初始化一个值
var b int = 100
//方法三:在初始化的时候,可以省去数据类型,通过自动匹配当前的变量的数据类型
var c = 100
//方法四:(常用的方法)省去 var 关键字,直接自动匹配
e:= 100
//申明全局变量:方法一、方法二、方法三都可以,方法四不行。
字符串
HasPrefix() 判断字符串 s 是否以 prefix 开头:
strings.HasPrefix(s, prefix string) bool
HasSuffix() 判断字符串 s 是否以 suffix 结尾:
strings.HasSuffix(s, suffix string) bool
Contains() 判断字符串 s 是否包含 substr:
strings.Contains(s, substr string) bool
函数
可以有多个返回值。
import 导包路径问题与 init 方法调用流程
import匿名及别名导包方式
在导入的包前面加别名可以匿名导包
import (
_ "./test-4/lib1"
mylib2 "./test-4/lib2"
. "./test-4/lib3"
)
defer
在函数调用最后执行,类似于 C++ 析构函数。
func main() {
defer fmt.Println("main end1")
defer fmt.Println("main end2")
fmt.Println("main::hello go 1")
fmt.Println("main::hello go 1")
}
main::hello go 1
main::hello go 1
main end2
main end1
// defer 与 return 谁先谁后
// return 比 defer 先执行,多个 defer 按照压栈顺序来执行。
数组与动态数组
package main
import "fmt"
func main() {
// 固定长度的数组
var myArray1 [10]int
myArray2 := [10]int{1, 2, 3, 4}
for i := 0; i < len(myArray1); i++ {
fmt.Println(myArray1[i])
}
for index, value := range myArray2 {
fmt.Println("index =", index, "value = ", value)
}
}
package main
import "fmt"
func main() {
// 动态数组, 切片 slice
myArray := []int{1, 2, 3, 4}
fmt.Printf("myArray type is %T\n", myArray)
}
slice 的四种定义方式
package main
import "fmt"
func main() {
// 申明 slice 是一个切片,并且初始化,默认值是 1,2,3, 长度 len 是 3
// slice1 := []int{1, 2, 3}
var slice2 []int
// slice2 = make([]int, 3)
// var slice3 []int = make([]int, 3)
// slice4 := make([]int, 4);
fmt.Printf("len = %d, slice = %v\n", len(slice2), slice2)
if slice2 == nil {
fmt.Println("slice2 是空切片")
} else {
fmt.Println("slice2 不是空切片")
}
}
slice 切片追加与截取
len 是slice 的长度。cap 是分配的空间,超出时会倍增开辟空间。
package main
import "fmt"
func main() {
var numbers = make([]int, 3, 5)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
numbers = append(numbers, 1)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
numbers = append(numbers, 2)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
numbers = append(numbers, 3)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
}
package main
import "fmt"
func main() {
s := []int{1, 2, 3}
s1 := s[0:2]
fmt.Println(s1)
// copy 可以深拷贝
s2 := make([]int, 3)
copy(s2, s)
fmt.Println(s2)
}
map 的三种申明方式
package main
import "fmt"
func main() {
var myMap1 map[string]string
if myMap1 == nil {
fmt.Println("myMap 是一个空 map")
}
// 1
myMap1 = make(map[string]string, 10)
myMap1["one"] = "java"
myMap1["two"] = "c++"
myMap1["three"] = "python"
fmt.Println(myMap1)
// 2
myMap2 := make(map[int]string)
myMap2[1] = "java"
myMap2[2] = "c++"
myMap2[3] = "python"
fmt.Println(myMap2)
// 3
myMap3 := map[string]string{
"one": "php",
"two": "c++",
"three": "python",
}
fmt.Println(myMap3)
}
package main
import "fmt"
func main() {
mp := make(map[string]string)
// 添加
mp["China"] = "Beijing"
mp["Japan"] = "Tokyo"
// 遍历
for key, val := range mp {
fmt.Println("key = ", key)
fmt.Println("val = ", val)
}
// 删除
delete(mp, "China")
// 遍历
for key, val := range mp {
fmt.Println("key = ", key)
fmt.Println("val = ", val)
}
}
map 传入函数时是引用传递。
类的封装
package main
import "fmt"
// 对 int 起别名
// type myint int
//如果类的首字母大写表示该类外部可以访问,否则外部不可以访问
type Hero struct {
Name string
Ad int
Level int
}
func (this *Hero) Show() {
fmt.Println("Name = ", this.Name)
fmt.Println("Ad = ", this.Ad)
fmt.Println("Level = ", this.Level)
}
func (this *Hero) GetName() string {
return this.Name
}
func (this *Hero) SetName(s string) {
this.Name = s
}
func main() {
// var a myint = 10
// fmt.Println(a)
hero := Hero{Name: "zhang3", Ad: 100, Level: 1}
hero.Show()
hero.SetName("Lib")
hero.Show()
}
类的继承
package main
import "fmt"
type Human struct {
name string
sex string
}
func (this *Human) Eat() {
fmt.Println("Human eat()...")
}
func (this *Human) Walk() {
fmt.Println("Human walk()...")
}
// ========>
type SuperMan struct {
Human
level int
}
func (this *SuperMan) Eat() {
fmt.Println("Superman Eat()...")
}
func (this *SuperMan) Fly() {
fmt.Println("SuperMan Fly()...")
}
func main() {
h := Human{"zhang3", "female"}
h.Eat()
h.Walk()
s := SuperMan{Human{"Li4", "male"}, 88}
s.Eat() // 子类
s.Walk() // 父类
s.Fly() // 子类
}
类的多态
用 interface 实现。
package main
import "fmt"
// interface 相当于一个指针类型
type AnimalIF interface {
Sleep()
GetColor() string
GetType() string
}
type Cat struct {
Color string
}
func (this *Cat) Sleep() {
fmt.Println("Cat sleep()...")
}
func (this *Cat) GetColor() string {
return this.Color
}
func (this *Cat) GetType() string {
return "Cat"
}
type Dog struct {
Color string
}
func (this *Dog) Sleep() {
fmt.Println("Dog sleep()...")
}
func (this *Dog) GetColor() string {
return this.Color
}
func (this *Dog) GetType() string {
return "Dog"
}
func ShowAnimal(NewAnimal AnimalIF) {
NewAnimal.Sleep()
fmt.Println("Color = ", NewAnimal.GetColor())
fmt.Println("Type = ", NewAnimal.GetType())
}
func main() {
/*
var animal AnimalIF
animal = &Cat{"Green"}
animal.Sleep()
animal = &Dog{"Yellow"}
animal.Sleep()
*/
cat := Cat{"Green"}
dog := Dog{"Yellow"}
ShowAnimal(&cat)
ShowAnimal(&dog)
}
interface 作为万能变量
package main
import "fmt"
//interface{} 是万能数据类型
func myFunc(arg interface{}) {
fmt.Println("mtFanc is called...")
fmt.Println(arg)
//interface{}
val, ok := arg.(string)
if !ok {
fmt.Println("arg is not string type")
} else {
fmt.Println("arg is string, val =", val)
fmt.Printf("val type is %T\n", val)
}
}
type Book struct {
auth string
Name string
}
func main() {
book := Book{"qwq", "wow"}
myFunc(book)
myFunc(100)
myFunc("abc")
myFunc(3.14)
}
golang反射reflect机制用法
我们可以把变量看成一个 pair<type, value> 的键值对,type 包含 static type 和 concrete type。
Reflect 包
reflect.ValueOf(interface{}) //得到当前变量的值。
reflect.TypeOf(interface{}) //得到当前变量的数据类型。
name.NumField() //获取name struct的变量数量。
name.Field(i) //获取name struct的第 i 个变量。
name.Field(i).interface{} //获取name struct的第 i 个变量并获得他的值。
name.NumMethod() //获取name struct的方法数量。
name.Method(i) //获取name struct的第 i 个方法。
name.Name //获取name的 变量/方法 名字
name.Type //获取name的 变量/方法 类型
package main
import (
"fmt"
"reflect"
)
type User struct {
ID int
Name string
Age int
}
func (this User) Call() {
fmt.Println("user is called...")
fmt.Printf("%v\n", this)
}
func Get(input interface{}) {
// 获取 input 的 type
inputType := reflect.TypeOf(input)
fmt.Println("inputType is :", inputType.Name())
// 获取 input 的 value
inputValue := reflect.ValueOf(input)
fmt.Println("inputValue is :", inputValue)
// 获取 input 里面的所有字段的 type
for i := 0; i < inputType.NumField(); i++ {
field := inputType.Field(i)
value := inputValue.Field(i).Interface()
fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
}
// 获取 input 里面的所有方法
for i := 0; i < inputType.NumMethod(); i++ {
m := inputType.Method(i)
fmt.Printf("%s : %v\n", m.Name, m.Type)
}
}
func main() {
user := User{1, "abc", 18}
Get(user)
}
golang反射解析结构体标签Tag
package main
import (
"fmt"
"reflect"
)
type resume struct {
Name string `info:"name" doc:"我的名字"`
Sex string `info:"sex"`
}
func findTag(str interface{}) {
t := reflect.TypeOf(str).Elem()
for i := 0; i < t.NumField(); i++ {
taginfo := t.Field(i).Tag.Get("info")
tagdoc := t.Field(i).Tag.Get("doc")
fmt.Println("info: ", taginfo, "doc:", tagdoc)
}
}
func main() {
var re resume
findTag(&re)
}
结构体标签在json中的应用
package main
import (
"encoding/json"
"fmt"
)
type Movie struct {
Title string `json:"title`
Year int `json:"year`
Price int `json:"price`
Actor []string `json:"actor`
}
func main() {
movie := Movie{"喜剧之王", 2000, 10, []string{"xinye", "zhangbozhi"}}
// 编码过程,结构体 ---> json
jsonStr, err := json.Marshal(movie)
if err != nil {
fmt.Println("json marshal error", err)
return
}
fmt.Printf("jsonStr = %s\n", jsonStr)
// 解码过程,jsonstr ---> 结构体
myMovie := Movie{}
err = json.Unmarshal(jsonStr, &myMovie)
if err != nil {
fmt.Println("json unmarshal error", err)
return
}
fmt.Printf("%v\n", myMovie)
}
创建goroutine
runtime.Goexit() 退出当前的 goroutine。
package main
import (
"fmt"
"time"
)
// 从 goroutine
func Mynew() {
for i := 0; i < 10; i++ {
fmt.Printf("Mynew i = %d\n", i)
time.Sleep(1 * time.Second)
}
}
// 主 goroutine
func main() {
go Mynew()
for i := 0; i < 10; i++ {
fmt.Printf("Main i = %d\n", i)
time.Sleep(1 * time.Second)
}
}
channel的基本定义与使用
生产者-消费者关系,channel 将 main() 与 func() 同步起来:
package main
import "fmt"
func main() {
// 定义一个 channel
c := make(chan int)
go func() {
defer fmt.Println("func() 结束...")
fmt.Println("func() 运行...")
c <- 666
}()
num := <-c
fmt.Println("num =", num)
fmt.Println("main() 结束...")
}
channel有缓冲与无缓冲同步问题
有缓冲的 channel :
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int, 3)
fmt.Println("len(c) = ", len(c), "cap(c) = ", cap(c))
go func() {
defer fmt.Println("func() 结束...")
for i := 0; i < 4; i++ {
fmt.Println("func() 正在传递 i = ", i, "len(c) = ", len(c), "cap(c) = ", cap(c))
c <- i
fmt.Println("func() 传递 i = ", i, " 成功!")
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 4; i++ {
num := <-c
fmt.Println("num = ", num)
}
time.Sleep(2 * time.Second)
fmt.Println("main() 结束...")
}
channel的关闭特点
通过 close(c) 关闭 channel 。
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
// 关闭 channel
close(c)
}()
for {
if num, ok := <-c; ok {
fmt.Println("num =", num)
} else {
break
}
}
fmt.Println("Main Finished...")
}
也可通过 range c 来迭代获取 c 的值。
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
// 关闭 channel
close(c)
}()
// for {
// if num, ok := <-c; ok {
// fmt.Println("num =", num)
// } else {
// break
// }
// }
for num := range c {
fmt.Println("num =", num)
}
fmt.Println("Main Finished...")
}
channel与select
select {
case <- chan1:
// 如果成功读到 chan1 数据,执行 ...
case chan2 <- 1:
// 如果 chan2 成功写入数据,执行 ...
default:
// 以上都不满足时,执行...
}
package main
import "fmt"
func fib(c, quit chan int) {
x, y := 1, 0
for {
select {
case c <- x:
x, y = x+y, x
case <-quit:
fmt.Println("quit...")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 1
}()
fib(c, quit)
}
GoModules模式基础环境说明
go env // 查看 go 环境
go env -w GO111MUDULE=on // 打开 GoModules 开关
GoModules 初始化项目
mkdir //创建文件夹
go mod init github.com/aceld/module_test //创建 go mod 文件
go get module_name //手动下载模块
改变模块依赖关系
go mod edit -replace=old=new
即时通信系统
服务器监听:服务器监听是指服务器程序在一个特定的端口上等待来自客户端的连接请求,并且处理这些请求的过程。
一些“新”数据类型:
sync.RWMutex //读写锁
conn //通用的面向流的网络连接
一些“新”函数:
net.Listen("tcp", "IP:Port") //监听 IP:Port 上的信息
func (l *TCPListener) Accept() (Conn, error) //等待下一个呼叫,并返回一个该呼叫的Conn接口
type Listener interface {
// Addr返回该接口的网络地址
Addr() Addr
// Accept等待并返回下一个连接到该接口的连接
Accept() (c Conn, err error)
// Close关闭该接口,并使任何阻塞的Accept操作都会不再阻塞并返回错误。
Close() error
}
type Conn interface {
// Read从连接中读取数据
// Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
Read(b []byte) (n int, err error)
// Write从连接中写入数据
// Write方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
Write(b []byte) (n int, err error)
// Close方法关闭该连接
// 并会导致任何阻塞中的Read或Write方法不再阻塞并返回错误
Close() error
// 返回本地网络地址
LocalAddr() Addr
// 返回远端网络地址
RemoteAddr() Addr
// 设定该连接的读写deadline,等价于同时调用SetReadDeadline和SetWriteDeadline
// deadline是一个绝对时间,超过该时间后I/O操作就会直接因超时失败返回而不会阻塞
// deadline对之后的所有I/O操作都起效,而不仅仅是下一次的读或写操作
// 参数t为零值表示不设置期限
SetDeadline(t time.Time) error
// 设定该连接的读操作deadline,参数t为零值表示不设置期限
SetReadDeadline(t time.Time) error
// 设定该连接的写操作deadline,参数t为零值表示不设置期限
// 即使写入超时,返回值n也可能>0,说明成功写入了部分数据
SetWriteDeadline(t time.Time) error
}
server端
server.go
package main
import (
"fmt"
"net"
)
type Server struct {
IP string
Port int
}
// 创建一个Server的接口
func NewServer(ip string, port int) *Server {
server := &Server{
IP: ip,
Port: port,
}
return server
}
func (ts *Server) Handler(conn net.Conn) {
//...当前链接的业务
fmt.Println("链接建立成功")
}
// 启动服务器的端口
func (ts *Server) Start() {
//socket listen
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ts.IP, ts.Port))
if err != nil {
fmt.Println("net.Listen() err", err)
}
// close listen soket
defer listener.Close()
for {
//accept
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err", err)
continue
}
//do handler
go ts.Handler(conn)
}
}
main.go
package main
func main() {
server := NewServer("127.0.0.1", 8888)
server.Start()
}
使用 go build -o server.exe main.go server.go 构建 server.exe。
Linux :nc 127.0.0.1:8888 可验证。
Windows:curl 127.0.0.1:8888 可验证。
用户上线及广播功能
server.go
package main
import (
"fmt"
"net"
"sync"
)
type Server struct {
IP string
Port int
//在线用户的的列表
OnlineMap map[string]*User
mapLock sync.RWMutex
//消息广播的
Message chan string
}
// 创建一个Server的接口
func NewServer(ip string, port int) *Server {
server := &Server{
IP: ip,
Port: port,
OnlineMap: make(map[string]*User),
Message: make(chan string),
}
return server
}
// 监听 Message 广播消息 channer 的 goroutine, 一旦有消息就发送给全部在线的 User
func (ts *Server) ListenMessager() {
for {
msg := <-ts.Message
// 将msg发送给全部在线的User
ts.mapLock.Lock()
for _, cli := range ts.OnlineMap {
cli.C <- msg
}
ts.mapLock.Unlock()
}
}
// 广播消息的方法
func (ts *Server) BroadCast(user *User, msg string) {
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
ts.Message <- sendMsg
}
func (ts *Server) Handler(conn net.Conn) {
//...当前链接的业务
//fmt.Println("链接建立成功")
user := NewUser(conn)
//用户上线,将用户加入到 onlineMap 中
ts.mapLock.Lock()
ts.OnlineMap[user.Name] = user
ts.mapLock.Unlock()
//广播当前用户的上线消息
ts.BroadCast(user, "已上线")
//当前 handle 阻塞
select {}
}
// 启动服务器的端口
func (ts *Server) Start() {
//socket listen
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ts.IP, ts.Port))
if err != nil {
fmt.Println("net.Listen() err", err)
}
// close listen soket
defer listener.Close()
//启动监听 Message 的 goroutine
go ts.ListenMessager()
for {
//accept
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err", err)
continue
}
//do handler
go ts.Handler(conn)
}
}
user.go
package main
import "net"
type User struct {
Name string
Addr string
C chan string
Conn net.Conn
}
// 创建一个用户的API
func NewUser(conn net.Conn) *User {
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
C: make(chan string),
Conn: conn,
}
go user.ListenMessage()
return user
}
// 监听当前 user channel 的方法,一旦有消息就发送给对端客户端
func (ts *User) ListenMessage() {
for {
msg := <-ts.C
ts.Conn.Write([]byte(msg + "\n"))
}
}
用户消息广播功能
server.go
package main
import (
"fmt"
"io"
"net"
"sync"
)
type Server struct {
IP string
Port int
//在线用户的的列表
OnlineMap map[string]*User
mapLock sync.RWMutex
//消息广播的
Message chan string
}
// 创建一个Server的接口
func NewServer(ip string, port int) *Server {
server := &Server{
IP: ip,
Port: port,
OnlineMap: make(map[string]*User),
Message: make(chan string),
}
return server
}
// 监听 Message 广播消息 channer 的 goroutine, 一旦有消息就发送给全部在线的 User
func (ts *Server) ListenMessager() {
for {
msg := <-ts.Message
// 将msg发送给全部在线的User
ts.mapLock.Lock()
for _, cli := range ts.OnlineMap {
cli.C <- msg
}
ts.mapLock.Unlock()
}
}
// 广播消息的方法
func (ts *Server) BroadCast(user *User, msg string) {
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
ts.Message <- sendMsg
}
func (ts *Server) Handler(conn net.Conn) {
//...当前链接的业务
//fmt.Println("链接建立成功")
user := NewUser(conn)
//用户上线,将用户加入到 onlineMap 中
ts.mapLock.Lock()
ts.OnlineMap[user.Name] = user
ts.mapLock.Unlock()
//广播当前用户的上线消息
ts.BroadCast(user, "已上线")
//接受客户端发送的消息
go func() {
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n == 0 {
ts.BroadCast(user, "下线")
return
}
if err != nil && err != io.EOF {
fmt.Println("Conn Read err:", err)
}
//提取用户的消息(去除 '\n')
msg := string(buf[:n-1])
//将得到的消息进行广播
ts.BroadCast(user, msg)
}
}()
//当前 handle 阻塞
select {}
}
// 启动服务器的端口
func (ts *Server) Start() {
//socket listen
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ts.IP, ts.Port))
if err != nil {
fmt.Println("net.Listen() err", err)
}
// close listen soket
defer listener.Close()
//启动监听 Message 的 goroutine
go ts.ListenMessager()
for {
//accept
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err", err)
continue
}
//do handler
go ts.Handler(conn)
}
}
用户业务封装
server.go
package main
import (
"fmt"
"io"
"net"
"sync"
)
type Server struct {
IP string
Port int
//在线用户的的列表
OnlineMap map[string]*User
mapLock sync.RWMutex
//消息广播的
Message chan string
}
// 创建一个Server的接口
func NewServer(ip string, port int) *Server {
server := &Server{
IP: ip,
Port: port,
OnlineMap: make(map[string]*User),
Message: make(chan string),
}
return server
}
// 监听 Message 广播消息 channer 的 goroutine, 一旦有消息就发送给全部在线的 User
func (ts *Server) ListenMessager() {
for {
msg := <-ts.Message
// 将msg发送给全部在线的User
ts.mapLock.Lock()
for _, cli := range ts.OnlineMap {
cli.C <- msg
}
ts.mapLock.Unlock()
}
}
// 广播消息的方法
func (ts *Server) BroadCast(user *User, msg string) {
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
ts.Message <- sendMsg
}
func (ts *Server) Handler(conn net.Conn) {
//...当前链接的业务
//fmt.Println("链接建立成功")
user := NewUser(conn, ts)
//用户上线
user.Online()
//接受客户端发送的消息
go func() {
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n == 0 {
user.Offline()
return
}
if err != nil && err != io.EOF {
fmt.Println("Conn Read err:", err)
}
//提取用户的消息(去除 '\n')
msg := string(buf[:n-1])
//将得到的消息进行广播
user.DoMessage(msg)
}
}()
//当前 handle 阻塞
select {}
}
// 启动服务器的端口
func (ts *Server) Start() {
//socket listen
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ts.IP, ts.Port))
if err != nil {
fmt.Println("net.Listen() err", err)
return
}
// close listen soket
defer listener.Close()
//启动监听 Message 的 goroutine
go ts.ListenMessager()
for {
//accept
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err", err)
continue
}
//do handler
go ts.Handler(conn)
}
}
user.go
package main
import "net"
type User struct {
Name string
Addr string
C chan string
Conn net.Conn
server *Server
}
// 创建一个用户的API
func NewUser(conn net.Conn, server *Server) *User {
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
C: make(chan string),
Conn: conn,
server: server,
}
go user.ListenMessage()
return user
}
// 用户的上线业务
func (ts *User) Online() {
//用户上线,将用户加入到 onlineMap 中
ts.server.mapLock.Lock()
ts.server.OnlineMap[ts.Name] = ts
ts.server.mapLock.Unlock()
//广播当前用户的上线消息
ts.server.BroadCast(ts, "已上线")
}
// 用户的下线业务
func (ts *User) Offline() {
//用户上线,将用户从 OnlineMap 中删除
ts.server.mapLock.Lock()
delete(ts.server.OnlineMap, ts.Name)
ts.server.mapLock.Unlock()
//广播当前用户的下线消息
ts.server.BroadCast(ts, "下线")
}
// 用户处理消息的业务
func (ts *User) DoMessage(msg string) {
ts.server.BroadCast(ts, msg)
}
// 监听当前 user channel 的方法,一旦有消息就发送给对端客户端
func (ts *User) ListenMessage() {
for {
msg := <-ts.C
ts.Conn.Write([]byte(msg + "\n"))
}
}
在线用户查询
格式:user list
user.go
package main
import "net"
type User struct {
Name string
Addr string
C chan string
Conn net.Conn
server *Server
}
// 创建一个用户的API
func NewUser(conn net.Conn, server *Server) *User {
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
C: make(chan string),
Conn: conn,
server: server,
}
go user.ListenMessage()
return user
}
// 用户的上线业务
func (ts *User) Online() {
//用户上线,将用户加入到 onlineMap 中
ts.server.mapLock.Lock()
ts.server.OnlineMap[ts.Name] = ts
ts.server.mapLock.Unlock()
//广播当前用户的上线消息
ts.server.BroadCast(ts, "已上线")
}
// 用户的下线业务
func (ts *User) Offline() {
//用户上线,将用户从 OnlineMap 中删除
ts.server.mapLock.Lock()
delete(ts.server.OnlineMap, ts.Name)
ts.server.mapLock.Unlock()
//广播当前用户的下线消息
ts.server.BroadCast(ts, "下线")
}
// 给当前用户对应的客户端发送信息
func (ts *User) sendMsg(msg string) {
ts.Conn.Write([]byte(msg))
}
// 用户处理消息的业务
func (ts *User) DoMessage(msg string) {
if msg == "user list" {
ts.server.mapLock.Lock()
for _, user := range ts.server.OnlineMap {
OnlineMsg := "[" + user.Addr + "]" + user.Name + ":在线...\n"
ts.sendMsg(OnlineMsg)
}
ts.server.mapLock.Unlock()
} else {
ts.server.BroadCast(ts, msg)
}
}
// 监听当前 user channel 的方法,一旦有消息就发送给对端客户端
func (ts *User) ListenMessage() {
for {
msg := <-ts.C
ts.Conn.Write([]byte(msg + "\n"))
}
}
修改用户名
格式:rename|username
user.go
package main
import (
"net"
"strings"
)
type User struct {
Name string
Addr string
C chan string
Conn net.Conn
server *Server
}
// 创建一个用户的API
func NewUser(conn net.Conn, server *Server) *User {
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
C: make(chan string),
Conn: conn,
server: server,
}
go user.ListenMessage()
return user
}
// 用户的上线业务
func (ts *User) Online() {
//用户上线,将用户加入到 onlineMap 中
ts.server.mapLock.Lock()
ts.server.OnlineMap[ts.Name] = ts
ts.server.mapLock.Unlock()
//广播当前用户的上线消息
ts.server.BroadCast(ts, "已上线")
}
// 用户的下线业务
func (ts *User) Offline() {
//用户上线,将用户从 OnlineMap 中删除
ts.server.mapLock.Lock()
delete(ts.server.OnlineMap, ts.Name)
ts.server.mapLock.Unlock()
//广播当前用户的下线消息
ts.server.BroadCast(ts, "下线")
}
// 给当前用户对应的客户端发送信息
func (ts *User) SendMsg(msg string) {
ts.Conn.Write([]byte(msg))
}
// 用户处理消息的业务
func (ts *User) DoMessage(msg string) {
if msg == "user list" {
ts.server.mapLock.Lock()
for _, user := range ts.server.OnlineMap {
OnlineMsg := "[" + user.Addr + "]" + user.Name + ":在线...\n"
ts.SendMsg(OnlineMsg)
}
ts.server.mapLock.Unlock()
} else if len(msg) > 7 && msg[:7] == "rename|" {
//rename|username 修改用户名
newName := strings.Split(msg, "|")[1]
_, ok := ts.server.OnlineMap[newName]
if ok {
ts.SendMsg("当前用户名被使用...\n")
} else {
ts.server.mapLock.Lock()
delete(ts.server.OnlineMap, ts.Name)
ts.server.OnlineMap[newName] = ts
ts.server.mapLock.Unlock()
ts.Name = newName
ts.SendMsg("您已修改用户名为:" + ts.Name + "\n")
}
} else {
ts.server.BroadCast(ts, msg)
}
}
// 监听当前 user channel 的方法,一旦有消息就发送给对端客户端
func (ts *User) ListenMessage() {
for {
msg := <-ts.C
ts.Conn.Write([]byte(msg + "\n"))
}
}
超时强踢功能
server.go
package main
import (
"fmt"
"io"
"net"
"sync"
"time"
)
type Server struct {
IP string
Port int
//在线用户的的列表
OnlineMap map[string]*User
mapLock sync.RWMutex
//消息广播的
Message chan string
}
// 创建一个Server的接口
func NewServer(ip string, port int) *Server {
server := &Server{
IP: ip,
Port: port,
OnlineMap: make(map[string]*User),
Message: make(chan string),
}
return server
}
// 监听 Message 广播消息 channer 的 goroutine, 一旦有消息就发送给全部在线的 User
func (ts *Server) ListenMessager() {
for {
msg := <-ts.Message
// 将msg发送给全部在线的User
ts.mapLock.Lock()
for _, cli := range ts.OnlineMap {
cli.C <- msg
}
ts.mapLock.Unlock()
}
}
// 广播消息的方法
func (ts *Server) BroadCast(user *User, msg string) {
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
ts.Message <- sendMsg
}
func (ts *Server) Handler(conn net.Conn) {
//...当前链接的业务
//fmt.Println("链接建立成功")
user := NewUser(conn, ts)
//用户上线
user.Online()
//监听客户端发送的信息
isLive := make(chan bool)
//接受客户端发送的消息
go func() {
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n == 0 {
user.Offline()
return
}
if err != nil && err != io.EOF {
fmt.Println("Conn Read err:", err)
}
//提取用户的消息(去除 '\n')
msg := string(buf[:n-1])
//将得到的消息进行广播
isLive <- true
user.DoMessage(msg)
}
}()
//当前 handle 阻塞
for {
select {
case <-isLive:
//当前用户是活跃的,应该重置定时器
//不做任何事情,为了激活 select,更新下面的定时器
case <-time.After(time.Second * 10):
//已经超时
//当当前的 User 强制关闭
user.SendMsg("你被踢出了聊天室...\n")
//撤销该 User 的资源
close(user.C)
//关闭连接
conn.Close()
//退出当前的 Handler
return
}
}
}
// 启动服务器的端口
func (ts *Server) Start() {
//socket listen
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ts.IP, ts.Port))
if err != nil {
fmt.Println("net.Listen() err", err)
return
}
// close listen soket
defer listener.Close()
//启动监听 Message 的 goroutine
go ts.ListenMessager()
for {
//accept
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err", err)
continue
}
//do handler
go ts.Handler(conn)
}
}
私聊功能
格式:to|username|message
package main
import (
"net"
"strings"
)
type User struct {
Name string
Addr string
C chan string
Conn net.Conn
server *Server
}
// 创建一个用户的API
func NewUser(conn net.Conn, server *Server) *User {
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
C: make(chan string),
Conn: conn,
server: server,
}
go user.ListenMessage()
return user
}
// 用户的上线业务
func (ts *User) Online() {
//用户上线,将用户加入到 onlineMap 中
ts.server.mapLock.Lock()
ts.server.OnlineMap[ts.Name] = ts
ts.server.mapLock.Unlock()
//广播当前用户的上线消息
ts.server.BroadCast(ts, "已上线")
}
// 用户的下线业务
func (ts *User) Offline() {
//用户上线,将用户从 OnlineMap 中删除
ts.server.mapLock.Lock()
delete(ts.server.OnlineMap, ts.Name)
ts.server.mapLock.Unlock()
//广播当前用户的下线消息
ts.server.BroadCast(ts, "下线")
}
// 给当前用户对应的客户端发送信息
func (ts *User) SendMsg(msg string) {
ts.Conn.Write([]byte(msg))
}
// 用户处理消息的业务
func (ts *User) DoMessage(msg string) {
if msg == "user list" {
ts.server.mapLock.Lock()
for _, user := range ts.server.OnlineMap {
OnlineMsg := "[" + user.Addr + "]" + user.Name + ":在线...\n"
ts.SendMsg(OnlineMsg)
}
ts.server.mapLock.Unlock()
} else if len(msg) > 7 && msg[:7] == "rename|" {
//rename|username 修改用户名
newName := strings.Split(msg, "|")[1]
_, ok := ts.server.OnlineMap[newName]
if ok {
ts.SendMsg("当前用户名被使用...\n")
} else {
ts.server.mapLock.Lock()
delete(ts.server.OnlineMap, ts.Name)
ts.server.OnlineMap[newName] = ts
ts.server.mapLock.Unlock()
ts.Name = newName
ts.SendMsg("您已修改用户名为:" + ts.Name + "\n")
}
} else if len(msg) > 4 && msg[:3] == "to|" {
//消息格式:to|username|message
//1.获取对方的用户名
remoteName := strings.Split(msg, "|")[1]
if remoteName == "" {
ts.SendMsg("消息格式不正确,请使用 to|username|message 的格式\n")
return
}
//2.根据用户名得到对方 User 对象
remoteUser, ok := ts.server.OnlineMap[remoteName]
if !ok {
ts.SendMsg("该用户名不存在\n")
return
}
//3.获取消息,通过对方 User 发送出去
content := strings.Split(msg, "|")[2]
if content == "" {
ts.SendMsg("无消息内容,请重发\n")
return
}
remoteUser.SendMsg(ts.Name + "对您说:" + content + "\n")
ts.SendMsg("您对" + remoteName + "说:" + content + "\n")
} else {
ts.server.BroadCast(ts, msg)
}
}
// 监听当前 user channel 的方法,一旦有消息就发送给对端客户端
func (ts *User) ListenMessage() {
for {
msg := <-ts.C
ts.Conn.Write([]byte(msg + "\n"))
}
}
客户端实现
建立链接
client.go
package main
import (
"fmt"
"net"
)
type Client struct {
ServerIp string
ServerPort int
Name string
conn net.Conn
}
func NewClient(serverIp string, serverPort int) *Client {
//创建客户端对象
client := &Client{
ServerIp: serverIp,
ServerPort: serverPort,
}
//链接server
conn, err := net.Dial("tcp", net.JoinHostPort(serverIp, fmt.Sprintf("%d", serverPort)))
if err != nil {
fmt.Println("net.Dial error:", err)
return nil
}
client.conn = conn
//返回对象
return client
}
func main() {
client := NewClient("127.0.0.1", 8888)
if client == nil {
fmt.Println(">>>>> 链接服务器失败...")
return
}
fmt.Println(">>>>> 链接服务器成功...")
//启动客户端的业务
select {}
}
命令行解析
package main
import (
"flag"
"fmt"
"net"
)
type Client struct {
ServerIp string
ServerPort int
Name string
conn net.Conn
}
func NewClient(serverIp string, serverPort int) *Client {
//创建客户端对象
client := &Client{
ServerIp: serverIp,
ServerPort: serverPort,
}
//链接server
conn, err := net.Dial("tcp", net.JoinHostPort(serverIp, fmt.Sprintf("%d", serverPort)))
if err != nil {
fmt.Println("net.Dial error:", err)
return nil
}
client.conn = conn
//返回对象
return client
}
var serverIp string
var serverPort int
// ./client -ip 127.0.0.1 -port 8888
func init() {
flag.StringVar(&serverIp, "ip", "127.0.0.1", "设置服务器IP地址(默认是:127.0.0.1)")
flag.IntVar(&serverPort, "port", 8888, "设置服务器端口(默认是:8888)")
}
func main() {
//命令行解析
flag.Parse()
client := NewClient(serverIp, serverPort)
if client == nil {
fmt.Println(">>>>> 链接服务器失败...")
return
}
fmt.Println(">>>>> 链接服务器成功...")
//启动客户端的业务
select {}
}
菜单显示
client.go
package main
import (
"flag"
"fmt"
"net"
)
type Client struct {
ServerIp string
ServerPort int
Name string
conn net.Conn
flag int
}
func NewClient(serverIp string, serverPort int) *Client {
//创建客户端对象
client := &Client{
ServerIp: serverIp,
ServerPort: serverPort,
flag: 999,
}
//链接server
conn, err := net.Dial("tcp", net.JoinHostPort(serverIp, fmt.Sprintf("%d", serverPort)))
if err != nil {
fmt.Println("net.Dial error:", err)
return nil
}
client.conn = conn
//返回对象
return client
}
func (ts *Client) menu() bool {
var flag int
fmt.Println("1.公聊模式")
fmt.Println("2.私聊模式")
fmt.Println("3.更新用户名")
fmt.Println("0.退出")
fmt.Scanln(&flag)
if flag >= 0 && flag <= 3 {
ts.flag = flag
return true
} else {
fmt.Println(">>>>请输入合法范围内的数字<<<<")
return false
}
}
func (ts *Client) Run() {
for ts.flag != 0 {
for !ts.menu() {
}
//更具不同的模式处理不同的业务
switch ts.flag {
case 1:
//公聊模式
fmt.Println("公聊模式选择...")
case 2:
//私聊模式
fmt.Println("私聊模式选择...")
case 3:
//更改用户名
fmt.Println("更新用户名选择...")
}
}
}
var serverIp string
var serverPort int
// ./client -ip 127.0.0.1 -port 8888
func init() {
flag.StringVar(&serverIp, "ip", "127.0.0.1", "设置服务器IP地址(默认是:127.0.0.1)")
flag.IntVar(&serverPort, "port", 8888, "设置服务器端口(默认是:8888)")
}
func main() {
//命令行解析
flag.Parse()
client := NewClient(serverIp, serverPort)
if client == nil {
fmt.Println(">>>>> 链接服务器失败...")
return
}
fmt.Println(">>>>> 链接服务器成功...")
//启动客户端的业务
client.Run()
}
更新用户名
client.go
package main
import (
"flag"
"fmt"
"io"
"net"
"os"
)
type Client struct {
ServerIp string
ServerPort int
Name string
conn net.Conn
flag int
}
func NewClient(serverIp string, serverPort int) *Client {
//创建客户端对象
client := &Client{
ServerIp: serverIp,
ServerPort: serverPort,
flag: 999,
}
//链接server
conn, err := net.Dial("tcp", net.JoinHostPort(serverIp, fmt.Sprintf("%d", serverPort)))
if err != nil {
fmt.Println("net.Dial error:", err)
return nil
}
client.conn = conn
//返回对象
return client
}
// 处理 server 回应的消息,直接显示到标准输出即可
func (ts *Client) DealResponse() {
//一旦client.conn有数据,就直接copy到stdout标准输出上,永久阻塞监听
io.Copy(os.Stdout, ts.conn)
}
func (ts *Client) menu() bool {
var flag int
fmt.Println("1.公聊模式")
fmt.Println("2.私聊模式")
fmt.Println("3.更新用户名")
fmt.Println("0.退出")
fmt.Scanln(&flag)
if flag >= 0 && flag <= 3 {
ts.flag = flag
return true
} else {
fmt.Println(">>>>请输入合法范围内的数字<<<<")
return false
}
}
// 更新用户名
func (ts *Client) UpdateName() bool {
fmt.Println(">>>请输入用户名:")
fmt.Scanln(&ts.Name)
sendMsg := "rename|" + ts.Name + "\n"
_, err := ts.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn.Write err:", err)
return false
}
return true
}
func (ts *Client) Run() {
for ts.flag != 0 {
for !ts.menu() {
}
//更具不同的模式处理不同的业务
switch ts.flag {
case 1:
//公聊模式
fmt.Println("公聊模式选择...")
case 2:
//私聊模式
fmt.Println("私聊模式选择...")
case 3:
//更改用户名
ts.UpdateName()
}
}
}
var serverIp string
var serverPort int
// ./client -ip 127.0.0.1 -port 8888
func init() {
flag.StringVar(&serverIp, "ip", "127.0.0.1", "设置服务器IP地址(默认是:127.0.0.1)")
flag.IntVar(&serverPort, "port", 8888, "设置服务器端口(默认是:8888)")
}
func main() {
//命令行解析
flag.Parse()
client := NewClient(serverIp, serverPort)
if client == nil {
fmt.Println(">>>>> 链接服务器失败...")
return
}
//单独开启一个 goroutine 去处理 server 回应的消息
go client.DealResponse()
fmt.Println(">>>>> 链接服务器成功...")
//启动客户端的业务
client.Run()
}
公聊与私聊
client.go
package main
import (
"flag"
"fmt"
"io"
"net"
"os"
)
type Client struct {
ServerIp string
ServerPort int
Name string
conn net.Conn
flag int
}
func NewClient(serverIp string, serverPort int) *Client {
//创建客户端对象
client := &Client{
ServerIp: serverIp,
ServerPort: serverPort,
flag: 999,
}
//链接server
conn, err := net.Dial("tcp", net.JoinHostPort(serverIp, fmt.Sprintf("%d", serverPort)))
if err != nil {
fmt.Println("net.Dial error:", err)
return nil
}
client.conn = conn
//返回对象
return client
}
// 处理 server 回应的消息,直接显示到标准输出即可
func (ts *Client) DealResponse() {
//一旦client.conn有数据,就直接copy到stdout标准输出上,永久阻塞监听
io.Copy(os.Stdout, ts.conn)
}
func (ts *Client) menu() bool {
var flag int
fmt.Println("1.公聊模式")
fmt.Println("2.私聊模式")
fmt.Println("3.更新用户名")
fmt.Println("0.退出")
fmt.Scanln(&flag)
if flag >= 0 && flag <= 3 {
ts.flag = flag
return true
} else {
fmt.Println(">>>>请输入合法范围内的数字<<<<")
return false
}
}
// 公聊模式
func (ts *Client) PublicChat() {
//提示用户输入消息
var chatMsg string
fmt.Println(">>>>请输入聊天内容, exit退出.")
fmt.Scanln(&chatMsg)
for chatMsg != "exit" {
//发送给服务器
//消息不为空则发送
if chatMsg != "" {
sendMsg := chatMsg + "\n"
_, err := ts.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn Write err:", err)
break
}
chatMsg = ""
fmt.Println(">>>>请输入聊天内容, exit退出.")
fmt.Scanln(&chatMsg)
}
}
}
// 更新用户名
func (ts *Client) UpdateName() bool {
fmt.Println(">>>请输入用户名:")
fmt.Scanln(&ts.Name)
sendMsg := "rename|" + ts.Name + "\n"
_, err := ts.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn.Write err:", err)
return false
}
return true
}
// 查询在线用户
func (client *Client) SelectUsers() {
sendMsg := "user list\n"
_, err := client.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn Write err:", err)
return
}
}
// 私聊模式
func (ts *Client) PrivateChat() {
var remoteName string
var chatMsg string
ts.SelectUsers()
fmt.Println(">>>>请输入聊天对象[用户名], exit退出.")
fmt.Scanln(&remoteName)
for remoteName != "exit" {
fmt.Println(">>>>请输入聊天内容, exit退出.")
fmt.Scanln(&chatMsg)
for chatMsg != "exit" {
//发送给服务器
//消息不为空则发送
if chatMsg != "" {
sendMsg := "to|" + remoteName + "|" + chatMsg + "\n\n"
_, err := ts.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn Write err:", err)
break
}
chatMsg = ""
fmt.Println(">>>>请输入聊天内容, exit退出.")
fmt.Scanln(&chatMsg)
}
}
ts.SelectUsers()
fmt.Println(">>>>请输入聊天对象[用户名], exit退出.")
fmt.Scanln(&remoteName)
}
}
func (ts *Client) Run() {
for ts.flag != 0 {
for !ts.menu() {
}
//更具不同的模式处理不同的业务
switch ts.flag {
case 1:
//公聊模式
ts.PublicChat()
case 2:
//私聊模式
ts.PrivateChat()
case 3:
//更改用户名
ts.UpdateName()
}
}
}
var serverIp string
var serverPort int
// ./client -ip 127.0.0.1 -port 8888
func init() {
flag.StringVar(&serverIp, "ip", "127.0.0.1", "设置服务器IP地址(默认是:127.0.0.1)")
flag.IntVar(&serverPort, "port", 8888, "设置服务器端口(默认是:8888)")
}
func main() {
//命令行解析
flag.Parse()
client := NewClient(serverIp, serverPort)
if client == nil {
fmt.Println(">>>>> 链接服务器失败...")
return
}
//单独开启一个 goroutine 去处理 server 回应的消息
go client.DealResponse()
fmt.Println(">>>>> 链接服务器成功...")
//启动客户端的业务
client.Run()
}

浙公网安备 33010602011771号