Skip to content

Instantly share code, notes, and snippets.

@nanoninja
Last active September 16, 2017 13:51
Show Gist options
  • Save nanoninja/429bd43af265964df9a111cb6159cef2 to your computer and use it in GitHub Desktop.
Save nanoninja/429bd43af265964df9a111cb6159cef2 to your computer and use it in GitHub Desktop.
Websocket Go Chat

Websocket Go chat

Installation

After installing Go and setting up your GOPATH

go get golang.org/x/net/websocket

Running chat

# Add your personnal ip as 192.168.*.* or your domain as www.example.com

go run main.go -addr=0.0.0.0:3000 

Open your favorite browser : http://localhost:3000

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Websocket</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous" />
<style>
#messages {
height: 120px;
overflow: scroll;
padding: 20px;
}
#messages div {
margin-bottom: 4px;
}
</style>
</head>
<body>
<div class="container">
<h1>Websocket</h1>
<div class="col">
<div class="form-group">
<input type="text" id="username" class="form-control" placeholder="Username" />
</div>
<div class="form-group">
<textarea id="text" class="form-control" placeholder="Write a message and press enter..."></textarea>
</div>
</div>
<div>
<h3>Messages:</h3>
<div id="messages" class="pre-scrollable well"></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script type="text/javascript">
$(document).ready(function () {
var ws = new WebSocket('ws://' + location.host + '/echo');
var username = '';
ws.onopen = function () {
console.log('Connected');
};
ws.onclose = function () {
alert('Disconnected');
}
ws.onmessage = function (e) {
console.log(e.data);
var m = JSON.parse(e.data);
var div = document.createElement('div');
var strg = document.createElement('strong');
strg.appendChild(document.createTextNode(m.username + ': '));
div.appendChild(strg);
div.appendChild(document.createTextNode(m.text));
$(div).hide().appendTo('#messages').fadeIn(300);
};
ws.onerror = function (e) {
console.debug(e.data);
};
$(document).on('keypress', '#text', function (e) {
if (e.keyCode == 13) {
e.preventDefault();
if (username === '') {
username = $('#username').val();
}
var text = $(this);
var msg = {
username: username,
text: text.val()
};
if (text.val() !== '') {
ws.send(JSON.stringify(msg));
text.val('');
}
}
});
});
setInterval(animateScroll($('#messages')), 3000);
function animateScroll(elem) {
return function () {
elem.animate({
scrollTop: elem[0].scrollHeight
}, 1000);
}
}
</script>
</body>
</html>
// Copyright 2017 The Nanoninja Authors. All rights reserved.
package main
import (
"flag"
"html/template"
"io"
"log"
"net/http"
"golang.org/x/net/websocket"
)
var (
addr = flag.String("addr", "0.0.0.0:3000", "host:port")
chat = NewChat()
)
func main() {
flag.Parse()
go chat.Run()
http.HandleFunc("/", indexHandler)
http.HandleFunc("/echo", webSocketHandler)
if err := http.ListenAndServe(*addr, nil); err != nil {
panic(err.Error())
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles("index.html")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
err = t.Execute(w, nil)
}
type User struct {
Name string
Addr string
Output chan Message
}
type Message struct {
Username string `json:"username"`
Text string `json:"text"`
}
type Chat struct {
Users map[string]User
Join chan User
Leave chan User
Input chan Message
}
func NewChat() *Chat {
return &Chat{
Users: make(map[string]User),
Join: make(chan User),
Leave: make(chan User),
Input: make(chan Message),
}
}
func (c *Chat) Run() {
for {
select {
case user := <-c.Join:
c.Users[user.Name] = user
case user := <-c.Leave:
delete(c.Users, user.Name)
case msg := <-c.Input:
for _, user := range c.Users {
user.Output <- msg
}
}
}
}
func webSocketServer(ws *websocket.Conn) {
defer ws.Close()
user := User{
Output: make(chan Message),
Addr: ws.Request().RemoteAddr,
}
defer func() {
chat.Leave <- user
}()
go func() {
for {
m := Message{}
if err := websocket.JSON.Receive(ws, &m); err != nil {
if err == io.EOF {
log.Printf("%s has disconnected", user.Name)
chat.Leave <- user
break
}
}
user.Name = m.Username
chat.Join <- user
m.Text = textLimit(255)(m.Text)
chat.Input <- m
addr := chat.Users[m.Username].Addr
log.Printf("[%s] %s: %s\n", addr, m.Username, m.Text)
}
}()
for msg := range user.Output {
if err := websocket.JSON.Send(ws, msg); err != nil {
log.Println(err)
break
}
}
}
func webSocketHandler(w http.ResponseWriter, r *http.Request) {
s := websocket.Server{Handler: websocket.Handler(webSocketServer)}
s.ServeHTTP(w, r)
}
type filter func(s string) string
func textLimit(length int) filter {
return func(s string) string {
if len(s) > length {
return s[:length]
}
return s
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment