package main
import (
"bufio"
"fmt"
"net"
"os"
)
/**
主程序 啓動客戶端和服務端
參數說明:
啓動服務器端: 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) != 3 {
fmt.Println("wrong params")
os.Exit(0)
}
if os.Args[1] == "server" {
StartServer(os.Args[2])
}
if os.Args[1] == "client" {
StartClient(os.Args[2])
}
}
/**
啓動服務器
參數: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, 10)
//啓動服務器廣播線程 :向所有客戶端發送消息
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, 1024)
for {
lenght,err := conn.Read(buf)
if checkError(err, "Connection") == false {
conn.Close()
break
}
if lenght >0 {
buf[lenght] = 0
}
reciveStr := string(buf[0: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, 1024)
for {
lenght,err := conn.Read(buf)
if checkError(err, "Connection") == false {
conn.Close()
fmt.Println("Server is dead ...ByeBye")
os.Exit(0)
}
fmt.Println(string(buf[0:lenght]))
}
}
/**
客戶端發送數據線程
參數:
發送連接 conn
*/
func chatSend(conn net.Conn) {
//var input string
username := conn.LocalAddr().String()
inputReader := bufio.NewReader(os.Stdin)
index := 0
for {
//fmt.Scanln(&input)
//這個地方改爲回車結束判斷,利用上面的輸入檢測,會有問題(不知道如何表述,就是會出現錯亂)
input, err := inputReader.ReadString('\n')
//fmt.Fscan(stdin, &input)
if input == "/quit" {
fmt.Println("ByeBye..")
conn.Close()
os.Exit(0)
}
lens,err := conn.Write([]byte(username + "say ::: " + input))
//這個地方lens不能缺省,不知道爲什麼
if lens == 0 {
//
}
if err != nil {
fmt.Println(err.Error())
conn.Close()
}
index++
}
}
//錯誤信息
func checkError(err error, info string) (res bool) {
if err != nil {
fmt.Println(info + ",err:" + err.Error())
return false
}
return true
}
這個是自己做了一點修改,如果是原版的方法,客戶端在字符串中添加空格的時候,那個方法scanln會自動分割空格兩端的字符串,從而錯亂,所以改了這個版本(就是客戶端在想服務端的通道出入數據的時候的一點修改),初學炸到,不喜勿噴