コマンドライン上で、複数人でチャットできるクライアント・サーバ型のやつをつくってみた。まだとりあえず動くレベル。スレッドセーフ?な状態でクライアントをclientListから消す方法がわからなかったから消すのをやめた。あと、参考サイトだとチャンネルをたくさん使ってるので、自分がつくったチープなやつと比較して研究したいと思ってます。せっかくなので、暗号化とかしてみたい。あとはp2pバージョンもつくってみる。自動でpがpを発見できる仕組みをつくりたい。
参考サイト
8.1 Socketプログラミング チャットで学ぶ Go ネットワークプログラミング mshahriarinia/Golang
Github
コード(サーバ)
package main import ( "./util" "fmt" "io" "math/rand" "net" "os" "time" ) type Client struct { name []byte conn net.Conn color int } var clientList []*Client var colorList = [5]int{32, 33, 34, 35, 36} func send(msg []byte) { for _, cl := range clientList { _, err := cl.conn.Write(msg) if err != nil { continue } } } func receiver(cl *Client) { buf := make([]byte, 560) for { n, err := cl.conn.Read(buf) if err != nil { go send(makeMsgForAdmin(string(cl.name) + " Quit.")) break } go send(makeMsg(buf[:n], cl)) buf = make([]byte, 560) } } func createClient(conn net.Conn) { name := getName(conn) color := getColor() cl := Client{ name: name, conn: conn, color: color, } clientList = append(clientList, &cl) send(makeMsgForAdmin(string(name) + " joined!!")) go receiver(&cl) } func getName(conn net.Conn) []byte { buf := make([]byte, 560) n, err := conn.Read(buf) if err != nil { fmt.Println("Fail get name") Close(conn) os.Exit(1) } return buf[:n] } func getColor() int { rand.Seed(time.Now().UnixNano()) return colorList[rand.Intn(5)] } func Close(c io.Closer) { err := c.Close() if err != nil { fmt.Fprintf(os.Stderr, err.Error()) } } func SprintColor(msg string, color int) string { return fmt.Sprintf("\x1b[%dm%s\x1b[0m", color, msg) } func getTime() string { return time.Now().Format("15:04") } func makeMsg(msg []byte, cl *Client) []byte { new_msg := fmt.Sprintf("%s[%s] %s", getTime(), cl.name, string(msg)) return []byte(SprintColor(new_msg, cl.color)) } func makeMsgForAdmin(msg string) []byte { new_msg := fmt.Sprintf("(%s) %s", getTime(), msg) return []byte(SprintColor(new_msg, 31)) } func main() { service := ":7777" tcpAddr, err := net.ResolveTCPAddr("tcp4", service) util.ChkErr(err, "tcpaddr") li, err := net.ListenTCP("tcp", tcpAddr) util.ChkErr(err, "tcpaddr") for { conn, err := li.Accept() if err != nil { fmt.Println("Fail to connect.") continue } defer Close(conn) createClient(conn) } }
コード(クライアント)
package main import ( "./util" "bufio" "fmt" "io" "net" "os" "time" ) var running = true func sender(conn net.Conn, name string) { reader := bufio.NewReader(os.Stdin) for { input, _, _ := reader.ReadLine() if string(input) == "\\q" { running = false break } _, err := conn.Write(input) util.ChkErr(err, "sender write") } } func receiver(conn net.Conn, name string) { buf := make([]byte, 560) for running == true { n, err := conn.Read(buf) util.ChkErr(err, "Receiver read") fmt.Println(string(buf[:n])) buf = make([]byte, 560) } } func Close(c io.Closer) { err := c.Close() if err != nil { fmt.Fprintf(os.Stderr, err.Error()) } } func main() { fmt.Print("Please input your name: ") reader := bufio.NewReader(os.Stdin) name, _, err := reader.ReadLine() host := "127.0.0.1:7777" tcpAddr, err := net.ResolveTCPAddr("tcp4", host) util.ChkErr(err, "tcpAddr") conn, err := net.DialTCP("tcp", nil, tcpAddr) util.ChkErr(err, "DialTCP") defer Close(conn) _, err = conn.Write(name) util.ChkErr(err, "Write name") go receiver(conn, string(name)) go sender(conn, string(name)) for running { time.Sleep(1 * 1e9) } }