Go---Go网络编程(详细)(二)

简介: Go---Go网络编程(详细)

UDP编程

UDP协议

UDP协议(User Datagram Protocol)中文名称是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联)参考模型中一种无连接的传输层协议,不需要建立连接就能直接进行数据发送和接收,属于不可靠的、没有时序的通信,但是UDP协议的实时性比较好,通常用于视频直播相关领域。

UDP服务端应用示例

使用Go语言的net包实现UDP服务端代码如下:

package main
import (
  "fmt"
  "net"
)
// UDP 服务端
func main() {
  listen, err := net.ListenUDP("udp", &net.UDPAddr{
    IP:   net.IPv4(0, 0, 0, 0),
    Port: 30000,
  })
  if err != nil {
    fmt.Println("listen failed,err =",err)
    return
  }
  defer listen.Close()
  for {
    var data [1024]byte
    // 接收数据
    n, addr, err := listen.ReadFromUDP(data[:])
    if err != nil {
      fmt.Println("read udp failed,err =",err)
      continue
    }
    fmt.Printf("data:%v add:%v count:%v\n",string(data[:n]),addr,n)
    // 发送数据
    _, err = listen.WriteToUDP(data[:n], addr)
    if err != nil {
      fmt.Println("write to udp failed,err =",err)
      continue
    }
  }
}

UDP客户端应用示例

package main
import (
  "bufio"
  "fmt"
  "net"
  "os"
  "strings"
)
// UDP 客户端
func main() {
  socket, err := net.DialUDP("udp", nil, &net.UDPAddr{
    IP:   net.IPv4(0, 0, 0, 0),
    Port: 30000,
  })
  if err != nil {
    fmt.Println("连接服务端失败,err =",err)
    return
  }
  defer socket.Close()
  inputReader := bufio.NewReader(os.Stdin)
  // 读取用户输入
  for {
    readString, _ := inputReader.ReadString('\n')
    trim := strings.Trim(readString, "\r\n")
    // 如果想退出就输入 q
    if strings.ToUpper(trim) == "Q" {
      return
    }
    // 发送数据
    _, err = socket.Write([]byte(trim))
    if err != nil {
      fmt.Println("发送数据失败,err =",err)
      return
    }
    data := make([]byte,4096)
    // 接收数据
    n, remoAddr, err := socket.ReadFromUDP(data)
    if err != nil {
      fmt.Println("接收数据失败,err =",err)
      return
    }
    fmt.Printf("recv:%v addr:%v count:%v\n",string(data[:n]),remoAddr,n)
  }
}


HTTP编程

web工作流程

  • Web服务器的工作原理可以简单地归纳为
  • 客户机通过TCP/IP协议建立到服务器的TCP连接
  • 客户端向服务器发送HTTP协议请求包,请求服务器里的资源文档
  • 服务器向客户机发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理“动态内容”,并将处理得到的数据返回给客户端
  • 客户机与服务器断开。由客户端解释HTML文档,在客户端屏幕上渲染图形结果

HTTP协议

  • 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,它详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议
  • HTTP协议通常承载于TCP协议之上

HTTP通信模拟

服务端

package main
import (
  "fmt"
  "net/http"
)
//handler 函数
func myHandler(w http.ResponseWriter, r *http.Request) {
  fmt.Println(r.RemoteAddr,"连接成功")
  // 请求方式
  fmt.Println("method:",r.Method)
  // /go
  fmt.Println("url:",r.URL.Path)
  fmt.Println("header:",r.Header)
  fmt.Println("body:",r.Body)
}
func main() {
  // http://127.0.0.1:80000/go
  // 单独写回调函数
  http.HandleFunc("/go",myHandler)
  // addr: 监听的地址
  // handler: 回调的函数
  http.ListenAndServe("127.0.0.1:8000",nil)
}

客户端

package main
import (
  "fmt"
  "io"
  "net/http"
)
func main() {
  resp, _ := http.Get("http://127.0.0.1:8000/go")
  defer resp.Body.Close()
  // 200 OK
  fmt.Println(resp.Status)
  fmt.Println(resp.Header)
  buf := make([]byte,1024)
  for {
    // 接收服务端信息
    n, err := resp.Body.Read(buf)
    if err != nil && err != io.EOF {
      fmt.Println("接收服务端信息失败,err =",err)
      return
    } else {
      fmt.Println("读取完毕")
      res := string(buf[:n])
      fmt.Println("服务端发来的信息为:",res)
      break
    }
  }
}

打开服务端再点击客户端如下


WebSocket编程

WebSocket是什么

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

聊天室的例子

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

server.go

package main
import (
  "fmt"
  "github.com/gorilla/mux"
  "net/http"
)
func main() {
  router := mux.NewRouter()
  go h.run()
  router.HandleFunc("/ws",myws)
  err := http.ListenAndServe("127.0.0.1:8080",router)
  if err != nil {
    fmt.Println("err:",err)
  }
}

hub.go

package main
import "encoding/json"
var h = hub{
  c: make(map[*connection]bool),
  u: make(chan *connection),
  b: make(chan []byte),
  r: make(chan *connection),
}
type hub struct {
  c map[*connection]bool
  b chan []byte
  r chan *connection
  u chan *connection
}
func (h *hub) run() {
  for {
    select {
    case c := <-h.r:
      h.c[c] = true
      c.data.Ip = c.ws.RemoteAddr().String()
      c.data.Type = "handshake"
      c.data.UserList = user_list
      data_b, _ := json.Marshal(c.data)
      c.sc <- data_b
    case c := <-h.u:
      if _, ok := h.c[c]; ok {
        delete(h.c, c)
        close(c.sc)
      }
    case data := <-h.b:
      for c := range h.c {
        select {
        case c.sc <- data:
        default:
          delete(h.c, c)
          close(c.sc)
        }
      }
    }
  }
}

data.go

package main
type Data struct {
  Ip       string   `json:"ip"`
  User     string   `json:"user"`
  From     string   `json:"from"`
  Type     string   `json:"type"`
  Content  string   `json:"content"`
  UserList []string `json:"user_list"`
}

connection.go

package main
import (
  "encoding/json"
  "fmt"
  "net/http"
  "github.com/gorilla/websocket"
)
type connection struct {
  ws   *websocket.Conn
  sc   chan []byte
  data *Data
}
var wu = &websocket.Upgrader{ReadBufferSize: 512,
  WriteBufferSize: 512, CheckOrigin: func(r *http.Request) bool { return true }}
func myws(w http.ResponseWriter, r *http.Request) {
  ws, err := wu.Upgrade(w, r, nil)
  if err != nil {
    return
  }
  c := &connection{sc: make(chan []byte, 256), ws: ws, data: &Data{}}
  h.r <- c
  go c.writer()
  c.reader()
  defer func() {
    c.data.Type = "logout"
    user_list = del(user_list, c.data.User)
    c.data.UserList = user_list
    c.data.Content = c.data.User
    data_b, _ := json.Marshal(c.data)
    h.b <- data_b
    h.r <- c
  }()
}
func (c *connection) writer() {
  for message := range c.sc {
    c.ws.WriteMessage(websocket.TextMessage, message)
  }
  c.ws.Close()
}
var user_list = []string{}
func (c *connection) reader() {
  for {
    _, message, err := c.ws.ReadMessage()
    if err != nil {
      h.r <- c
      break
    }
    json.Unmarshal(message, &c.data)
    switch c.data.Type {
    case "login":
      c.data.User = c.data.Content
      c.data.From = c.data.User
      user_list = append(user_list, c.data.User)
      c.data.UserList = user_list
      data_b, _ := json.Marshal(c.data)
      h.b <- data_b
    case "user":
      c.data.Type = "user"
      data_b, _ := json.Marshal(c.data)
      h.b <- data_b
    case "logout":
      c.data.Type = "logout"
      user_list = del(user_list, c.data.User)
      data_b, _ := json.Marshal(c.data)
      h.b <- data_b
      h.r <- c
    default:
      fmt.Print("========default================")
    }
  }
}
func del(slice []string, user string) []string {
  count := len(slice)
  if count == 0 {
    return slice
  }
  if count == 1 && slice[0] == user {
    return []string{}
  }
  var n_slice = []string{}
  for i := range slice {
    if slice[i] == user && i == count {
      return slice[:count]
    } else if slice[i] == user {
      n_slice = append(slice[:i], slice[i+1:]...)
      break
    }
  }
  fmt.Println(n_slice)
  return n_slice
}

local.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
    <style>
        p {
            text-align: left;
            padding-left: 20px;
        }
    </style>
</head>
<body>
<div style="width: 800px;height: 600px;margin: 30px auto;text-align: center">
    <h1>演示聊天室</h1>
    <div style="width: 800px;border: 1px solid gray;height: 300px;">
        <div style="width: 200px;height: 300px;float: left;text-align: left;">
            <p><span>当前在线:</span><span id="user_num">0</span></p>
            <div id="user_list" style="overflow: auto;">
            </div>
        </div>
        <div id="msg_list" style="width: 598px;border:  1px solid gray; height: 300px;overflow: scroll;float: left;">
        </div>
    </div>
    <br>
    <textarea id="msg_box" rows="6" cols="50" onkeydown="confirm(event)"></textarea><br>
    <input type="button" value="发送" onclick="send()">
</div>
</body>
</html>
<script type="text/javascript">
    var uname = prompt('请输入用户名', 'user' + uuid(8, 16));
    var ws = new WebSocket("ws://127.0.0.1:8080/ws");
    ws.onopen = function () {
        var data = "系统消息:建立连接成功";
        listMsg(data);
    };
    ws.onmessage = function (e) {
        var msg = JSON.parse(e.data);
        var sender, user_name, name_list, change_type;
        switch (msg.type) {
            case 'system':
                sender = '系统消息: ';
                break;
            case 'user':
                sender = msg.from + ': ';
                break;
            case 'handshake':
                var user_info = {'type': 'login', 'content': uname};
                sendMsg(user_info);
                return;
            case 'login':
            case 'logout':
                user_name = msg.content;
                name_list = msg.user_list;
                change_type = msg.type;
                dealUser(user_name, change_type, name_list);
                return;
        }
        var data = sender + msg.content;
        listMsg(data);
    };
    ws.onerror = function () {
        var data = "系统消息 : 出错了,请退出重试.";
        listMsg(data);
    };
    function confirm(event) {
        var key_num = event.keyCode;
        if (13 == key_num) {
            send();
        } else {
            return false;
        }
    }
    function send() {
        var msg_box = document.getElementById("msg_box");
        var content = msg_box.value;
        var reg = new RegExp("\r\n", "g");
        content = content.replace(reg, "");
        var msg = {'content': content.trim(), 'type': 'user'};
        sendMsg(msg);
        msg_box.value = '';
    }
    function listMsg(data) {
        var msg_list = document.getElementById("msg_list");
        var msg = document.createElement("p");
        msg.innerHTML = data;
        msg_list.appendChild(msg);
        msg_list.scrollTop = msg_list.scrollHeight;
    }
    function dealUser(user_name, type, name_list) {
        var user_list = document.getElementById("user_list");
        var user_num = document.getElementById("user_num");
        while(user_list.hasChildNodes()) {
            user_list.removeChild(user_list.firstChild);
        }
        for (var index in name_list) {
            var user = document.createElement("p");
            user.innerHTML = name_list[index];
            user_list.appendChild(user);
        }
        user_num.innerHTML = name_list.length;
        user_list.scrollTop = user_list.scrollHeight;
        var change = type == 'login' ? '上线' : '下线';
        var data = '系统消息: ' + user_name + ' 已' + change;
        listMsg(data);
    }
    function sendMsg(msg) {
        var data = JSON.stringify(msg);
        ws.send(data);
    }
    function uuid(len, radix) {
        var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
        var uuid = [], i;
        radix = radix || chars.length;
        if (len) {
            for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
        } else {
            var r;
            uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
            uuid[14] = '4';
            for (i = 0; i < 36; i++) {
                if (!uuid[i]) {
                    r = 0 | Math.random() * 16;
                    uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
                }
            }
        }
        return uuid.join('');
    }
</script>

运行

1.先在控制台执行 go run connection.go data.go hub.go server.go

2.然后打开local.html

参考文档:go语言中文文档

相关文章
|
8月前
|
数据采集 监控 安全
Go语言在网络安全中的应用
【2月更文挑战第24天】Go语言,作为一种高效且易于维护的编程语言,近年来在网络安全领域得到了广泛的应用。本文旨在探讨Go语言在网络安全中的应用,包括其在防火墙、入侵检测、网络爬虫以及Web安全等方面的应用,并分析了Go语言在网络安全领域的优势与前景。
|
2月前
|
安全 网络协议 Go
Go语言网络编程
【10月更文挑战第28天】Go语言网络编程
127 65
|
2月前
|
数据库连接 Go 数据库
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
41 1
|
23天前
|
Go 数据安全/隐私保护 UED
优化Go语言中的网络连接:设置代理超时参数
优化Go语言中的网络连接:设置代理超时参数
|
2月前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
70 13
|
2月前
|
网络协议 安全 Go
Go语言的网络编程基础
【10月更文挑战第28天】Go语言的网络编程基础
55 8
|
2月前
|
缓存 网络协议 Unix
Go语言网络编程技巧
【10月更文挑战第27天】Go语言网络编程技巧
44 8
|
2月前
|
网络协议 Go
Go语言网络编程的实例
【10月更文挑战第27天】Go语言网络编程的实例
28 7
|
5月前
|
数据采集 Go 定位技术
使用go并发网络爬虫
使用go并发网络爬虫
|
5月前
|
安全 Java Go
为什么选择Go语言编写网络应用程序
为什么选择Go语言编写网络应用程序