服务器端server.go代码如下:
package main
import (
"fmt"
"net"
)
var ConnMap map[string]*net.TCPConn //客户端IP到conn(接收连接)的映射
func checkErr(err error) int {
if err != nil {
if err.Error() == "EOF" { //客户端自己退出了程序
fmt.Println("用户退出")
return 0
}
fmt.Println("发生错误")
return -1
}
return -1
}
func say(tcpConn *net.TCPConn) {
for {
data := make([]byte, 256)
total, err := tcpConn.Read(data) //读取客户端发来的数据
if err != nil {
fmt.Println(string(data[:total]), err)
} else {
fmt.Println(string(data[:total]))
}
flag := checkErr(err)
if flag == 0 {
break //若客户端退出程序,则这次请求的处理直接终止
}
for _, conn := range ConnMap {
if conn.RemoteAddr().String() == tcpConn.RemoteAddr().String() {
continue //遍历map,将消息发给除发送端之外的其他客户端
}
conn.Write(data[:total])
}
}
}
func main() {
tcpAddr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8080") //创建一个TCPAddr并绑定地址
fmt.Println("......")
tcpListen, _ := net.ListenTCP("tcp", tcpAddr) //开始监听连接请求
ConnMap = make(map[string]*net.TCPConn)
for {
tcpConn, _ := tcpListen.AcceptTCP() //接收客户端连接,返回给conn
defer tcpConn.Close() //程序结束后关闭conn
ConnMap[tcpConn.RemoteAddr().String()] = tcpConn //建立映射
fmt.Println("连接客户端信息: ", tcpConn.RemoteAddr().String())
go say(tcpConn) //启动处理连接请求的go routine
}
}
客户端代码client.go代码如下:
package main
import (
"fmt"
"net"
"os"
"bufio" //缓冲包
)
var ch chan int = make(chan int)
func reader(conn *net.TCPConn) {
buff := make([]byte, 256)
for {
j, err := conn.Read(buff)
if err != nil {
ch <- 1
break
}
fmt.Println(string(buff[0:j]))
}
}
func main() {
if len(os.Args) != 2 { //通过os.Args获取命令行参数,包括命令和IP地址及端口号
fmt.Fprintf(os.Stderr, "Usage:%s host:port", os.Args[0])
os.Exit(1)
}
service := os.Args[1] //该参数保存的是从命令行获取的IP地址及端口号
TcpAdd, _ := net.ResolveTCPAddr("tcp", service)
conn, err := net.DialTCP("tcp", nil, TcpAdd) //请求与TCPAdd指定的TCP服务器建立连接
if err != nil {
fmt.Println("服务没打开")
os.Exit(1)
}
defer conn.Close()
go reader(conn) //开辟另一个go routine,接收其他客户端发来的消息
inputReader := bufio.NewReader(os.Stdin) //从Stdin读取数据
fmt.Println("请输入昵称:")
nickname, err := inputReader.ReadString('\n') //读取一行数据,以字符串形式返回
nickname = nickname[:len(nickname)-1] //将字符串最后的换行符去掉
if err == nil {
fmt.Println("你的昵称是: ", nickname)
}
for {
var msg string
fmt.Print(">>")
msg, err := inputReader.ReadString('\n')
msg = msg[:len(msg)-1]
if err != nil {
fmt.Println("error!")
os.Exit(-1)
}
fmt.Println("<" + nickname + ">" + "说:" + msg)
b := []byte("<" + nickname + ">" + "说:" + msg)
conn.Write(b) //将该消息发送给服务器以转发给其他客户端
select {
case <-ch :
fmt.Println("server发生错误,请重新连接")
os.Exit(2)
default:
}
}
}
该程序运行效果如下(开启三个终端,同时运行服务器和两个客户端进行调试)