实现了客户端的菜单功能,分为
-
公聊模式
-
私聊模式
-
更新用户名
-
退出
并实现了这四个功能,本次的简易即时聊天系统到此结束 -
公聊模式
func (client *Client) PublicChat() {
//发给服务器
for {
var chatMsg string
fmt.Println(">>>>>>请输入聊天内容, exit退出。")
fmt.Scanln(&chatMsg)
if chatMsg == "exit" {
break
}
if len(chatMsg) != 0 {
sendMsg := chatMsg + "\n"
_, err := client.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn.Write error:", err)
break
}
}
}
}
- 私聊模式
// 查询在线用户
func (client *Client) SelectUsers() {
sendMsg := "who\n"
_, err := client.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn Write error:", err)
return
}
}
// 私聊模式
func (client *Client) PrivateChat() {
for {
var remoteName string
client.SelectUsers()
fmt.Println(">>>>>请选择聊天对象, exit退出")
fmt.Scanln(&remoteName)
if remoteName == "exit" {
break
}
for {
var chatMsg string
fmt.Println(">>>>>请输入消息内容, exit退出")
fmt.Scanln(&chatMsg)
if chatMsg == "exit" {
break
}
if len(chatMsg) != 0 {
sendMsg := "to|" + remoteName + "|" + chatMsg + "\n\n"
_, err := client.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn.Write error:", err)
break
}
}
}
}
}
- 更新用户名
func (client *Client) UpdateName() bool {
fmt.Println(">>>>>请输入用户名:")
fmt.Scanln(&client.Name)
sendMsg := "rename|" + client.Name + "\n"
_, err := client.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn.Write err:", err)
return false
}
return true
}
客户端完整代码
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: 99,
}
//连接server
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", serverIp, serverPort))
if err != nil {
fmt.Println("net.Dial error:", err)
return nil
}
client.conn = conn
//返回对象
return client
}
var serverIp string
var serverPort int
func init() {
//./client -ip 127.0.0.1 -port 8888
flag.StringVar(&serverIp, "ip", "127.0.0.1", "设置服务器IP地址(默认是127.0.0.1)")
flag.IntVar(&serverPort, "port", 8888, "设置服务区端口(默认是8888)")
}
func (client *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 {
client.flag = flag
} else {
fmt.Println(">>>>>请输入合法范围的数<<<<<")
return false
}
return true
}
// 查询在线用户
func (client *Client) SelectUsers() {
sendMsg := "who\n"
_, err := client.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn Write error:", err)
return
}
}
// 私聊模式
func (client *Client) PrivateChat() {
for {
var remoteName string
client.SelectUsers()
fmt.Println(">>>>>请选择聊天对象, exit退出")
fmt.Scanln(&remoteName)
if remoteName == "exit" {
break
}
for {
var chatMsg string
fmt.Println(">>>>>请输入消息内容, exit退出")
fmt.Scanln(&chatMsg)
if chatMsg == "exit" {
break
}
if len(chatMsg) != 0 {
sendMsg := "to|" + remoteName + "|" + chatMsg + "\n\n"
_, err := client.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn.Write error:", err)
break
}
}
}
}
}
func (client *Client) PublicChat() {
//提示用户输入消息
//发给服务器
for {
//消息不为空
var chatMsg string
fmt.Println(">>>>>>请输入聊天内容, exit退出。")
fmt.Scanln(&chatMsg)
if chatMsg == "exit" {
break
}
if len(chatMsg) != 0 {
sendMsg := chatMsg + "\n"
_, err := client.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn.Write error:", err)
break
}
}
}
}
func (client *Client) UpdateName() bool {
fmt.Println(">>>>>请输入用户名:")
fmt.Scanln(&client.Name)
sendMsg := "rename|" + client.Name + "\n"
_, err := client.conn.Write([]byte(sendMsg))
if err != nil {
fmt.Println("conn.Write err:", err)
return false
}
return true
}
func (client *Client) Run() {
for client.flag != 0 {
for client.menu() != true {
}
//根据不同的flag处理不同业务
switch client.flag {
case 1:
client.PublicChat()
case 2:
client.PrivateChat()
case 3:
client.UpdateName()
}
}
}
// 处理 server 回应的消息,直接显示到标准输出
func (client *Client) DealResponse() {
io.Copy(os.Stdout, client.conn)
/*
等价于
for {
buf := make()
client.conn.Read(buf)
fmt.Println(buf)
}
*/
}
func main() {
//命令行解析
flag.Parse()
client := NewClient(serverIp, serverPort)
if client == nil {
fmt.Println(">>>>> 连接服务器失败...")
return
}
fmt.Println(">>>>> 连接服务器成功...")
//单独开启一个 goruntine,处理 server 发回的消息
go client.DealResponse()
client.Run()
}
总结:
- user 与 server 打交道的功能都是在服务器实现的,在 user.go 和 server.go 中,客户端主要实现一些逻辑并调用他们的功能,比如将 chatMsg 修改为 to|xxx|xxx 的格式,再调用 user.go 中的接口
- 要开一个 goruntine 来处理 server 发回的内容,用
io.Copy(os.Stdout, client.conn)
更方便 - 本次即时通讯系统的开发(抄代码)让我第一次完整的体会到开发的过程,与写算法题相比,虽然不需要冥思苦想一些很精妙的解法,但是统筹考虑,合理设计的难度更加高,尤其是涉及到并发与加锁的时候,还需要积累更多的经验