为了看Fabric的源码,前几天开始学了Go语言,看得是《Go语言圣经》,基本看了一遍,感觉还是比较容易上手,不过因为grpc和一些Go的库我都还不熟悉,现在看源码还是有点费劲,到现在也没怎么看。
不过Go语言学了就得练一下,不写代码估计过几天就跟没学一样了,(就像前几天学的js一样=_=||
所以今天找时间做了个聊天系统,比较简单,主要拿来熟悉一下Go语言…
过几天有空会试着用grpc写一下,再把文件改成数据库试试~
TODO:
- 客户端多重登陆问题
客户端:client.go
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"os"
)
func main() {
//cinq := bufio.NewScanner(os.Stdin)
//if cinq.Scan() {
// fmt.Printf(cinq.Text())
//}
//if cinq.Scan() {
// fmt.Printf(cinq.Text())
//}
//if cinq.Scan() {
// fmt.Printf(cinq.Text())
//}
conn, err := net.Dial("tcp", "localhost:8000")
defer conn.Close()
if err != nil {
log.Fatal(err)
}
done := make(chan struct{})
go func() {
io.Copy(os.Stdout, conn) // NOTE: ignoring errors
log.Println("done")
done <- struct{}{} // signal the main goroutine
}()
ch := make(chan string)
go clientWriter(conn, ch)
cin := bufio.NewScanner(os.Stdin)
for cin.Scan() {
switch cin.Text() {
case "register":
ch <- "register"
var userName, password, tel string
fmt.Print("请输入用户名:")
if cin.Scan() {
userName = cin.Text()
}
fmt.Print("请输入密码:")
if cin.Scan() {
password = cin.Text()
}
fmt.Print("请输入手机号:")
if cin.Scan() {
tel = cin.Text()
}
ch <- userName
ch <- password
ch <- tel
case "login":
ch <- "login"
var userName, password string
fmt.Print("请输入用户名:")
if cin.Scan() {
userName = cin.Text()
}
fmt.Print("请输入密码:")
if cin.Scan() {
password = cin.Text()
}
ch <- userName
ch <- password
case "logoff":
ch <- "logoff"
case "exit":
os.Exit(0)
case "add":
var userName string
fmt.Print("请输入要添加好友的用户名:")
if cin.Scan() {
userName = cin.Text()
}
ch <- "add"
ch <- userName
case "delete":
fmt.Print("请输入要删除好友的用户名:")
var userName string
if cin.Scan() {
userName = cin.Text()
}
ch <- "delete"
ch <- userName
case "list":
ch <- "list"
case "sendTo":
var userName, content string
fmt.Print("请输入好友用户名:")
if cin.Scan() {
userName = cin.Text()
}
fmt.Print("请输入消息:")
if cin.Scan() {
content = cin.Text()
}
ch <- "sendTo"
ch <- userName
ch <- content
case "sendAll":
var content string
fmt.Print("请输入消息:")
if cin.Scan() {
content = cin.Text()
}
ch <- "sendAll"
ch <- content
default:
ch <- "none"
}
}
//conn.Close() //前面用了defer
<-done // wait for background goroutine to finish
}
func mustCopy(conn net.Conn, src io.Reader) {
if _, err := io.Copy(conn, src); err != nil {
log.Fatal(err)
}
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
服务器端:server.go
package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"time"
)
func help() string {
return (`
please choose options:
- register : 注册。 格式:"register"
- login : 登录。 格式:"login"
- logoff : 注销登录。 格式:"logoff"
- exit : 退出系统。 格式:"exit"
- add : 添加好友。 格式:"add"
- delete : 删除好友。 格式:"delete"
- list : 查看好友列表。 格式:"list"
- sendTo : 给某位好友发送消息。 格式:"sendTo"
- sendAll : 给全部好友发送消息。 格式:"sendAll"
`)
}
/*
Client is a demo
*/
type Client struct {
userName string
}
type userCH chan<- string
var (
messages = make(chan string)
entering = make(chan userCH)
leaving = make(chan userCH)
clients = make(map[string]userCH)
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handle(conn)
}
}
func handle(conn net.Conn) {
var client *Client
ch := make(chan string)
go clientWriter(conn, ch)
who := conn.RemoteAddr().String()
ch <- "welcome! " + who
ch <- help()
ch <- ">>"
input := bufio.NewScanner(conn)
for input.Scan() {
var op = input.Text()
fmt.Println(op)
switch op {
case "register":
//register
ok := register(input)
if !ok {
ch <- "Fail register=_=||\nmaybe your userName or phoneNumber is invalid"
} else {
ch <- "Success register!"
}
case "login":
//login
client = login(input)
if client != nil {
clients[client.userName] = ch
ch <- "Success logi!\n" + "Your new messages:"
client.toRead(ch)
} else {
ch <- "Fail login=_=||maybe your userName or password is wrong,please check them carefully:)"
}
case "logoff":
if client == nil {
ch <- "Please login first:)"
} else {
ch <- "you logoff successfully!"
clients[client.userName] = nil
client = nil
}
case "add":
var userName string
if input.Scan() {
userName = input.Text()
}
if client == nil {
ch <- "Please login first:)"
} else {
ok := client.addFriend(userName)
if ok {
ch <- "add successfully!"
} else {
ch <- "maybe no such user=_=||"
}
}
case "delete":
var userName string
if input.Scan() {
userName = input.Text()
}
if client == nil {
ch <- "Please login first:)"
} else {
ok := client.deleteFriend(userName)
if ok {
ch <- "delete successfully!"
} else {
ch <- "maybe no such user=_=||"
}
}
case "list":
if client == nil {
ch <- "Please login first:)"
} else {
ch <- client.list()
}
case "sendTo":
if client == nil {
ch <- "Please login first:)"
} else {
var userName, content string
if input.Scan() {
userName = input.Text()
}
if input.Scan() {
content = input.Text()
}
ok := client.sendTo(userName, content)
if ok {
ch <- "发送成功"
} else {
ch <- "发送失败,可能你没有这个好友:)"
}
}
case "sendAll":
if client == nil {
ch <- "Please login first:)"
} else {
var content string
if input.Scan() {
content = input.Text()
}
client.sendAll(content)
}
default:
ch <- "无法识别的命令=_=||请重新输入正确的命令:)"
ch <- help()
}
ch <- ">>"
}
conn.Close()
}
func clientWriter(conn net.Conn, ch chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
//读取文件user.txt,判断手机号和用户名是否重复注册
//将注册信息写入user.txt
//如果成功就创建messages、logs、friends等.txt文件
func register(cin *bufio.Scanner) bool {
var userName, password, tel string
if cin.Scan() {
userName = cin.Text()
}
if cin.Scan() {
password = cin.Text()
}
if cin.Scan() {
tel = cin.Text()
}
file, err := os.OpenFile("user.txt", os.O_RDWR|os.O_CREATE, 0644)
defer file.Close()
if err != nil {
fmt.Println("in func register: open user.txt")
}
for {
var name, pwd, phone string
_, err := fmt.Fscan(file, &name, &pwd, &phone) //读取三个string
if err != nil {
break
}
fmt.Println(userName, name, tel, phone)
if userName == name && tel == phone {
return false
}
}
file.Write([]byte("\n" + userName + " " + password + " " + tel))
createInfo(userName)
return true
}
func createInfo(userName string) {
file, err := os.OpenFile("friendsOf"+userName+".txt", os.O_CREATE, 0664)
if err != nil {
log.Fatalln("in func createInfo: ---")
}
file.Close()
file, err = os.OpenFile("messagesOf"+userName+".txt", os.O_CREATE, 0664)
file.Close()
file, err = os.OpenFile("logsOf"+userName+".txt", os.O_CREATE, 0664)
file.Close()
}
//读取user.txt,判断是否有这个用户以及密码是否正确
//上线后自动查看收件箱,显示未读消息
func login(cin *bufio.Scanner) *Client {
var userName, password string
if cin.Scan() {
userName = cin.Text()
}
if cin.Scan() {
password = cin.Text()
}
file, err := os.Open("user.txt")
defer file.Close()
if err != nil {
fmt.Println("func register: open user.txt")
}
for {
var name, pwd, phone string
_, err := fmt.Fscan(file, &name, &pwd, &phone) //读取三个string
if err != nil {
fmt.Println(err)
break
}
if userName == name && pwd == password {
var client Client
client.userName = userName
// client.ch = make(chan string)
// go clientWriter(conn, client.ch)
// clients[userName] = client.ch
return &client
}
}
return nil
}
func (c *Client) toRead(ch chan string) {
messages := "messagesOf" + c.userName + ".txt"
logs := "logsOf" + c.userName + ".txt"
msgsfile, err := os.OpenFile(messages, os.O_RDWR, 0666) //读写
defer msgsfile.Close()
logsfile, err := os.OpenFile(logs, os.O_APPEND|os.O_WRONLY, 0666)
defer logsfile.Close()
if err != nil {
log.Fatalln("func toRead, fail to open file")
}
for {
//格式 : 时间、好友名字、信息内容
var t, n, content string
_, err := fmt.Fscan(msgsfile, &t, &n, &content)
if err != nil {
//log.Fatalln("messagesfuserName.txt went wrong! in to read")
fmt.Println(err)
break
}
temp := t + " " + n + " " + content
//fmt.Println(temp)
_, err = logsfile.Write([]byte("\n" + t + " " + n + " " + c.userName + " " + content))
if err != nil {
fmt.Println("write logs wrong! in func toRead")
}
ch <- temp
}
msgsfile.Seek(0, 0) //second para: (0:begin) (1:cur) (2:end)
msgsfile.Truncate(0) //清空未读消息
}
func (c *Client) addFriend(userName string) bool {
userFile := "user.txt"
file, err := os.Open(userFile)
defer file.Close()
if err != nil {
log.Fatalln("in func addFriend: open user.txt")
}
var name, pwd, phone string
for {
_, err = fmt.Fscan(file, &name, &pwd, &phone)
if err != nil { //EOF?? //TODO
break
}
if name == userName {
friendFile, err := os.OpenFile("friendsOf"+c.userName+".txt", os.O_APPEND|os.O_WRONLY, 0666)
defer friendFile.Close()
if err != nil {
log.Fatalln("in func addFriend: open friendsOf" + c.userName + ".txt")
}
friendFile.Write([]byte("\n" + userName))
return true
}
}
return false
}
func (c *Client) deleteFriend(userName string) bool {
friendFile, err := os.OpenFile("friendsOf"+c.userName+".txt", os.O_RDWR, 0666)
defer friendFile.Close()
if err != nil {
log.Fatalln("in func deleteFriend: open friendsOf" + c.userName + ".txt")
}
var name, sum string
var ok = false
for {
_, err := fmt.Fscan(friendFile, &name)
if err != nil {
break
}
if name == userName {
ok = true
} else {
sum += "\n" + name
}
}
if ok {
friendFile.Seek(0, 0)
friendFile.Truncate(0) //这里是截断是不是清除内容待验证 //TODO
friendFile.Write([]byte(sum))
}
return ok
}
func (c *Client) list() string {
filename := "friendsOf" + c.userName + ".txt"
file, err := os.Open(filename)
if err != nil {
return ""
}
res, _ := ioutil.ReadAll(file)
return string(res)
}
func (c *Client) sendTo(userName, content string) bool {
msg := "\n" + time.Now().Format("2006-01-02_15:04:05") + " " + c.userName + " " + content
msglog := "\n" + time.Now().Format("2006-01-02_15:04:05") + " " + c.userName + " " + userName + " " + content
friendFile, err := os.OpenFile("friendsOf"+c.userName+".txt", os.O_RDONLY, 0666)
if err != nil {
log.Fatalln("in func sendAll: open friendsOf" + c.userName + ".txt")
}
var friend string
var ok bool
for {
_, err := fmt.Fscan(friendFile, &friend)
if err != nil {
break
}
if friend == userName {
ok = true
break
}
}
friendFile.Close()
if ok {
if clients[userName] != nil {
clients[userName] <- msg
logsFile, err := os.OpenFile("logsOf"+userName+".txt", os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
log.Fatalln("in func sendTo: open logsOf" + userName + ".txt")
}
logsFile.Write([]byte(msglog))
logsFile.Close()
} else {
msgsFile, err := os.OpenFile("messagesOf"+userName+".txt", os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
log.Fatalln("in func sendTo: open messagesOf" + userName + ".txt")
}
msgsFile.Write([]byte(msg))
msgsFile.Close()
}
logsFile, err := os.OpenFile("logsOf"+c.userName+".txt", os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
log.Fatalln("in func sendTo: open logsOf" + c.userName + ".txt")
}
logsFile.Write([]byte(msglog))
logsFile.Close()
return true
}
return false
}
func (c *Client) sendAll(content string) bool {
msg := "\n" + time.Now().Format("2006-01-02_15:04:05") + " " + c.userName + " " + content
friendFile, err := os.OpenFile("friendsOf"+c.userName+".txt", os.O_RDONLY, 0666)
if err != nil {
log.Fatalln("in func sendAll: open friendsOf" + c.userName + ".txt")
}
file, err := os.OpenFile("logsOf"+c.userName+".txt", os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
log.Fatalln("in func sendAll: open logsOf" + c.userName + ".txt")
}
defer file.Close()
var userName, msglog string
for {
_, err := fmt.Fscan(friendFile, &userName)
if err != nil {
break
}
msglog = "\n" + time.Now().Format("2006-01-02_15:04:05") + " " + c.userName + " " + userName + " " + content
if clients[userName] != nil {
clients[userName] <- msg
logsFile, err := os.OpenFile("logsOf"+userName+".txt", os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
log.Fatalln("in func sendAll open logsOf" + userName + ".txt")
}
logsFile.Write([]byte(msglog))
logsFile.Close()
} else {
msgsFile, err := os.OpenFile("messagesOf"+userName+".txt", os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
log.Fatalln("in func sendAll: open messagesOf" + userName + ".txt")
}
msgsFile.Write([]byte(msg))
msgsFile.Close()
}
file.Write([]byte(msglog))
}
return true
}
/*
func readfile(filename string) string {
//var res []string
file, err := os.Open(filename)
if err != nil {
return ""
}
buf, _ := ioutil.ReadAll(file)
//for {
// var name, pwd string
// _, err := fmt.Fscan(file, &name, &pwd)
// if err != nil {
// break
// }
// res = append(res, name)
// res = append(res, pwd)
//}
file.Close()
//file, err = os.OpenFile("user.txt", os.O_APPEND|os.O_WRONLY, 0644)
//if err != nil {
// return nil
//}
//name, pwd := "dsfa", "jag"
//file.Write([]byte("\n" + name))
//file.Write([]byte(" " + pwd))
//file.Close()
return string(buf)
}
*/