1. WebSocket编程

1.1.1. webSocket是什么

  • WebSocket是一种在单个TCP连接上进行全双工通信的协议
  • WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据
  • 在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
  • 需要安装第三方包:
    • cmd中:go get -u -v github.com/gorilla/websocket

1.1.2. 举个聊天室的小例子

在同一级目录下新建四个go文件connection.go|data.go|hub.go|server.go

运行

  1. go run server.go hub.go data.go connection.go

运行之后执行local.html文件

server.go文件代码

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "github.com/gorilla/mux"
  6. )
  7. func main() {
  8. router := mux.NewRouter()
  9. go h.run()
  10. router.HandleFunc("/ws", myws)
  11. if err := http.ListenAndServe("127.0.0.1:8080", router); err != nil {
  12. fmt.Println("err:", err)
  13. }
  14. }

hub.go文件代码

  1. package main
  2. import "encoding/json"
  3. var h = hub{
  4. c: make(map[*connection]bool),
  5. u: make(chan *connection),
  6. b: make(chan []byte),
  7. r: make(chan *connection),
  8. }
  9. type hub struct {
  10. c map[*connection]bool
  11. b chan []byte
  12. r chan *connection
  13. u chan *connection
  14. }
  15. func (h *hub) run() {
  16. for {
  17. select {
  18. case c := <-h.r:
  19. h.c[c] = true
  20. c.data.Ip = c.ws.RemoteAddr().String()
  21. c.data.Type = "handshake"
  22. c.data.UserList = user_list
  23. data_b, _ := json.Marshal(c.data)
  24. c.sc <- data_b
  25. case c := <-h.u:
  26. if _, ok := h.c[c]; ok {
  27. delete(h.c, c)
  28. close(c.sc)
  29. }
  30. case data := <-h.b:
  31. for c := range h.c {
  32. select {
  33. case c.sc <- data:
  34. default:
  35. delete(h.c, c)
  36. close(c.sc)
  37. }
  38. }
  39. }
  40. }
  41. }

data.go文件代码

  1. package main
  2. type Data struct {
  3. Ip string `json:"ip"`
  4. User string `json:"user"`
  5. From string `json:"from"`
  6. Type string `json:"type"`
  7. Content string `json:"content"`
  8. UserList []string `json:"user_list"`
  9. }

connection.go文件代码

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "github.com/gorilla/websocket"
  7. )
  8. type connection struct {
  9. ws *websocket.Conn
  10. sc chan []byte
  11. data *Data
  12. }
  13. var wu = &websocket.Upgrader{ReadBufferSize: 512,
  14. WriteBufferSize: 512, CheckOrigin: func(r *http.Request) bool { return true }}
  15. func myws(w http.ResponseWriter, r *http.Request) {
  16. ws, err := wu.Upgrade(w, r, nil)
  17. if err != nil {
  18. return
  19. }
  20. c := &connection{sc: make(chan []byte, 256), ws: ws, data: &Data{}}
  21. h.r <- c
  22. go c.writer()
  23. c.reader()
  24. defer func() {
  25. c.data.Type = "logout"
  26. user_list = del(user_list, c.data.User)
  27. c.data.UserList = user_list
  28. c.data.Content = c.data.User
  29. data_b, _ := json.Marshal(c.data)
  30. h.b <- data_b
  31. h.r <- c
  32. }()
  33. }
  34. func (c *connection) writer() {
  35. for message := range c.sc {
  36. c.ws.WriteMessage(websocket.TextMessage, message)
  37. }
  38. c.ws.Close()
  39. }
  40. var user_list = []string{}
  41. func (c *connection) reader() {
  42. for {
  43. _, message, err := c.ws.ReadMessage()
  44. if err != nil {
  45. h.r <- c
  46. break
  47. }
  48. json.Unmarshal(message, &c.data)
  49. switch c.data.Type {
  50. case "login":
  51. c.data.User = c.data.Content
  52. c.data.From = c.data.User
  53. user_list = append(user_list, c.data.User)
  54. c.data.UserList = user_list
  55. data_b, _ := json.Marshal(c.data)
  56. h.b <- data_b
  57. case "user":
  58. c.data.Type = "user"
  59. data_b, _ := json.Marshal(c.data)
  60. h.b <- data_b
  61. case "logout":
  62. c.data.Type = "logout"
  63. user_list = del(user_list, c.data.User)
  64. data_b, _ := json.Marshal(c.data)
  65. h.b <- data_b
  66. h.r <- c
  67. default:
  68. fmt.Print("========default================")
  69. }
  70. }
  71. }
  72. func del(slice []string, user string) []string {
  73. count := len(slice)
  74. if count == 0 {
  75. return slice
  76. }
  77. if count == 1 && slice[0] == user {
  78. return []string{}
  79. }
  80. var n_slice = []string{}
  81. for i := range slice {
  82. if slice[i] == user && i == count {
  83. return slice[:count]
  84. } else if slice[i] == user {
  85. n_slice = append(slice[:i], slice[i+1:]...)
  86. break
  87. }
  88. }
  89. fmt.Println(n_slice)
  90. return n_slice
  91. }

local.html文件代码

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title></title>
  5. <meta http-equiv="content-type" content="text/html;charset=utf-8">
  6. <style>
  7. p {
  8. text-align: left;
  9. padding-left: 20px;
  10. }
  11. </style>
  12. </head>
  13. <body>
  14. <div style="width: 800px;height: 600px;margin: 30px auto;text-align: center">
  15. <h1>www.5lmh.comy演示聊天室</h1>
  16. <div style="width: 800px;border: 1px solid gray;height: 300px;">
  17. <div style="width: 200px;height: 300px;float: left;text-align: left;">
  18. <p><span>当前在线:</span><span id="user_num">0</span></p>
  19. <div id="user_list" style="overflow: auto;">
  20. </div>
  21. </div>
  22. <div id="msg_list" style="width: 598px;border: 1px solid gray; height: 300px;overflow: scroll;float: left;">
  23. </div>
  24. </div>
  25. <br>
  26. <textarea id="msg_box" rows="6" cols="50" onkeydown="confirm(event)"></textarea><br>
  27. <input type="button" value="发送" onclick="send()">
  28. </div>
  29. </body>
  30. </html>
  31. <script type="text/javascript">
  32. var uname = prompt('请输入用户名', 'user' + uuid(8, 16));
  33. var ws = new WebSocket("ws://127.0.0.1:8080/ws");
  34. ws.onopen = function () {
  35. var data = "系统消息:建立连接成功";
  36. listMsg(data);
  37. };
  38. ws.onmessage = function (e) {
  39. var msg = JSON.parse(e.data);
  40. var sender, user_name, name_list, change_type;
  41. switch (msg.type) {
  42. case 'system':
  43. sender = '系统消息: ';
  44. break;
  45. case 'user':
  46. sender = msg.from + ': ';
  47. break;
  48. case 'handshake':
  49. var user_info = {'type': 'login', 'content': uname};
  50. sendMsg(user_info);
  51. return;
  52. case 'login':
  53. case 'logout':
  54. user_name = msg.content;
  55. name_list = msg.user_list;
  56. change_type = msg.type;
  57. dealUser(user_name, change_type, name_list);
  58. return;
  59. }
  60. var data = sender + msg.content;
  61. listMsg(data);
  62. };
  63. ws.onerror = function () {
  64. var data = "系统消息 : 出错了,请退出重试.";
  65. listMsg(data);
  66. };
  67. function confirm(event) {
  68. var key_num = event.keyCode;
  69. if (13 == key_num) {
  70. send();
  71. } else {
  72. return false;
  73. }
  74. }
  75. function send() {
  76. var msg_box = document.getElementById("msg_box");
  77. var content = msg_box.value;
  78. var reg = new RegExp("\r\n", "g");
  79. content = content.replace(reg, "");
  80. var msg = {'content': content.trim(), 'type': 'user'};
  81. sendMsg(msg);
  82. msg_box.value = '';
  83. }
  84. function listMsg(data) {
  85. var msg_list = document.getElementById("msg_list");
  86. var msg = document.createElement("p");
  87. msg.innerHTML = data;
  88. msg_list.appendChild(msg);
  89. msg_list.scrollTop = msg_list.scrollHeight;
  90. }
  91. function dealUser(user_name, type, name_list) {
  92. var user_list = document.getElementById("user_list");
  93. var user_num = document.getElementById("user_num");
  94. while(user_list.hasChildNodes()) {
  95. user_list.removeChild(user_list.firstChild);
  96. }
  97. for (var index in name_list) {
  98. var user = document.createElement("p");
  99. user.innerHTML = name_list[index];
  100. user_list.appendChild(user);
  101. }
  102. user_num.innerHTML = name_list.length;
  103. user_list.scrollTop = user_list.scrollHeight;
  104. var change = type == 'login' ? '上线' : '下线';
  105. var data = '系统消息: ' + user_name + ' 已' + change;
  106. listMsg(data);
  107. }
  108. function sendMsg(msg) {
  109. var data = JSON.stringify(msg);
  110. ws.send(data);
  111. }
  112. function uuid(len, radix) {
  113. var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
  114. var uuid = [], i;
  115. radix = radix || chars.length;
  116. if (len) {
  117. for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
  118. } else {
  119. var r;
  120. uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
  121. uuid[14] = '4';
  122. for (i = 0; i < 36; i++) {
  123. if (!uuid[i]) {
  124. r = 0 | Math.random() * 16;
  125. uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
  126. }
  127. }
  128. }
  129. return uuid.join('');
  130. }
  131. </script>