Go - チャットアプリをつくってみた
コマンドライン上で、複数人でチャットできるクライアント・サーバ型のやつをつくってみた。まだとりあえず動くレベル。スレッドセーフ?な状態でクライアントを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 []*Clientvar 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) }}