下面先列舉一下程序使用到的函數(shù),省的大家去找,直接拷貝官方api的解釋吧。
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)
DialTCP在網(wǎng)絡(luò)協(xié)議net上連接本地地址laddr和遠(yuǎn)端地址raddr。
net必須是"tcp"、"tcp4"、"tcp6";如果laddr不是nil,將使用它作為本地地址,否則自動(dòng)選擇一個(gè)本地地址。
func ResolveTCPAddr(net, addr string) (*TCPAddr, error)
ResolveTCPAddr將addr作為TCP地址解析并返回。
參數(shù)addr格式為"host:port"或"[ipv6-host%zone]:port",解析得到網(wǎng)絡(luò)名和端口名;net必須是"tcp"、"tcp4"或"tcp6"。
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error)
ListenTCP在本地TCP地址laddr上聲明并返回一個(gè)*TCPListener,
net參數(shù)必須是"tcp"、"tcp4"、"tcp6",如果laddr的端口字段為0,函數(shù)將選擇一個(gè)當(dāng)前可用的端口,可以用Listener的Addr方法獲得該端口。TCPListener代表一個(gè)TCP網(wǎng)絡(luò)的監(jiān)聽者。使用者應(yīng)盡量使用Listener接口而不是假設(shè)(網(wǎng)絡(luò)連接為)TCP。
func (l *TCPListener) AcceptTCP() (*TCPConn, error)
//AcceptTCP接收下一個(gè)呼叫,并返回一個(gè)新的*TCPConn。
//TCPConn代表一個(gè)TCP網(wǎng)絡(luò)連接,實(shí)現(xiàn)了Conn接口。
Write(b []byte) (n int, err error)
Write從連接中寫入數(shù)據(jù)
Write方法可能會(huì)在超過(guò)某個(gè)固定時(shí)間限制后超時(shí)返回錯(cuò)誤,該錯(cuò)誤的Timeout()方法返回真
Read(b []byte) (n int, err error)
Read從連接中讀取數(shù)據(jù)
Read方法可能會(huì)在超過(guò)某個(gè)固定時(shí)間限制后超時(shí)返回錯(cuò)誤,該錯(cuò)誤的Timeout()方法返回真
var Args []string
Args保管了命令行參數(shù),第一個(gè)是程序名。
服務(wù)端代碼
server.go
package main
import (
"encoding/json"
"fmt"
"log"
"net"
)
func main() {
//端口號(hào)
StartServer("8080")
}
//結(jié)構(gòu)體
type person struct {
news string
ip string
}
//StartServer 啟動(dòng)服務(wù)器
func StartServer(s string) {
// 獲取tcp地址
tcpAddr, err := net.ResolveTCPAddr("tcp4", ":"+s)
if err != nil {
log.Printf("resolve tcp addr failed: %v\n", err)
return
}
//連接池,用來(lái)保存所有人的數(shù)據(jù)
conns := make(map[string]net.Conn)
//消息信道,為有緩沖信道,當(dāng)然緩沖內(nèi)存也可以增大
messages := make(chan person, 10)
// 監(jiān)聽
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
log.Printf("listen tcp port failed: %v\n", err)
return
}
//開啟發(fā)送消息的協(xié)程
go BroadCastMessage(conns, messages)
//時(shí)刻監(jiān)測(cè)有沒有新的消息發(fā)送過(guò)來(lái)
for {
/*
func (l *TCPListener) AcceptTCP() (*TCPConn, error)
AcceptTCP接收下一個(gè)呼叫,并返回一個(gè)新的*TCPConn。
TCPConn代表一個(gè)TCP網(wǎng)絡(luò)連接,實(shí)現(xiàn)了Conn接口。
*/
conn, err := listener.AcceptTCP()
if err != nil {
fmt.Println("鏈接失敗")
continue
}
conns[conn.RemoteAddr().String()] = conn
go HandlerMessage(conn, conns, messages)
}
}
//HandlerMessage 檢查發(fā)送來(lái)的消息
/*
//conn:返回的新的*TCPConn
conns:連接池
messages:消息通道
*/
func HandlerMessage(conn net.Conn, conns map[string]net.Conn, messages chan person) {
//切片用來(lái)暫存消息
buf := make([]byte, 1024)
for {
//從鏈接里面讀取數(shù)據(jù),寫給buf
len, err := conn.Read(buf)
if err != nil {
conn.Close()
delete(conns, conn.RemoteAddr().String())
break
}
//消息寫入結(jié)構(gòu)體,并且把發(fā)送者的ip一塊寫入
p := person{
news: string(buf[:len]),
ip: conn.RemoteAddr().String(),
}
//發(fā)送給信道
messages <- p
fmt.Println(string(buf[:len]))
}
}
//BroadCastMessage 發(fā)送消息
/*
conns:連接池
messages:消息通道
*/
func BroadCastMessage(conns map[string]net.Conn, messages chan person) {
for {
//讀取信道里面的消息
mess := <-messages
//發(fā)送給所有人
for k, v := range conns {
//不發(fā)送給自己
if k != mess.ip {
var m map[string]interface{}
//將發(fā)送過(guò)來(lái)消息的news序列化為map
json.Unmarshal([]byte(mess.news), &m)
//拼接字符串
msg := m["time"].(string) + "\n" + m["userID"].(string) + ":" + m["message"].(string)
//發(fā)送
_, err := v.Write([]byte(msg))
//如果失敗關(guān)閉v所屬人的協(xié)程,繼續(xù)監(jiān)測(cè)
if err != nil {
delete(conns, k)
v.Close()
continue
}
}
}
}
}
個(gè)人端代碼
net.go
package main
import (
"encoding/json"
"fmt"
"log"
"net"
"os"
"time"
)
func main() {
/*
var Args []string
Args保管了命令行參數(shù),第一個(gè)是程序名。
*/
tcpAddr, _ := net.ResolveTCPAddr("tcp", os.Args[1])
//撥號(hào)
conn, _ := net.DialTCP("tcp", nil, tcpAddr)
var p = make(map[string]interface{})
p["userID"] = os.Args[2]
go HandlerMessage(conn, p)
ReceivesMessage(conn)
}
//HandlerMessage 向服務(wù)器發(fā)送數(shù)據(jù)
func HandlerMessage(conn net.Conn, p map[string]interface{}) {
for {
var input string
// 接收輸入消息,放到input變量中
fmt.Scanln(&input)
//用戶端退出啊
if input == "/q" || input == "/quit" {
fmt.Println("Byebye ...")
conn.Close()
os.Exit(0)
}
//發(fā)送的信息
p["message"] = input
//時(shí)間戳
p["time"] = time.Now()
// 只處理有內(nèi)容的消息
if len(input) > 0 {
//序列化為json
msg, err := json.Marshal(p)
if err != nil {
//如果不成功,返回錯(cuò)誤
fmt.Println("錯(cuò)誤", err)
} else {
//發(fā)送數(shù)據(jù)
_, err := conn.Write([]byte(string(msg)))
//沒有發(fā)送成功
if err != nil {
conn.Close()
break
}
}
}
}
}
//ReceivesMessage 接收服務(wù)器消息
func ReceivesMessage(conn net.Conn) {
// 接收來(lái)自服務(wù)器端的廣播消息
buf := make([]byte, 1024)
for {
length, err := conn.Read(buf)
if err != nil {
log.Printf("recv server msg failed: %v\n", err)
conn.Close()
os.Exit(0)
break
}
fmt.Println(string(buf[0:length]))
}
}
測(cè)試步驟,只用本地測(cè)試一下,懶的上傳云服務(wù)器了
一、啟動(dòng)服務(wù)器

二、啟動(dòng)個(gè)人端,當(dāng)然這里我們啟動(dòng)兩個(gè),兩個(gè)以上才能看出來(lái)效果不是嗎


三、發(fā)送消息
