Go net 网络模块

发布时间: 更新时间: 总字数:1967 阅读时间:4m 作者: IP上海 分享 网址

Golang 提供 net 库用来处理网络相关协议

基础示例

原生 net 实现

package main

import (
	"fmt"
	"net"
)

func main() {
	host := "0.0.0.0"
	port := "8888"
	addr := net.JoinHostPort(host, port)
	fmt.Println(addr)                    // 0.0.0.0:8888
	fmt.Println(net.SplitHostPort(addr)) // 0.0.0.0 8888 <nil>

	// 获取本地所有 IP 信息
	fmt.Println("Interfaces:")
	ifaces, err := net.Interfaces()
	for _, iface := range ifaces {
		fmt.Println(iface.Index, iface.Name, iface.MTU, iface.Flags, iface.HardwareAddr)
		fmt.Println(iface.Addrs())
		fmt.Println(iface.MulticastAddrs())
	}

	fmt.Println("InterfaceAddrs:")
	addrs, err := net.InterfaceAddrs()
	for _, addr := range addrs {
		fmt.Println(addr.String(), addr.Network())
	}

	host = "www.xiexianbin.cn"
	// 根据域名查找 IP
	var ips []net.IP
	ips, err = net.LookupIP(host)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(ips)

	fmt.Println(net.LookupHost(host))

	// 解析 txt 记录
	mxs, err := net.LookupMX("xiexianbin.cn")
	if err != nil {
		fmt.Println(err)
	}
	for _, mx := range mxs {
		//&net.MX{Host:"mxn.mxhichina.com.", Pref:0x5}
		//&net.MX{Host:"mxw.mxhichina.com.", Pref:0xa}
		fmt.Printf("%#v\n", mx)
	}
	txts, err := net.LookupTXT("_dnsauth.ca.xiexianbin.cn ")
	if err != nil {
		fmt.Println(err)
	}
	for _, txt := range txts {
		fmt.Printf("%#v\n", txt)
	}

	// cidr
	ip, ipnet, err := net.ParseCIDR("172.20.0.1/24")
	fmt.Println(ip, ipnet, err)                             // 172.20.0.1 172.20.0.0/24 <nil>
	fmt.Println(ipnet.Contains(net.ParseIP("172.20.0.10"))) // true
	fmt.Println(ipnet.Contains(net.ParseIP("172.20.1.1")))  // false
	fmt.Println(ipnet.Network())                            // ip+net

	for _, s := range []string{"172.20.0.1", "::1", "foo.ip"} {
		ip := net.ParseIP(s)
		fmt.Println(ip) // [172.20.0.1 ::1 <nil>]
		if ip != nil {
			fmt.Println(ip.IsLinkLocalMulticast())
			fmt.Println(ip.IsLoopback())
			fmt.Println(ip.IsGlobalUnicast())
			fmt.Println(ip.IsPrivate())
		}
	}

	// 根据 IP 查找域名
	names, err := net.LookupAddr(ips[0].String())
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(names)
}

服务端

package main

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"strconv"
	"strings"
	"time"
)

func response1(conn net.Conn) {
	defer conn.Close()

	n, err := conn.Write([]byte(time.Now().Format("2006-01-02 15:04:06")))
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println("sent to client", conn.RemoteAddr(), n, "bytes.")
}

func response2(conn net.Conn) {
	defer conn.Close()
	var err error
	var n int
	bs := make([]byte, 1024)

	for {
		n, err = conn.Read(bs)
		if err != nil {
			fmt.Println()
			return
		}
		req := strings.Join([]string{"receive from", conn.RemoteAddr().String(), "data is", string(bs[:n]), ",len is", strconv.Itoa(n), "bytes."}, " ")
		fmt.Println(req)

		resp := strings.Join([]string{time.Now().Format("2006-01-02 15:04:06"), req}, " ")
		n, err = conn.Write([]byte(resp))
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		fmt.Println("sent to client", conn.RemoteAddr(), n, "bytes.")
	}
}

func response3(conn net.Conn) {
	defer conn.Close()
	defer func() {
		fmt.Println("conn", conn.RemoteAddr(), "is closed.")
	}()

	reader := bufio.NewReader(conn)
	writer := bufio.NewWriter(conn)

	for {
		_, err := writer.WriteString(fmt.Sprintf("client from %s\n", conn.RemoteAddr().String()))
		if err != nil {
			fmt.Println(err.Error())
		}
		_ = writer.Flush()

		str, err := reader.ReadString('\n')
		if err != nil {
			fmt.Println(err.Error())
			if err == io.EOF {
				break
			}
			return
		}
		fmt.Printf("receive from %s, data is %s.\n", conn.RemoteAddr(), str)
	}

}

func main() {
	addr := "0.0.0.0:8888" // ":8888"
	var err error

	listen, err := net.Listen("tcp", addr)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer listen.Close()
	fmt.Println("listen on", addr)

	for {
		// 等待客户端连接
		conn, err := listen.Accept()
		if err != nil {
			return
		}

		//go response1(conn)
		//go response2(conn)
		go response3(conn)
	}
}

客户端

package main

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

func main() {
	var err error
	addr := "127.0.0.1:8888" // ":8888"
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(-1)
	}
	defer conn.Close()

	var send string

	reader := bufio.NewReader(conn)
	writer := bufio.NewWriter((conn))

	//for {
	//	fmt.Print("please input which to send:")
	//	_, _ = fmt.Scan(&send)
	//	n, err = conn.Write([]byte(send + "\n"))
	//	if err != nil {
	//		fmt.Println(err.Error())
	//		return
	//	}
	//	fmt.Println("success send", n, "bytes to", conn.RemoteAddr())
	//
	//	bs := make([]byte, 1024)
	//	n, err := conn.Read(bs)
	//	if err != nil {
	//		fmt.Println(err.Error())
	//		return
	//	}
	//	fmt.Println("success receive:", string(bs[:n]))
	//}

	for {
		str, _ := reader.ReadString('\n')
		fmt.Println("success receive:", str)

		fmt.Print("please input which to send(q:exit):")
		// 从命令行读取数据
		// 方法一
		//_, _ = fmt.Scan(&send)
		// 方法二:只读一行数据,否则要使用 for
		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		send = scanner.Text()

		if send == "q" {
			break
		}
		_, _ = writer.WriteString(fmt.Sprintf("%s\n", send))
		_ = writer.Flush()
		fmt.Println("success send to", conn.RemoteAddr())
	}
}

httpclient

package main

import (
	"bufio"
	"fmt"
	"io"
	"net"
)

func main() {
	addr := "www.baidu.com:80"
	//addr := "www.xiexianbin.cn:80"
	//addr := "127.0.0.1:8888"
	dial, err := net.Dial("tcp", addr)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println("connect to", dial.RemoteAddr())
	reqStr := fmt.Sprintf("GET /index.html HTTP/1.1\r\n:authority: www.baidu.com\r\n:method: GET\r\n:path: /index.html\r\naccept: text/html,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nuser-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36\r\n\r\n")
	fmt.Fprintf(dial, reqStr)

	//bs := make([]byte, 1024)
	//for {
	//	n, err := dial.Read(bs)
	//	if err != nil {
	//		if err == io.EOF {
	//			break
	//		}
	//		fmt.Println(err.Error())
	//		return
	//	}
	//	fmt.Print(string(bs[:n]))
	reader := bufio.NewReader(dial)
	for {
		str, err := reader.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			fmt.Println(err)
			return
		}
		fmt.Print(str)
	}
}

跳过 SSL 证书认证

package main

import (
    "fmt"
    "net/http"
    "crypto/tls"
)

func main() {
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := &http.Client{Transport: tr}
    _, err := client.Get("https://golang.org/")
    if err != nil {
        fmt.Println(err)
    }
}

netClient 公共方法

// WebhookClient exists to mock the client in tests.
type WebhookClient interface {
	Do(req *http.Request) (*http.Response, error)
}

var netTransport = &http.Transport{
	TLSClientConfig: &tls.Config{
		Renegotiation: tls.RenegotiateFreelyAsClient,
	},
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 5 * time.Second,
}
var netClient WebhookClient = &http.Client{
	Timeout:   time.Second * 30,
	Transport: netTransport,
}

// 其他方法内使用
func x() {
	request, err := http.NewRequestWithContext(ctx, "Get", "URL", bytes.NewReader([]byte("{}")))
	resp, err := netClient.Do(request)
	...
}

Web 示例

基于 net/http 实现

httpserver

纯原生实现 Http Server:go-httpserver

package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"time"
)

// Home Page, 实现 handler
func home(w http.ResponseWriter, r *http.Request) {
	//n, err := resp.Write([]byte("home page"))
	n, err := io.WriteString(w, "home page")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println("send to client", n, "bytes.")
}

// Post 处理器,实现 http.Handler interface
type Post struct{}

func (p Post) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, time.Now().Format("2006-01-02 15:04:05"))
}

type Info struct{}

func (info Info) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("%#v\n", r.URL)

	fmt.Println(r.RemoteAddr)

	w.WriteHeader(200)
	_, _ = io.WriteString(w, fmt.Sprintf("%s %s %s\r\n", r.Method, r.RequestURI, r.Proto))

	for k, v := range r.Header {
		_, _ = io.WriteString(w, fmt.Sprintf("%s: %s\r\n", k, v))
	}

	io.WriteString(w, "\r\n")

	fmt.Println("req body is:")
	//bs := make([]byte, 1024)
	//for {
	//	n, err := r.Body.Read(bs)
	//	if n != 0 {
	//		fmt.Print(string(bs[:n]))
	//		_, _ = w.Write(bs[:n])
	//	}
	//	if err != nil {
	//		if err != io.EOF {
	//			fmt.Println(err.Error())
	//		}
	//		break
	//	}
	//}
	//io.Copy(os.Stdout, r.Body)
	//io.Copy(w, r.Body)
	buf := bytes.NewBuffer(nil)
	io.Copy(buf, r.Body)
	fmt.Println(buf.String())
	io.Copy(w, buf)
}

func main() {
	addr := "0.0.0.0:8000"

	// 面向过程实现
	http.HandleFunc("/", home)

	// 面向对象实现
	//http.Handle("/post/", Post{})
	http.Handle("/post/", &Post{})

	// curl -XPOST http://0.0.0.0:8000/info -d "hello world"
	http.Handle("/info", &Info{})

	fmt.Println("start server, listen on", addr)
	log.Fatal(http.ListenAndServe(addr, nil))
}

fileserver

package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	addr := "0.0.0.0:8000"

	// http.Dir(".") 为类型转换
	http.Handle("/", http.FileServer(http.Dir(".")))

	fmt.Println("start server, listen on", addr)
	log.Fatal(http.ListenAndServe(addr, nil))
}

httpclient

package main

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
)

func testGet() {
	url := "https://www.xiexianbin.cn"

	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
		return
	}

	buf := bytes.NewBuffer(nil)

	buf.WriteString(fmt.Sprintf("%s %s\r\n", resp.Proto, resp.Status))
	for k, v := range resp.Header {
		buf.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
	}
	buf.WriteString("\r\n")
	io.Copy(buf, resp.Body)

	fmt.Println(buf.String())
}

// self sign ca
func testGet2() {
	u := "https://s1.dddd.dev"
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		fmt.Println(err)
		return
	}

	transport := http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}

	client := &http.Client{Transport: &transport}
	//client.Get()
	//client.Post()
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(resp.StatusCode)
	io.Copy(os.Stdout, resp.Body)
}

func testPost() {
	// curl -v -XPOST "https://api.github.com/gists" -H "content-type: application/json" -H "Accept: application/vnd.github.v3+json" -d "{}"
	url := "https://api.github.com/gists"
	body := bytes.NewReader([]byte("{}"))
	resp, err := http.Post(url, "application/json", body)
	if err != nil {
		return
	}

	buf := bytes.NewBuffer(nil)

	buf.WriteString(fmt.Sprintf("%s %s\r\n", resp.Proto, resp.Status))
	for k, v := range resp.Header {
		buf.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
	}
	buf.WriteString("\r\n")
	io.Copy(buf, resp.Body)

	fmt.Println(buf.String())
}

func testPostForm() {
	// curl -v -XPOST "https://api.github.com/gists" -H "content-type: application/json" -H "Accept: application/vnd.github.v3+json" -d "{}"
	u := "https://api.github.com/gists"
	//var data url.Values = url.Values{}
	data := make(url.Values)
	data.Add("username", "xianbin")
	data.Add("password", "123456")
	resp, err := http.PostForm(u, data)
	if err != nil {
		return
	}

	buf := bytes.NewBuffer(nil)

	buf.WriteString(fmt.Sprintf("%s %s\r\n", resp.Proto, resp.Status))
	for k, v := range resp.Header {
		buf.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
	}
	buf.WriteString("\r\n")
	io.Copy(buf, resp.Body)

	fmt.Println(buf.String())
}

func testDelete() {
	u := "https://api.github.com/gists"
	req, err := http.NewRequest("DELETE", u, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	client := &http.Client{Transport: http.DefaultTransport}
	//client.Get()
	//client.Post()
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(resp.StatusCode, resp.Body)
}

func main() {
	testGet()
	testGet2()
	testPost()
	testPostForm()
	testDelete()
}
package main

import (
	"bytes"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"time"
)

func main() {
	call("http://localhost:8080/employee", "POST")
}
func call(urlPath, method string) error {
	client := &http.Client{
		Timeout: time.Second * 10,
	}
	// New multipart writer.
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	fw, err := writer.CreateFormField("name")
	if err != nil {
	}
	_, err = io.Copy(fw, strings.NewReader("John"))
	if err != nil {
		return err
	}
	fw, err = writer.CreateFormField("age")
	if err != nil {
	}
	_, err = io.Copy(fw, strings.NewReader("23"))
	if err != nil {
		return err
	}
	fw, err = writer.CreateFormFile("photo", "test.png")
	if err != nil {
	}
	file, err := os.Open("test.png")
	if err != nil {
		panic(err)
	}
	_, err = io.Copy(fw, file)
	if err != nil {
		return err
	}
	// Close multipart writer.
	writer.Close()
	req, err := http.NewRequest("POST", "http://localhost:8080/employee", bytes.NewReader(body.Bytes()))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	rsp, _ := client.Do(req)
	if rsp.StatusCode != http.StatusOK {
		log.Printf("Request failed with response code: %d", rsp.StatusCode)
	}
	return nil
}

multipart/formdata client

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
)

var (
	filePath string
	addr     string
)

func init() {
	flag.StringVar(&filePath, "file", "", "the file to upload")
	flag.StringVar(&addr, "addr", "localhost:8080", "the addr of file server")
	flag.Parse()
}

func main() {
	if filePath == "" {
		fmt.Println("file must not be empty")
		return
	}

	err := doUpload(addr, filePath)
	if err != nil {
		fmt.Printf("upload file [%s] error: %s", filePath, err)
		return
	}
	fmt.Printf("upload file [%s] ok\n", filePath)
}

func createReqBody(filePath string) (string, io.Reader, error) {
	var err error

	buf := new(bytes.Buffer)
	bw := multipart.NewWriter(buf) // body writer

	f, err := os.Open(filePath)
	if err != nil {
		return "", nil, err
	}
	defer f.Close()

	// text part1
	p1w, _ := bw.CreateFormField("name")
	p1w.Write([]byte("Tony Bai"))

	// text part2
	p2w, _ := bw.CreateFormField("age")
	p2w.Write([]byte("15"))

	// file part1
	_, fileName := filepath.Split(filePath)
	fw1, _ := bw.CreateFormFile("file1", fileName)
	io.Copy(fw1, f)

	bw.Close() //write the tail boundry
	return bw.FormDataContentType(), buf, nil
}

func doUpload(addr, filePath string) error {
	// create body
	contType, reader, err := createReqBody(filePath)
	if err != nil {
		return err
	}

	url := fmt.Sprintf("http://%s/upload", addr)
	req, err := http.NewRequest("POST", url, reader)

	// add headers
	req.Header.Add("Content-Type", contType)

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

F&Q

301 response missing Location header

源码在 net/http/client.go,可以高版本 go(如1.9.x)时就不在验证 301 中的 Location header

Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数