DEV

Go - チャットアプリをつくってみた

#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 []*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)
}
}