https://www.youtube.com/watch?v=R_v4fxoNKMg&list=PLy-g2fnSzUTDALoERcKDniql16SAaQYHF&index=13
Websocket 을 이용한 Echo Server
- go 서버
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/pat"
"github.com/gorilla/websocket"
"github.com/urfave/negroni"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
type Message struct {
Type string `json:"type"`
Data interface{} `json:"data"`
}
func wshandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
for {
m := &Message{}
err := conn.ReadJSON(m)
if err != nil {
log.Println(err)
return
}
err = conn.WriteJSON(m)
if err != nil {
log.Println(err)
return
}
}
}
func main() {
mux := pat.New()
mux.Get("/ws", wshandler)
n := negroni.Classic()
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
}
- index.html
<html>
<head>
<title>WebSocket Chatting program</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="chat.js"></script>
</head>
<body>
<div id="chat-log"></div>
<div id="user-name"></div>
<form id="input-form">
<input type="text" id="chat-msg" size="64" autofocus />
<input type="submit" value="Send" />
</form>
</body>
</html>
- chat.js
$(function(){
if (!window.WebSocket) {
alert("No WebSocket!")
return
}
var $chatlog = $('#chat-log')
var $chatmsg = $('#chat-msg')
addmessage = function(data) {
$chatlog.prepend("<div><span>"+data+"</span></div>");
}
connect = function() {
ws = new WebSocket("ws://" + window.location.host + "/ws");
ws.onopen = function(e) {
console.log("onopen", arguments);
};
ws.onclose = function(e) {
console.log("onclose", arguments);
};
ws.onmessage = function(e) {
addmessage(e.data);
};
}
connect();
var isBlank = function(string) {
return string == null || string.trim() === "";
};
var username;
while (isBlank(username)) {
username = prompt("What's your name?");
if (!isBlank(username)) {
$('#user-name').html('<b>' + username + '</b>');
}
}
$('#input-form').on('submit', function(e){
if (ws.readyState === ws.OPEN) {
ws.send(JSON.stringify({
type: "msg",
data: $chatmsg.val()
}));
}
$chatmsg.val("");
$chatmsg.focus();
return false;
});
})
EventSource를 이용한 채팅서버
https://yari-demos.prod.mdn.mozit.cloud/ko/docs/Web/API/EventSource
- main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"github.com/antage/eventsource"
"github.com/gorilla/pat"
"github.com/urfave/negroni"
)
func postMessageHandler(w http.ResponseWriter, r *http.Request) {
msg := r.FormValue("msg")
name := r.FormValue("name")
sendMessage(name, msg)
}
func addUserHandler(w http.ResponseWriter, r *http.Request) {
username := r.FormValue("name")
sendMessage("", fmt.Sprintf("add user: %s", username))
}
func leftUserHandler(w http.ResponseWriter, r *http.Request) {
username := r.FormValue("username")
sendMessage("", fmt.Sprintf("left user: %s", username))
}
type Message struct {
Name string `json:"name"`
Msg string `json:"msg"`
}
var msgCh chan Message
func sendMessage(name, msg string) {
// send message to every clients
msgCh <- Message{name, msg}
}
func processMsgCh(es eventsource.EventSource) {
for msg := range msgCh {
data, _ := json.Marshal(msg)
es.SendEventMessage(string(data), "", strconv.Itoa(time.Now().Nanosecond()))
}
}
func main() {
msgCh = make(chan Message)
es := eventsource.New(nil, nil)
defer es.Close()
go processMsgCh(es)
mux := pat.New()
mux.Post("/messages", postMessageHandler)
mux.Handle("/stream", es)
mux.Post("/users", addUserHandler)
mux.Delete("/users", leftUserHandler)
n := negroni.Classic()
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
}
- index.html
<html>
<head>
<title>Simple Chat</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript" src="chat.js"></script>
</head>
<body>
<div id="chat-log"></div>
<div id="user-name"></div>
<form id="input-form">
<input type="text" id="chat-msg" size="64" autofocus />
<input type="submit" value="Send" />
</form>
</body>
</html>
- chat.js
$(function(){
if (!window.EventSource) {
alert("No EventSource!")
return
}
var $chatlog = $('#chat-log')
var $chatmsg = $('#chat-msg')
var isBlank = function(string) {
return string == null || string.trim() === "";
};
var username;
while (isBlank(username)) {
username = prompt("What's your name?");
if (!isBlank(username)) {
$('#user-name').html('<b>' + username + '</b>');
}
}
$('#input-form').on('submit', function(e){
$.post('/messages', {
msg: $chatmsg.val(),
name: username
});
$chatmsg.val("");
$chatmsg.focus();
return false;
});
var addMessage = function(data) {
var text = "";
if (!isBlank(data.name)) {
text = '<strong>' + data.name + ':</strong> ';
}
text += data.msg;
$chatlog.prepend('<div><span>' + text + '</span></div>');
};
var es = new EventSource('/stream');
es.onopen = function(e) {
$.post('users/', {
name: username
});
}
es.onmessage = function(e) {
var msg = JSON.parse(e.data);
addMessage(msg);
};
window.onbeforeunload = function() {
$.ajax({
url: "/users?username=" + username,
type: "DELETE"
});
es.close()
};
})
Websocket을 사용한 다중사용자 채팅
package main
import (
"container/list"
"fmt"
"log"
"net/http"
"github.com/gorilla/pat"
"github.com/gorilla/websocket"
"github.com/urfave/negroni"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
type Message struct {
name string `json:"username"`
Type string `json:"type"`
Data string `json:"data"`
// Data interface{} `json:"data"`
}
// ================== Queue ==============================
type Queue struct {
v *list.List
}
func (q *Queue) Push(val interface{}) {
q.v.PushBack(val)
}
func (q *Queue) Pop() interface{} {
front := q.v.Front()
if front != nil {
return q.v.Remove(front)
}
return nil
}
func NewQueue() *Queue {
return &Queue{list.New()}
}
// =======================================================
// ================== Client ==============================
type WClient struct {
idx int
name string
state int
queue *Queue
}
func (user *WClient) setState(s int) {
user.state = s
}
func (user *WClient) addMsg(msg *Message) {
user.queue.Push(msg)
}
// =======================================================
type Manager struct {
m map[int]*WClient
idx int
}
func (manager *Manager) getIndex() int {
manager.idx++
return manager.idx
}
func (manager *Manager) broadcast(m *Message) {
for _, value := range manager.m {
value.queue.Push(m)
}
}
func (manager *Manager) wisper(m *Message, str string) {
for _, value := range manager.m {
if value.name == str {
value.addMsg(m)
}
}
}
var m *Manager
func newManager() *Manager {
return &Manager{m: make(map[int]*WClient), idx: 0}
}
func getManager() *Manager {
if m == nil {
m = newManager()
}
return m
}
// =======================================================
func funcClient(w http.ResponseWriter, r *http.Request, conn *websocket.Conn, user *WClient) {
defer fmt.Println("funcClient exit")
defer delete(getManager().m, user.idx)
for {
if user.state == 0 {
return
}
msg := user.queue.Pop()
if msg != nil {
err := conn.WriteJSON(msg)
if err != nil {
log.Println(err)
return
}
}
}
}
func wshandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
fmt.Println("Client Creatd")
user := &WClient{idx: getManager().getIndex(), name: "noname", state: 1, queue: NewQueue()}
getManager().m[user.idx] = user
defer user.setState(0)
go funcClient(w, r, conn, user)
for {
m := &Message{}
err := conn.ReadJSON(m)
if err != nil {
log.Println(err)
return
}
mtype := m.Type
m.Type = user.name
if mtype == "name" && user.name == "noname" {
user.name = m.Data
continue
}
if mtype == "msg" {
getManager().broadcast(m)
continue
}
getManager().wisper(m, mtype)
}
}
func main() {
getManager()
mux := pat.New()
mux.Get("/ws", wshandler)
n := negroni.Classic()
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
}
index.html
<html>
<head>
<title>WebSocket Chatting program</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="chat.js"></script>
</head>
<body>
<div id="chat-log"></div>
<div id="user-name"></div>
<form id="input-form">
<input type="hidden" id="username" value="noname" />
<input type="text" id="chat-type" size="64" value="msg" />
<input type="text" id="chat-msg" size="64" autofocus />
<input type="submit" value="Send" />
</form>
</body>
</html>
chat.js
$(function(){
if (!window.WebSocket) {
alert("No WebSocket!")
return
}
var $chatlog = $('#chat-log')
var $chatmsg = $('#chat-msg')
var $chattype = $('#chat-type')
addmessage = function(data) {
obj = JSON.parse(data)
str = obj.type + ":" + obj.data;
$chatlog.prepend("<div><span>"+str+"</span></div>");
}
connect = function() {
ws = new WebSocket("ws://" + window.location.host + "/ws");
ws.onopen = function(e) {
console.log("onopen", arguments);
ws.send(JSON.stringify({
type: "name",
data: username
}));
};
ws.onclose = function(e) {
console.log("onclose", arguments);
};
ws.onmessage = function(e) {
addmessage(e.data);
};
}
connect();
var isBlank = function(string) {
return string == null || string.trim() === "";
};
var username;
while (isBlank(username)) {
username = prompt("What's your name?");
if (!isBlank(username)) {
$('#user-name').html('<b>' + username + '</b>');
}
}
$('#input-form').on('submit', function(e){
if (ws.readyState === ws.OPEN) {
ws.send(JSON.stringify({
name: username,
type: $chattype.val(), // "msg",
data: $chatmsg.val()
}));
}
$chatmsg.val("");
$chatmsg.focus();
return false;
});
})
'Go' 카테고리의 다른 글
Go 웹서버관련 - Render, Pat, Negroni (0) | 2024.03.25 |
---|---|
[묘공단] Tucker의 Go 언어 프로그래밍 31장 : Todo 리스트 웹사이트 만들기 (0) | 2024.03.24 |
[묘공단] Tucker의 Go 언어 프로그래밍 30장 : RESTful API 서버 만들기 (0) | 2024.03.22 |
[묘공단] Tucker의 Go 언어 프로그래밍 29장 : Go언어로 만드는 웹서버 (0) | 2024.03.16 |
[묘공단] Tucker의 Go 언어 프로그래밍 26장 : 단어검색프로그램 (0) | 2024.03.09 |