Skip to content

Instantly share code, notes, and snippets.

@dadamssg
Last active August 29, 2015 14:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dadamssg/30f47974fc62ce8cb6f2 to your computer and use it in GitHub Desktop.
Save dadamssg/30f47974fc62ce8cb6f2 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Chat</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<script>
$(function() {
var $chatName = $("#chat-name-input");
var $memberName = $("#member-name-input");
var $input = $("#msg-input");
var $button = $("#msg-button");
var $ul = $("#messages");
var appendMsg = function (msg) {
$ul.append("<li>" + msg + "</li>");
}
$("#chat-name-button").click(function () {
var roomName = $chatName.val();
var memberName = $memberName.val();
var conn = new WebSocket("ws://192.168.56.109:8089/ws?room=" + roomName + "&name=" + memberName);
conn.onclose = function(e) {
appendMsg("Disconnected from '" + roomName + "'");
};
conn.onopen = function(e) {
appendMsg("Connected to '" + roomName + "' as '" + memberName + "'");
};
conn.onmessage = function(e){
appendMsg(e.data);
};
$button.click(function () {
conn.send($input.val());
$input.val('');
return false;
});
$chatName.val('');
$memberName.val('');
return false;
});
});
</script>
</head>
<body>
<div class="row">
<div class="col-md-4">
<h3>Join</h3>
<form class="form-inline" role="form">
<div class="form-group">
<input type="text" class="form-control" id="chat-name-input" placeholder="Chat room">
<input type="text" class="form-control" id="member-name-input" placeholder="Nick">
</div>
<button type="submit" class="btn btn-primary" id="chat-name-button">Join</button>
</form>
</div>
<div class="col-md-4">
<h3>Chat</h3>
<div>
<ul id="messages">
</ul>
</div>
<form class="form-inline" role="form">
<div class="form-group">
<input type="text" class="form-control" id="msg-input" placeholder="Message">
</div>
<button type="submit" class="btn btn-primary" id="msg-button">Send</button>
</form>
</div>
<div class="col-md-4"></div>
</div>
</body>
</html>
package main
import (
"flag"
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
type user struct {
name string
send chan []byte
conn *websocket.Conn
}
func (u *user) reader(broadcast chan []byte) {
for {
_, msg, err := u.conn.ReadMessage()
if err != nil {
break
}
log.Printf("'%s' wrote message: '%s'.", u.name, string(msg))
broadcast <- msg
}
}
func (u *user) writer() {
for msg := range u.send {
log.Printf("'%s' is being sent message: '%s'.", u.name, string(msg))
err := u.conn.WriteMessage(websocket.TextMessage, msg)
if err != nil {
break
}
}
}
type chatRoom struct {
name string
broadcast chan []byte
register chan *user
unregister chan *user
members map[*user]bool
}
func (room *chatRoom) open() {
for {
select {
case u := <-room.register:
room.members[u] = true
log.Printf("'%s' connected to '%s'.", u.name, room.name)
case u := <-room.unregister:
if _, ok := room.members[u]; ok {
delete(room.members, u)
close(u.send)
log.Printf("'%s' disconnected from '%s'.", u.name, room.name)
}
case msg := <-room.broadcast:
for u := range room.members {
select {
case u.send <- msg:
default:
delete(room.members, u)
close(u.send)
}
}
}
}
}
type roomFinder struct {
name string
finder chan *chatRoom
}
type chatHouse struct {
rooms map[*chatRoom]bool
finder chan *roomFinder
destroy chan *chatRoom
}
func (house *chatHouse) open() {
for {
select {
case f := <-house.finder:
name := f.name
var room *chatRoom
for r := range house.rooms {
if r.name == name {
log.Printf("'%s' room found.", name)
room = r
}
}
if room == nil {
room = &chatRoom{
name: name,
broadcast: make(chan []byte),
register: make(chan *user),
unregister: make(chan *user),
members: make(map[*user]bool),
}
go room.open()
house.rooms[room] = true
log.Printf("'%s' room created.", name)
}
f.finder <- room
case room := <-house.destroy:
if _, ok := house.rooms[room]; ok {
close(room.broadcast)
close(room.register)
close(room.unregister)
delete(house.rooms, room)
}
}
}
}
func getWsHandler(upgrader *websocket.Upgrader, house *chatHouse) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
roomName := string(r.URL.Query()["room"][0])
memberName := string(r.URL.Query()["name"][0])
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
defer func() { conn.Close() }()
u := &user{
name: memberName,
send: make(chan []byte, 256),
conn: conn,
}
finderChan := make(chan *chatRoom)
finder := &roomFinder{name: roomName, finder: finderChan}
house.finder <- finder
room := <-finderChan
close(finderChan)
room.register <- u
defer func() { room.unregister <- u }()
go u.writer()
u.reader(room.broadcast)
}
}
func main() {
port := flag.Int("port", 8089, "port to serve on")
dir := flag.String("directory", "web/", "directory of web files")
flag.Parse()
upgrader := &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024}
house := &chatHouse{
rooms: make(map[*chatRoom]bool),
finder: make(chan *roomFinder),
destroy: make(chan *chatRoom),
}
go house.open()
fs := http.Dir(*dir)
fileHandler := http.FileServer(fs)
http.Handle("/", fileHandler)
http.Handle("/ws", getWsHandler(upgrader, house))
log.Printf("Running on port %d\n", *port)
addr := fmt.Sprintf(":%d", *port)
err := http.ListenAndServe(addr, nil)
fmt.Println(err.Error())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment