Go语言实现一个简单的并发聊天室

写在前面

Go语言在很多方面天然的具备很多便捷性,譬如网络编程,并发编程。而通道则又是Go语言实现并发编程的重要工具,因为其承担着通道之间互相通信的重任。并且因为其本身就是并发安全的,所以在某些场景下是非常好用的。

并发聊天服务器

这里主要是实现一个简单的并发聊天服务器。首先,客户端可以在服务器中注册自己的信息(登录以及退出),客户端发出的所有的信息由服务器向各个客户端进行转发,或者换句话说是广播。

具体代码

服务端

说的再多,没有代码简单明了,直接上代码~

package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
)
type client chan <- string //定义一个单向的向外发送数据的通道

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

func main() {
     
	listener, err := net.Listen("tcp","localhost:8000")
	if err != nil {
     
		log.Fatal("network is broken", err)
	}
	go broadcaster()
	for {
     
		conn, err := listener.Accept()
		if err != nil {
     
			log.Print(err)
			continue
		}
		go handleConn1(conn)
	}
}
func broadcaster()  {
     
	clients := make(map[client]bool) //存储每个client的登录状态
	for{
     
		select {
     
		case msg := <-messages:
			for cli := range clients {
     
				cli <- msg
			}
		case cli := <-entering:
			clients[cli] = true
		case cli := <-leaving:
			delete(clients,cli)
			close(cli)
		}
	}
}

func handleConn1(conn net.Conn)  {
     
	ch := make(chan string)
	go clientWriter(conn, ch)
	who := conn.RemoteAddr().String()
	ch <- "You are " + who
	entering <- ch
	messages <- who + "has arrived"

	input := bufio.NewScanner(conn)
	for input.Scan() {
     
		messages <- who + ":" + input.Text()
	}

	leaving <- ch
	messages <- who + "has left"
	conn.Close()
}

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

客户端

客户端相对简单,只是涉及到信息的发送和接受工作。

package main

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

func main() {
     
	conn, err := net.Dial("tcp","localhost:8000")
	if err != nil {
     
		log.Fatal("Connected has been refused!",err)
	}
	defer conn.Close()
	go mesCopy(os.Stdout,conn)
	mesCopy(conn,os.Stdin)
}

func mesCopy(des io.Writer, res io.Reader)  {
     
	if _, err := io.Copy(des, res); err != nil {
     
		log.Print("wrong!")
	}
}

总结

实现原理较为简单,所以代码并没有多少注释,如果有任何疑问,欢迎留言讨论。最后说一句,在MIT的课程中,其实并不是很推荐在并发编程中使用通道chan,除非你对其应用的场景和可能出现的情况有很好的把握,不然可能会出现很多不可预测的事情,譬如死锁(见另外一篇博客)。在这种时候,共享变量将会是一种很好的选择,具体查看go多线程实践。

你可能感兴趣的:(go,多线程,网络,go)