go版本的tcp聊天室入门(体会go的强大与简洁)

1)效果

服务器启动后,多个客户端分别连接进来就可以通信了

2)server下的main.go

package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
)

func main() {
	listener, err := net.Listen("tcp", "localhost:8000")
	if err != nil {
		//log.Fatal()打印错误信息并调用os.Exit(1),终止程序
		log.Fatal(err)
	}

	//广播,发送消息到所有客户端
	go broadcaster()

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println(err)
			continue
		}
		//每个客户端一个goroutine
		go handleConn(conn)
	}
}

//channel的三种类型(只发送、只接受、即发送也接受)
//这里client只发送不接受
//只接受 type client <-chan string
//即发送也接受 type client chan string
type client chan<- string

var (
	entering = make(chan client)
	leaving  = make(chan client)
	message  = make(chan string)
)

func broadcaster() {
	clients := make(map[client]bool)
	for {
		select {
		case msg := <-message:
			for cli := range clients {
				//这里的cli就是handleConn里的ch channel,
				//writeToCLient goroutine一直在监听ch channel,读取channel中的内容,并写入客户端连接
				cli <- msg
			}
		case cli := <-entering:
			clients[cli] = true
		case cli := <-leaving:
			delete(clients, cli)
			close(cli)
		}
	}
}

func handleConn(conn net.Conn) {
	ch := make(chan string)
	//写入消息到客户端的连接
	go writeToCLient(conn, ch)

	who := conn.RemoteAddr().String()
	//当客户端连接过来时,给客户端一条消息
	//注意,这时的ch会立马被writeToCLient goroutine读取,并发送到当前客户端
	//所以已连接的其他客户端不会接受到该条消息
	ch <- "You are " + who
	//这里的message channel会被broadcaster读取,广播给所有已连接的客户端
	//注意,这时当前客户端还没给entering,所以当前客户端不会接受到该条消息
	message <- who + " are arrived"
	//将当前客户端发送给entering channel,broadcaster会将当前客户端添加到已连接的客户端集合中
	entering <- ch

	input := bufio.NewScanner(conn)
	//阻塞监听客户端输入
	for input.Scan() {
		//获取客户端输入,并发送到message channel,然后broadcaster会将它广播给所有连接的客户端
		//因为这时,当前客户端已经添加到clients集合中,所以当前客户端也会接受到消息
		message <- who + ": " + input.Text()
	}

	//客户端断开连接
	leaving <- ch
	message <- who + " are left"
	conn.Close()
}

func writeToCLient(conn net.Conn, ch <-chan string) {
	for msg := range ch {
		fmt.Fprintln(conn, msg)
	}
}

client下的main.go

package main

import (
	"io"
	"log"
	"net"
	"os"
)

func main() {
	conn, err := net.Dial("tcp", "localhost:8000")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	//获取服务端消息
	go ioCopy(os.Stdout, conn)
	//将用户输入的文本消息发送到到服务端
	ioCopy(conn, os.Stdin)
}

func ioCopy(dst io.Writer, src io.Reader) {
	if _, err := io.Copy(dst, src); err != nil {
		log.Fatal(err)
	}
}

3)要发布到生产环境也是非常简单,编译生产可执行文件

然后就可以单独运行,可见go确实非常强大和简洁!

猜你喜欢

转载自blog.csdn.net/themagickeyjianan/article/details/106953939