go【第十篇】网络编程

 

TCP/UDP协议

由于tcp/udp协议过于底层基本不会使用,这里不再过多赘述,了解基本即可

网络分层架构

为了减少协议设计的复杂性,大多数网络模型均采用分层的方式来组织。每一层都有自己的功能,就像建筑物一样,每一层都靠下一层支持。每一层利用下一层提供的服务来为上一层提供服务,本层服务的实现细节对上层屏蔽。越下面的层,越靠近硬件;越上面的层,越靠近用户。

链路层

以太网规定,连入网络的所有设备,都必须具有“网卡”接口。数据包必须是从一块网卡,传送到另一块网卡。通过网卡能够使不同的计算机之间连接,从而完成数据通信等功能。网卡的地址——MAC 地址,就是数据包的物理发送地址和物理接收地址。

网络层

网络层的作用是引进一套新的地址,使得我们能够区分不同的计算机是否属于同一个子网络。这套地址就叫做“网络地址”,这是我们平时所说的IP地址。这个IP地址好比我们的手机号码,通过手机号码可以得到用户所在的归属地。网络地址帮助我们确定计算机所在的子网络,MAC 地址则将数据包送到该子网络中的目标网卡。因此,从逻辑上可以推断,必定是先处理网络地址,然后再处理 MAC 地址。

传输层

端口是每一个使用网卡的程序的编号。每个数据包都发到主机的特定端口,所以不同的程序就能取到自己所需要的数据

应用层

应用程序收到“传输层”的数据,接下来就要进行解读。由于互联网是开放架构,数据来源五花八门,必须事先规定好格式,否则根本无法解读。“应用层”的作用,就是规定应用程序的数据格式。

什么是Socket

Socket起源于Unix,而Unix基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用:Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用

socket工作流程

 

一次通话

package main

import (
    "fmt"
    "net"
)

func main() {
    //监听
    listener, err := net.Listen("tcp", "127.0.0.1:8000")
    if err != nil {
        fmt.Println("err = ", err)
        return
    }

    defer listener.Close()

    //阻塞等待用户链接
    conn, err := listener.Accept()
    if err != nil {
        fmt.Println("err = ", err)
        return
    }

    //接收用户的请求
    buf := make([]byte, 1024) //1024大小的缓冲区
    n, err1 := conn.Read(buf)
    if err1 != nil {
        fmt.Println("err1 = ", err1)
        return
    }

    fmt.Println("buf = ", string(buf[:n]))

    defer conn.Close() //关闭当前用户链接
}
server

eg1

client:

server: buf =  hi go

eg2

package main

import (
    "fmt"
    "net"
)

func main() {
    //主动连接服务器
    conn, err := net.Dial("tcp", "127.0.0.1:8000")
    if err != nil {
        fmt.Println("err = ", err)
        return
    }

    defer conn.Close()

    //发送数据
    conn.Write([]byte("are u ok?"))

}
client

server:  buf =  are u ok?

高并发socket

package main

import (
    "fmt"
    "net"
    "strings"
)

//处理用户请求
func HandleConn(conn net.Conn) {
    //函数调用完毕,自动关闭conn
    defer conn.Close()

    //获取客户端的网络地址信息
    addr := conn.RemoteAddr().String()
    fmt.Println(addr, " conncet sucessful")

    buf := make([]byte, 2048)

    for {
        //读取用户数据
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("err = ", err)
            return
        }
        fmt.Printf("[%s]: %s\n", addr, string(buf[:n]))
        fmt.Println("len = ", len(string(buf[:n])))

        //if "exit" == string(buf[:n-1]) { //nc测试
        if "exit" == string(buf[:n-2]) { //自己写的客户端测试, 发送时,多了2个字符, "\r\n"
            fmt.Println(addr, " exit")
            return
        }

        //把数据转换为大写,再给用户发送
        conn.Write([]byte(strings.ToUpper(string(buf[:n]))))
    }

}

func main() {
    //监听
    listener, err := net.Listen("tcp", "127.0.0.1:8000")
    if err != nil {
        fmt.Println("err = ", err)
        return
    }

    defer listener.Close()

    //接收多个用户
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("err = ", err)
            return
        }

        //处理用户请求, 新建一个协程
        go HandleConn(conn)
    }

}
server

HTTP协议

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。

net/http

它提供了HTTP客户端和服务端的实现,这是其它语言没有做的。其它语言一般只提供了HTTP客户端的实现。

如果要深入了解这个包对于web服务器的实现,可以去看《Go Web编程这本书》。

net/http实现了http消息的封装,web框架的基本功能(cookie/session,模板,数据库支持,路由系统),尽管如此,是一个很基础的很简陋的异步socket框架,封装的不是很高层,使用起来仍然有点复杂,因此,很多框架(gin beego等)基于net/http进行了更高级的封装(如orm的支持、更高级的路由系统等)。

以下就是介绍net/http包的关于web的简单使用,目的仅仅是让大家对net/http有更深入更感性的认识,因为这个包过于简陋做WEB开发还是不行的。

HTTP服务端

使用Go语言中的net/http包来编写一个简单的接收HTTP请求的Server端示例,net/http包是对net包的进一步封装,类似于python中的wsgiref专门用来处理HTTP协议的数据。具体的代码如下:

package main

import (
	"fmt"
	"net/http"
)

// http server
func sayHello(w http.ResponseWriter, r *http.Request) {
	dataStr:="Welcome Go Server"
	w.Write([]byte(dataStr))
}

func main() {
	http.HandleFunc("/", sayHello)
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		fmt.Printf("http server failed, err:%v\n", err)
		return
	}
}

package main

import (
	"fmt"
	"net/http"
)

// http server
func sayHello(w http.ResponseWriter, r *http.Request) {
	dataStr := "<h2>Welcome Go Server</h2>"
	w.Write([]byte(dataStr))
}

func main() {
	http.HandleFunc("/", sayHello)
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		fmt.Printf("http server failed, err:%v\n", err)
		return
	}
}

前端支持

经测试,net/http库默认支持html/css/js

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>HTML基础</title>
</head>
<body>

<h2 style="color: red">Welcome Go Server</h2>

</body>

<script>
        function f(){
            console.log("hello")
    
        }
    
        f()
    </script>
</html>
hello.html
package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

// HTTP server端

func sayHello(w http.ResponseWriter, r *http.Request) {

    // 从hello.html文件中读取数据写入到w中
    data, err := ioutil.ReadFile("./hello.html")
    if err != nil {
        fmt.Println("read from file failed, err:", err)
        return
    }
    w.Write(data)
}
func main() {
    http.HandleFunc("/", sayHello) // 注册一个处理 / 的函数
    // 启动服务
    err := http.ListenAndServe("127.0.0.1:9090", nil)
    if err != nil {
        panic(err)
    }
}
Server

表单处理

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>HTML基础</title>
</head>
<body>

<h2 style="color: red">Welcome Go Server</h2>

</body>

<script>
        function f(){
            console.log("hello")
    
        }
    
        f()
    </script>
</html>
hello.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>获取用户输入</title>
</head>
<body>
    <h1>欢迎注册</h1>

    <hr>
    <form action="http://127.0.0.1:9090/index" method="POST">
     <div>
        <label for="i1">用户名:</label>
        <input id="i1" name="username" type="text" placeholder="请输入用户名">
     </div>
    <div>
        <label>&nbsp;&nbsp;&nbsp;码:
            <input name="pwd" type="password">
        </label>
    </div>
    <div>
        <label>性别:</label>
        <input name="gender" value="male" type="radio"><input name="gender" value="female" type="radio"></div>

    <div><label>头像:</label>
        <input type="file" name="avatar" accept="image/*">
    </div>

    <div>
        <label for="s1" >地址</label>
        <select name="addr" id="s1">
            <option value="">---</option>
            <option value="bj">北京</option>
            <option value="sh">上海</option>
            <option value="gz" selected>广州</option>
            <option value="sz">深圳</option>
        </select>
    </div>
    <div>
        <input type="submit" value="提交">
        <button type="submit">提交</button>

        <input type="reset" value="重置">
        <input type="button" value="普通按钮">
    </div>
    </form>
    
</body>
</html>
form.html
package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

// HTTP server端

func sayHello(w http.ResponseWriter, r *http.Request) {
    fmt.Println("请求的url, 方法", r.Method, r.URL)

    // 从hello.html文件中读取数据写入到w中
    data, err := ioutil.ReadFile("./hello.html")
    if err != nil {
        fmt.Println("read from file failed, err:", err)
        return
    }
    w.Write(data)
}

func search(w http.ResponseWriter, r *http.Request) {
    fmt.Println("请求的url, 方法", r.Method, r.URL)

    data, err := ioutil.ReadFile("./form.html")
    if err != nil {
        fmt.Println("read html file failed, err:", err)
        return
    }
    w.Write(data)
}

func index(w http.ResponseWriter, r *http.Request) {
    // r:代表跟请求相关的所有内容
    // 获取注册的信息
    // 获取请求的方法
    fmt.Println("请求的url, 方法", r.Method, r.URL)
    
    r.ParseForm() // 解析
    // 获取表单中的数据
    fmt.Printf("%#v\n", r.Form)
    usernameValue := r.Form.Get("username")
    pwdValue := r.Form.Get("pwd")
    fmt.Println(usernameValue, pwdValue)
    // 校验用户输入信息的有效性
    w.Write([]byte("index"))
}

func main() {
    http.HandleFunc("/", sayHello) // 注册一个处理 / 的函数
    http.HandleFunc("/web", search)
    http.HandleFunc("/index", index)
    http.ListenAndServe("127.0.0.1:9090", nil)
}
Server
分别访问以下三个url
127.0.0.1:9090
127.0.0.1:9090/index
127.0.0.1:9090/web

服务器打印信息

Go模板

文档

 

不使用模板(字符串替换)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>HTML基础</title>
</head>
<body>

<h2 style="color: red">Welcome Go Server</h2>
{ooxx}

</body>

<script>
        function f(){
            console.log("hello")
    
        }
    
        f()
    </script>
</html>
hello.html
package main

import (
    "fmt"
    "io/ioutil"
    "math/rand"
    "net/http"
    "strings"
)

// template 示例

func info(w http.ResponseWriter, r *http.Request) {
    data, err := ioutil.ReadFile("./hello.html")
    if err != nil {
        fmt.Println("read file failed, err:", err)
        return
    }

    num := rand.Intn(10)
    dataStr := string(data) // 转换成字符串
    if num > 5 {
        dataStr = strings.Replace(dataStr, "{ooxx}", "<li>服务器开发</li>", 1)
    } else {
        dataStr = strings.Replace(dataStr, "{ooxx}", "<li>分布式开发</li>", 1)
    }
    w.Write([]byte(dataStr))
}

func main() {
    http.HandleFunc("/info", info)
    http.ListenAndServe("127.0.0.1:8080", nil)
}
Server

 使用模板(本质也是字符串替换)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>HTML基础</title>
</head>
<body>

<h2 style="color: red">Welcome Go Server</h2>

<p>Hello {{.}}</p>

</body>

<script>
        function f(){
            console.log("hello")
    
        }
    
        f()
    </script>
</html>
hello.html
package main

import (
    "fmt"
    "html/template"
    "math/rand"
    "net/http"
)

// template 示例

func info(w http.ResponseWriter, r *http.Request) {
    tpl, err := template.ParseFiles("./hello.html")
    if err != nil {
        fmt.Println("read file failed, err:", err)
        return
    }

    var dataStr string
    num := rand.Intn(10)
    if num > 5 {
        dataStr = "服务器开发"
    } else {
        dataStr = "分布式开发"
    }

    // 用数据去渲染模板
    fmt.Println(dataStr)
    tpl.Execute(w, dataStr)

}

func main() {
    http.HandleFunc("/info", info)
    http.ListenAndServe("127.0.0.1:8080", nil)
}
Server

 

HTTP客户端

// http_client/main.go

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
)

func main() {
	resp, err := http.Get("http://localhost:9090/")
	if err != nil {
		fmt.Println("get failed, err:", err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	fmt.Println(body)
}  

 

 

  

 

posted @ 2019-01-29 12:02  沐风先生  阅读(188)  评论(0编辑  收藏  举报