golang网络编程day3

golang网络编程day3

  • golang TCP聊天室
  • golang UDP聊天室
  • URL组成
  • golang URL编程
  • golang http编程
  • http请求方法
  • golanghttp框架

golang TCP聊天室

看懂例子把它理解好,知道实现的原理是什么,还可以自己实现出来。
忘了就看day2的讲解,里面的函数都懂,这个并不难。
首先要知道tcp聊天室的实现理论:客户端采用简单的输入输出方式进行消息的发送和接收,服务端通过广播的方式将信息发送给所有的客户端。

服务端例子

package main

import (
	"fmt"
	"net"
)

var (
	clients    = make(map[net.Addr]net.Conn)
	addCh      = make(chan net.Conn)
	delCh      = make(chan net.Addr)
	messageCh  = make(chan []byte)
	listenAddr = "localhost:8080"
)

func main() {
	fmt.Println("Server started on", listenAddr)
	listener, err := net.Listen("tcp", listenAddr)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer listener.Close()

	go broadcaster()

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println(err)
			continue
		}

		addCh <- conn

		go handleConn(conn)
	}
}

func broadcaster() {
	for {
		select {
		case conn := <-addCh:
			clients[conn.RemoteAddr()] = conn
			fmt.Println("New client:", conn.RemoteAddr())
		case addr := <-delCh:
			delete(clients, addr)
			fmt.Println("Client disconnected:", addr)
		case msg := <-messageCh:
			for _, conn := range clients {
				_, err := conn.Write(msg)
				if err != nil {
					fmt.Println(err)
				}
			}
		}
	}
}

func handleConn(conn net.Conn) {
	defer func() {
		delCh <- conn.RemoteAddr()
		conn.Close()
	}()

	for {
		msg := make([]byte, 4096)
		n, err := conn.Read(msg)
		if err != nil {
			return
		}

		messageCh <- msg[:n]
	}
}

常量解读:
clients:这里客户登记使用了键值对的形式,创建了一个map来登记连接,key是客户端的地址,value是与该地址建立的连接。
addCh :net.Conn类型的通道,这个是用于实现客户端连接建立。
delCh:net.Addr类型的通道,用于实现客户端连接的删除。
messageCH :字节切片类型的通道,放要传输的数据。
listenAddr:监听的地址和端口号。

可能有这样的一个疑问:这里为什么要用通道,用通道有个好处,在并发的环境下不会产生冲突,保证了并发安全。因为是模拟聊天室,那就要考虑一时间有好多个用户接入的情况,我们在操作map的时候就必须保证并发安全,这里保证map的并发安全就使用了通道来进行实现。

main主函数逻辑:
服务器端先调用了net.Listener,先创建一个监听器,然后启动广播协程:这个协程主要是用于监听每一个通道的,等下再说这个函数的细节。然后服务器有个无限循环,因为启动监听之后,就要开始监听客户端发来的请求了,这里一般都是无限循环然后调用listener.Accept()来接收客户端的请求,一旦请求接收成功就建立了Conn,可以进行数据的收发。既然有连接建立那就要进行连接的登记,也就是for里面有个处理:addCh <- conn,这就是连接的登记,把连接送入addCh通道里面。然后启动一个处理函数协程handleConn对连接进行处理。

func broadcaster()函数逻辑
首先要明白select的性质,select是go并发编程里面的内容,与通道相关,每个case都是一个通道操作,当有case的通道操作能够执行,那就会执行通道语句,当有多个通道case有效,会随机选择一个进行执行,这里是for无限循环,那就会处理所有的通道操作。当没有通道操作的时候这个select语句就会阻塞。
所以说这个函数就是来处理所有的通道操作的。来看里面每一个通道的处理

case conn := <-addCh:
			clients[conn.RemoteAddr()] = conn

这个是进行登记处理,net.Conn类型里面的这个函数是登记远端的IP地址,对于服务器来说那就是用户的IP地址。conn就是这个连接,注意这里我前面说过了是采用map来登记。

case addr := <-delCh:
			delete(clients, addr)

这个就是连接断开,然后进行登记的删除,这个delete也是调用了map里面的一个api,删除指定的key也就是addr。

case msg := <-messageCh:
			for _, conn := range clients {
				_, err := conn.Write(msg)
				if err != nil {
					fmt.Println(err)
				}
			}

这个通道处理是进行消息广播的,这里for range clients,就是拿到每一个连接然后把msg发送到这个连接去。可以看到拿到连接后调用了conn.Write(msg)把消息写入每个用户的连接中,达到数据广播的效果。

func handleConn(conn net.Conn)的逻辑,这个函数是用来进行拿到连接后的处理的。因为同一时间要进行操作用户很多,所以要启动协程来进行处理。

func handleConn(conn net.Conn) {
	defer func() {
		delCh <- conn.RemoteAddr()
		conn.Close()
	}()

	for {
		msg := make([]byte, 4096)
		n, err := conn.Read(msg)
		if err != nil {
			return
		}

		messageCh <- msg[:n]
	}
}

defer这里是进行了模拟用户操作完后的连接关闭,所以这里就要给delCH通道发个信息,进行登记的删除,然后关闭这个conn。
for循环是进行了无限循环从连接里面读取数据,因为,msg是广播的。所以每个用户都要随时接收连接里面的数据。
这里接收完数据之后也会做一个messageCh <- msg[:n],也是再次进行消息的发送,这个消息会广播出去。

总结:把握好tcp聊天室的理论原理就是用户输入数据后,服务器负责将消息广播给每个用户。这样就能很好的理解每个操作是为了什么。
实现原理:main函数负责启动开启服务器监听,然后启动通道处理,启动用户请求处理。通道处理复杂所有通道的功能,启动用户请求处理是处理每个用户。


golang UDP聊天室

package main

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

const (
	serverAddr = "localhost:8080"
)

func main() {
	// 解析UDP地址
	addr, err := net.ResolveUDPAddr("udp", serverAddr)
	if err != nil {
		fmt.Println(err)
		return
	}

	// 建立UDP连接
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close()

	fmt.Println("Server started on", serverAddr)

	// 用于保存客户端的地址
	clients := make(map[string]*net.UDPAddr)

	for {
		// 接收请求
		buf := make([]byte, 1024)
		n, clientAddr, err := conn.ReadFromUDP(buf)
		if err != nil {
			fmt.Println(err)
			continue
		}

		// 解析客户端请求
		msg := strings.TrimSpace(string(buf[:n]))
		if msg == "" {
			continue
		}
		fmt.Printf("Received %d bytes from %s: %s\n", n, clientAddr, msg)

		// 如果是新客户端,添加到客户端列表中
		if _, ok := clients[clientAddr.String()]; !ok {
			fmt.Println("New client:", clientAddr)
			clients[clientAddr.String()] = clientAddr
		}

		// 广播消息给其他客户端
		for _, addr := range clients {
			if addr.String() == clientAddr.String() {
				continue // 不发送给发送者
			}
			_, err := conn.WriteToUDP([]byte(msg), addr)
			if err != nil {
				fmt.Println(err)
			}
			fmt.Printf("Sent %d bytes to %s: %s\n", len(msg), addr, msg)
		}
	}
}

解读:
1.UDP和tcp有点区别,这个解析地址的步骤要单独写出来。解析本地地址,端口号8080,调用ResolveUDPAddr()之后会得到解析后的UDP地址UDPAddr,然后进行调用ListenUDP()监听客户端的UDP请求,返回操作数据的连接。
2.这里还是用map来进行保存客户端的地址。
我学这里的时候我会去对比tcp建立聊天室的实现,我发现udp的监听这里不是无限循环监听,这里其实是我理解错了,因为udp是无连接的,所以这里我是这么来方便理解的,相当于这里调了监听函数,就是服务器UDP开了个口子,然后客户端根本不用建立连接,只管往这个
UDPConn里面发数据就完事了。
3.所以说真正的无限循环是在直接对数据进行处理这里。
下面这个无限循环for,直接调用这个conn的读数据,从里面读udp客户端的请求,n, clientAddr, err := conn.ReadFromUDP(buf)调用这个函数,把数据读到buf中,并返回远端的IP地址。
把数据清理后赋给msg并输出。
4.下面访问键值对看这个地址原来在不在,不在就是新连接进行登记。
法消息的时候遍历这个map,这样调用func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error),可以把数据广播给登记的IP地址。这就完成了广播功能。

客户端:

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "time"
)

const (
    serverAddr = "localhost:8080"
)

func main() {
    // 解析服务器地址
    raddr, err := net.ResolveUDPAddr("udp", serverAddr)
    if err != nil {
        fmt.Println(err)
        return
    }

    // 建立与服务器的连接
    conn, err := net.DialUDP("udp", nil, raddr)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer conn.Close()

    // 从标准输入读取数据
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("Enter message: ")
    message, _ := reader.ReadString('\n')

    // 向服务器发送数据
    _, err = conn.Write([]byte(message))
    if err != nil {
        fmt.Println(err)
        return
    }

    // 设置读取超时
    err = conn.SetReadDeadline(time.Now().Add(15 * time.Second))
    if err != nil {
        fmt.Println(err)
        return
    }

    // 从服务器接收数据
    buf := make([]byte, 1024)
    n, _, err := conn.ReadFromUDP(buf)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Printf("Received: %s\n", string(buf[:n]))
}

分析:
客户端也是先解析服务器ip地址
然后调用,net.DialUDP(“udp”, nil, raddr),这样就拿到了conn,相当于建立了连接。(这里注意这并不是真正意义上的连接,只是我这样形容)。
拿到这个conn用户就可以往服务器发数据了,我要发的数据全装在这个reader里面,然后调用reader的Readstring方法把内容给message。
然后调用conn的写把mes传进去。这样就完成了用户发送数据。
下面还设置了一个读取超时。15秒服务器没读就会发生错误。

从服务器接收数据用到了ReadFromUDP,读到的数据都存在了buf里面。
基本逻辑就是这样。


URL组成

专业说法:什么是URL(统一资源定位符),是一种用于定位互联网上资源的地址,它是互联网上标准资源的地址,可以用来访问网页、图片、视频或其他类型的数据。
通俗理解:你在网上点击的每个链接,都是URL,因为你点链接的过程实际上就是请求资源的过程。这个URL就是定义好的访问资源的通用方法。

一个URL通常包含以下几个部分:
1.协议(scheme):定义了访问资源的方法,比如http/https/ftp等
2.域名或IP地址(host):制定了托管资源的服务器位置
3.端口号(可选)(port):用于访问服务器上的特定服务,比如HTTP协议端口号是80,HTTPS是443。
4.路径(path):指定服务器上资源的具体位置
5.查询字符串(可选)(query):以键值对的形式提供额外参数,通常用于提交表单数据或指定资源的某种特定视图。
6.片段标识符(fragment):用于指向网页内的特定部分。

举个例子:这个例子会分析就相当于学会啥是URL了。

https://www.example.com:443/path/to/myfile.html?key1=value1&key2=value2#Section2

分析:
协议:https
域名:www.example.com
端口号443
路径/path/to/myfile.html
查询字符串:key1=value1&key2=value2
注意这里是键值对形式
片段标识符:Section2

//:是协议分隔符,用于分割协议
?是查询字符串的开始,后面跟着一系列键值对,每对键值对用&隔开。
#是片段标识符的开始,它通常用于指向网页中的特定部分。浏览器会滚动到页面中ID为该标识符的元素处。注意片段标识符不会被发送到服务器,它只客户端(i浏览器上使用)。

Golang URL编程

go的标准库net/url包提供了URL编程的实现,该包提供了一些功能,使得我们可以解析、构建和操作URL字符串,注意是字符串,我们平时点的就是字符串。

解析URL

主要用到了net/url包中的Parse函数,该函数可以将URL字符串解析为URL结构体,该结构体包含了URL的各个组成部分.

package main

import (
	"fmt"
	"net/url"
)

func main() {
	// 解析URL
	u, err := url.Parse("https://www.example.com/search?q=golang#top")
	if err != nil {
		fmt.Println(err)
		return
	}

	// 输出URL的各个部分
	fmt.Println("Scheme:", u.Scheme)
	fmt.Println("Host:", u.Host)
	fmt.Println("Path:", u.Path)
	fmt.Println("Query:", u.Query())
	fmt.Println("Fragment:", u.Fragment)
}

输出的内容:

Scheme: https
Host: www.example.com
Path: /search
Query: map[q:[golang]]
Fragment: top

u就是结构体,下面这些都是它里面的属性和方法

总结url.Parse(url string) ,返回值是URL结构体和一个可能发送的错误。
然后就可以操作这个结构体。

构建URL

就是用这个URL结构体转回字符串,相当于上面逆过来。

package main

import (
	"fmt"
	"net/url"
)

func main() {
	// 构建URL
	u := &url.URL{
		Scheme: "https",
		Host:   "www.example.com",
		Path:   "/search",
	}
	q := u.Query()
	q.Set("q", "golang")
	u.RawQuery = q.Encode()

	// 输出URL字符串
	fmt.Println(u.String())
}

解读
u.Query() 用于返回一个url.Values类型,代表URL的查询字符串,这个查询字符串就是上面介绍部分的那个。url.Values是什么类型:map[string][]string,key是字符串,value是字符串切片,这意味着每个键可以对于多个值,这是因为在URL的查询字符串中,同一个键可以有多个值,例如:
?key=value1&key=value2

q.Set(“q”, “golang”) 这个事Values类型的一个方法,用于向查询字符串中添加一个键值对"q=golang"

u.RawQuery = q.Encode()
使用Encode方法,将修改后的查询参数编码为字符串,并将其赋值给u的RawQuery字段。这样URL的查询部分就被设置为"q=golang"

代码运行输出结果:

https://www.example.com/search?q=golang

解析查询参数

要解析查询参数可以使用net/url包中的Values类型。将查询字符串作为传输传递给Values函数,然后通过Get方法来获取特定参数的值。

package main

import (
	"fmt"
	"net/url"
)

func main() {
	// 解析查询参数
	values, err := url.ParseQuery("q=golang&sort=recent&limit=10")
	if err != nil {
		fmt.Println(err)
		return
	}

	// 获取特定参数的值
	q := values.Get("q")
	sort := values.Get("sort")
	limit := values.Get("limit")

	fmt.Println("q:", q)
	fmt.Println("sort:", sort)
	fmt.Println("limit:", limit)
}

解读:
values, err := url.ParseQuery(“q=golang&sort=recent&limit=10”) 用于解读一个URL编码格式的查询字符串。这个字符串包含三个键值对,解析的结果是values类型的,存储在遍历values中。

values.Get(“q”) 用于获取特定参数的值,里面的参数是key,返回值是value

代码运行输出

q: golang
sort: recent
limit: 10

编码和解码

要对URL字符串进行编码和解码就要用net/url包下面的QueryEscape和QueryUnescape函数,分别实现对URL字符串进行编码和解码。

package main

import (
	"fmt"
	"net/url"
)

func main() {
	// 编码字符串
	encoded := url.QueryEscape("https://www.example.com/search?q=golang&sort=recent")
	fmt.Println("Encoded:", encoded)

	// 解码字符串
	decoded, err := url.QueryUnescape(encoded)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Decoded:", decoded)
}

解读:
encoded := url.QueryEscape(“https://www.example.com/search?q=golang&sort=recent”)
用于编码字符串,对给定的字符串进行百分比编码,这个对于编码URL的查询字符串部分很有用,因为对处理URL特殊字符(& = ?)时,会把这些特殊字符转换为百分比形式。
这里的输出结果:

Encoded: https%3A//www.example.com/search%3Fq%3Dgolang%26sort%3Drecent

decoded, err := url.QueryUnescape(encoded)
解码就是进行还原

输出:
Decoded: https://www.example.com/search?q=golang&sort=recent

一个疑问:为什么要换成百分比?
将URL中的特定字符串替换为百分比,这种被定义为URL编码。
这种编码的好处:
1.保留字符:URL中某些字符有特殊含义,百分比编码可以在不改变原有意义的情况下安全地包含这些字符。
2.非ASCII字符,对于某些在ASCII中有特殊含义的字符必须使用百分比编码来标识
3.安全性:百分比编码有助于消除URL中可能引起安全问题的字符。
4.一致性和标准性:通过百分比编码可以确保URL的一致性和标准化,使得不同的网络设备和软件能够正确解析URL。


golang http编程

这种编程是通过net/http包实现的。
这个标注库提供了完善的HTTP客户端和服务器实现。所以可以在go语言实现编写HTTP相关的应用。
直接来看demo,这些都是这个包的应用

HTTP客户端
一个简单的使用net/http包发送 HTTP GET请求的例子

package main

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

func main() {
	resp, err := http.Get("http://www.example.com/")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(string(body))
}

解读:
resp, err := http.Get(“http://www.example.com/”) ,调用http下的这个函数可以向这个指定的网站发送一个GET请求,这个函数非常常用于从指定的URL获取数据。
参数就是一个url,就是要请求的url,字符串类型的。
resp是*http.Response类型的指针,它标识服务器的响应。err是请求过程中可能发生的错误。

当这个函数调用成功,它会向这个URL发送一个GET请求,如果成功,resp将是服务器的响应。这个响应前面学过理论基础,就是服务器那边发来的信息。包含状态码、响应头以及响应体等信息。如果请求失败err会描述发生的错误。

*http.Response类型解读:这是一个结构体,下面注意介绍里面的字段。
Status:
类型:string
描述:响应的状态行,例如 “200 OK”。这个字段包含了状态码和状态描述。

StatusCode:
类型:int
描述:数字形式的 HTTP 状态码,例如 200、404 等。

Header:
类型:http.Header(实质上是 map[string][]string)
描述:响应头。这是一个映射表,包含了所有的响应头字段和值。每个头字段可以有一个或多个值。

Body:
类型:io.ReadCloser
描述:响应的主体。这是一个 io.Reader 接口,用于读取响应的主体内容。它还实现了 io.Closer 接口,这意味着在读取完毕后,你需要调用 Body.Close() 来关闭它,释放资源。

ContentLength:
类型:int64
描述:响应主体的长度。如果长度未知,该值为 -1。

TransferEncoding:
类型:[]string
描述:传输编码列表,按照应用的顺序。

Close:
类型:bool
描述:指示是否应在读取完响应主体后关闭连接。

Request:
类型:*http.Request
描述:生成这个响应的 HTTP 请求。这对于跟踪请求-响应链很有用。

TLS:
类型:*tls.ConnectionState
描述:如果通过 HTTPS 访问,则包含有关 TLS 连接的信息。如果不是 HTTPS,则为 nil。

通过上述描述,这个resp完全就是服务器的响应的全部内容,我们可以按照需要选择字段进行操作。这里我们一般都去关注body这个字段:响应的主体,就是我们要的主要内容。
关于这个body的数据类型做一点说明:

type ReadCloser interface {
    Reader
    Closer
}

它是一个接口类型,它又嵌套了两个接口,一个接口用于从数据流中读数据,这个接口包含了个read方法,可以实现读取数据到指定字节切片中;另一个用于关闭数据流,它又一个close()方法,调用后关闭数据流。

这里有个注意就是处理完响应主题后,应该关闭resp.Body,这样做的原因和前面学的那些关闭几乎都一个原因,避免资源泄露。

body, err := ioutil.ReadAll(resp.Body)
用于读取响应体的数据,传的参数只要求实现了io.Reader接口就可以传,这样刚好符合。然后返回一个字节切片,这样就做到了把响应体里面的内容读出来。

综上:使用net/http包轻松的做到了向指定的URL发送http get请求,并且得到响应后输出响应的内容。

一个更进阶的用法,使用http.Client可以实现更复杂的HTTP客户端逻辑,例如设置请求头、发送POST请求等。

看例子,还是客户端

package main

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

func main() {
	url := "http://www.example.com/login"
	data := []byte(`{"username": "admin", "password": "password"}`)

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
	if err != nil {
		fmt.Println(err)
		return
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(string(body))
}

解读
url := “http://www.example.com/login”
data := []byte({"username": "admin", "password": "password"})
url指向登录接口,下面这个json格式标识的用户名和密码。

req, err := http.NewRequest(“POST”, url, bytes.NewBuffer(data)) 这个函数用于创建一个新的HTTP请求。这个函数的作用是构造一个指定HTTP方法、URL和可选正文的HTTP请求。

结构

func NewRequest(method, url string, body io.Reader) (*http.Request, error)

参数:方法;指定url;body是请求的正文,如果请求不需要正文,可以为nil。正文是在HTTP请求和响应中,传输的主要数据部分,就是头部的下面那部分,即实际要收发的数据内容。这个正文通常也是有格式的:文本(纯文本,json,xml),表单数据,二进制数据(文件)
在上面这个代码中,我请求的正文就是一个json格式的字节切片,这里由于是要io.Reader所以这里用了一个bytes.NewBuffer(data)创建了一个io.Reader对象,从而可以作为参数传入。

返回值:*http.Request标识构造的HTTP请求,可以说是请求的要素全部齐全了,包含了方法,URL,头部,正文等信息。

总结 这个函数起到了定制请求的效果,满足了对灵活性的需求,通过这个函数我可以构造几乎任何类型的HTTP请求。

req.Header.Set(“Content-Type”, “application/json”) 设置请求的请求正文的内容格式

client := &http.Client{}
resp, err := client.Do(req)
这里是创建一个实例来发送求情,使用client结构体的Do()方法,来发送这个创建的请求,并接收响应。这个resp就是接收的响应。

最后要关闭响应体。defer resp.Body.Close()

拿到的这个响应体,可以用
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
打印出来,因为resp.Body是io.Reader类型的。

http服务器

下面是一个简单的服务器锂离子

package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, World!")
	})

	http.ListenAndServe(":8080", nil)
}

解读:
*http.HandleFunc(“/”, func(w http.ResponseWriter, r http.Request) {
fmt.Fprintf(w, “Hello, World!”)
})

注册处理函数:这个函数用于注册一个处理函数,该函数用于处理对特定路径的HTTP请求。
这个代码中,这个函数被用来处理所有发往根路径"/"的请求。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

pattern,字符串类型,代表指定要处理的URL路径,handler一个函数,当接收到匹配这个路径的请求时,这个函数被调用。

*func(w http.ResponseWriter, r http.Request) 匿名函数,w http.ResponseWriter用于构建和发送HTTP响应,你可以通过w写入响应正文、设置响应的状态码、添加响应头等。
*r http.Request 代表接收到的HTTP请求。它包含了请求的各自信息,比如请求方法,URL,请求头,请求正文等。

综上:也就是服务器接收到了请求,就会输出响应w和hello world

http.ListenAndServe(“:8080”, nil) 用于启动HTTP服务器,第一个参数代表监听本地的8080端口,第二个参数是处理器,nil表示使用默认多路复用器,http.DefaultServeMux。

调用之后会起到HTTP服务器,并监听端口,等待并处理HTTP请求。

http.ServeFile可以轻松地将静态文件提高给客户端。
package main

import (
	"net/http"
)

func main() {
	http.Handle("/", http.FileServer(http.Dir("static")))
	http.ListenAndServe(":8080", nil)
}

解读:
会把当前目录下的static文件夹作为根目录,提供静态文件服务,例如:客户端请求http://localhost:8080/index.html时,服务器返回的是static/index.html文件。

总结:

以上是http客户端和服务器的基本用法,这个包还有很多其他的功能,比如实现了WebSocket,HTTP长连接,Http代理等等。根据需要查询文档。


http请求方法

看day1

http框架

1.Gin:Gin是一个高性能、易用的HTTP框架,它提供了路由、中间件、静态文件服务、模板渲染等常用功能,并且支持自定义中间件和路由分组等高级特性。Gin的设计理念是尽量简单、快速地完成HTTP请求处理,并且提供高度可定制的API接口。

2.ECHO

3.Beego:Beego是一个完整的Web框架,它提供了MVC架构、ORM、Websocket、RESTful API等功能,并且支持国际化、日志、缓存等高级特性。Beego的设计理念是快速开发、易用可扩展,并且提供丰富的文档和社区支持。

4.Revel
还有很多框架,这里做了解,1是必须要学的,Beego看有没有多于的时间学。

你可能感兴趣的:(golang,网络,开发语言)