xfeng

xfeng

Sporting | Reading | Technology | Recording
github
bilibili

Golangネットワークプログラミングの基礎

image

1. 概要#

ネットワークプログラミングについては非常に広範な分野であり、この記事では Golang のネットワークパッケージを使用して通信を行う方法を簡単に示します。

  • tcp ソケットプログラミング(ネットワークプログラミングの主流で、基盤は tcp/ip)
  • b/s 構造の http プログラミング(ブラウザでウェブサイトにアクセスする際に使用されるのが http プロトコルであり、http の基盤も tcp ソケットで実現されています)

tcp ソケットプログラミング:

  • サーバー処理の流れ:
    • ポートをリッスン
    • クライアントの tcp リクエストを受け入れ、クライアントとの接続を確立
    • goroutine を作成し、その接続のリクエストを処理
  • クライアント処理の流れ:
    • サーバーとの接続を確立
    • リクエストデータを送信し、サーバーからの結果を受け取る
    • 接続を閉じる

2. インターネットの階層モデル#

インターネットの論理実装は数層に分かれており、それぞれの層には自分が担当する内容があります。まるで建物のように、各層は下層の支えを必要とします。私たちが普段接触するのは主に最上部の層であり、それは何層にもわたってカプセル化され、最も友好的な方法で私たちの前に提示されます。

image

image

3. いくつかのネットワークコアパッケージ#

3.1 net#

net.Dial () メソッドはクライアントがネットワークに接続するために使用されます

net.Listen () メソッドはサーバーがネットワーク接続を受け入れるために使用されます

3.2 net/http#

net/http は強力な Web サーバーとクライアントを開発するための関数機能を提供します

http.Get () および https.Get () メソッドはクライアントが HTTP および HTTPS リクエストを送信するために使用できます

http.ListenAndServe () メソッドは Web サーバーを作成し、サーバーがリッスンする IP アドレスと TCP ポート番号を指定し、そのメソッド内で受信リクエストを処理します

3.3 http.RoundTripper#

RoundTripper は http.Client のミドルウェアと見なすことができます

使用シーン:

  • http レスポンスのキャッシュ(キャッシュが存在する場合は直接キャッシュから取得)
  • 適切な HTTP ヘッダーの設定
  • レート制限

参考リンク

4. Go による DNS クエリの実装#

DNS(ドメインネームシステム)の役割は IP アドレスをドメイン名に変換すること、またはドメイン名を IP アドレスに変換することです

package main
import (
	"fmt"
	"net"
	"os"
)
func main() {
	arguments := os.Args
	if len(arguments) == 1 {
		fmt.Println("引数を提供してください!")
		return
	}
	input := arguments[1]
	IPaddress := net.ParseIP(input)  //関数net.ParseIPは渡された文字列をIPアドレスに解析します
	if IPaddress == nil {
		IPs, err := lookHostname(input)
		if err == nil {
			for _, singleIP := range IPs {
				fmt.Println(singleIP)
			}
		}
	} else {
		hosts, err := lookIP(input)
		if err == nil {
			for _, hostname := range hosts {
				fmt.Println(hostname)
			}
		}
	}
}
func lookIP(address string) ([]string, error) {
	hosts, err := net.LookupAddr(address) //渡されたIPアドレスに一致するホストのリストを返します(/etc/hosts)
	if err != nil {
		return nil, err
	}
	return hosts, nil
}
func lookHostname(hostname string) ([]string, error) {
	IPs, err := net.LookupHost(hostname) //渡されたホスト名に一致するIPアドレスのリストを返します
	if err != nil {
		return nil, err
	}
	return IPs, nil
}

5. Go による Web 機能の実装#

Go の標準ライブラリ関数を使用して Web サーバーを実装できます

本当に強力な Web サーバーが必要な場合は、apache や nginx のような Web サーバーを使用することをお勧めします

5.1 Web サーバー#

package main
import (
	"fmt"
	"net/http"
	"os"
	"time"
)
func main() {
	PORT := ":8001"
	arguments := os.Args
	if len(arguments) == 1 {
		fmt.Println("デフォルトのポート番号を使用します: ", PORT)
	}else{
		PORT = ":" + arguments[1]
	}
	http.HandleFunc("/time", timeHandler) //URLルートと処理関数を関連付けます
	http.HandleFunc("/", myHandler)
	err := http.ListenAndServe(PORT, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
}
func myHandler(w http.ResponseWriter, r *http.Request) { 
	fmt.Fprintf(w, "提供中: %s\n", r.URL.Path)
	fmt.Printf("提供された: %s\n", r.Host)
}
func timeHandler(w http.ResponseWriter, r *http.Request) { 
	t := time.Now().Format(time.RFC1123)
	Body := "現在の時刻は:" //動的出力
	fmt.Fprintf(w, "<h1 align=\"center\">%s</h1>", Body)
	fmt.Fprintf(w, "<h2 align=\"center\">%s</h2>\n", t)
	fmt.Fprintf(w, "提供中: %s\n", r.URL.Path)
	fmt.Fprintf(w, "提供された時刻: %s\n", r.Host)
}

5.2 Web クライアント#

package main
import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)
func main() {
	if len(os.Args) != 2 {
		fmt.Printf("使用法: %s URL\n", filepath.Base(os.Args[0]))
		return
	}
	URL, err := url.Parse(os.Args[1]) //渡されたURLを解析します
	if err != nil {
		fmt.Println("解析エラー:", err)
		return
	}
	c := &http.Client{
		Timeout: 15 * time.Second,
	}
	request, err := http.NewRequest("GET", URL.String(), nil)
	if err != nil {
		fmt.Println("取得エラー:", err)
		return
	}
	httpData, err := c.Do(request)
	if err != nil {
		fmt.Println("Do()エラー:", err)
		return
	}
	fmt.Println("ステータスコード:", httpData.Status)
	header, _ := httputil.DumpResponse(httpData, false)
	fmt.Println(string(header))
	contentType := httpData.Header.Get("Content-Type")
	characterSet := strings.SplitAfter(contentType, "charset=")
	if len(characterSet) > 1 {
		fmt.Println("文字セット:", characterSet[1])
	}
	if httpData.ContentLength == -1 {
		fmt.Println("ContentLengthは不明です!")
	} else {
		fmt.Println("ContentLength:", httpData.ContentLength)
	}
	length := 0
	var buffer [1024]byte
	r := httpData.Body
	for {
		n, err := r.Read(buffer[0:])
		if err != nil {
			fmt.Println(err)
			break
		}
		length = length + n
	}
	fmt.Println("計算されたレスポンスデータの長さ:", length)
}

6. Go による TCP 機能の実装#

6.1 TCP サーバー#

package main
import(
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)
func main() {
	arguments := os.Args
	if len(arguments) == 1 {
		fmt.Println("ポート番号を提供してください")
		return
	}
	PORT := ":" + arguments[1]
	l, err := net.Listen("tcp" , PORT)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer l.Close()
	c, err := l.Accept()
	if err != nil {
		fmt.Println(err)
		return
	}
	for {
		netData, err := bufio.NewReader(c).ReadString('\n')
		if err != nil {
			fmt.Println(err)
			return
		}
		if strings.TrimSpace(string(netData)) == "STOP" {
			fmt.Println("TCPサーバーを終了します!")
			return
		}
		fmt.Print("-> ", string(netData))
		t := time.Now()
		myTime := t.Format(time.RFC3339) + "\n"
		c.Write([]byte(myTime))
	}
}

6.2 TCP クライアント#

package main
import (
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
)
func main(){
	arguments := os.Args
	if len(arguments) == 1 {
		fmt.Println("host:portを提供してください。")
		return
	}
	CONNECT := arguments[1]
	c, err := net.Dial("tcp", CONNECT)
	if err != nil {
		fmt.Println(err)
		return
	}
	for {
		reader := bufio.NewReader(os.Stdin)
		fmt.Print(">> ")
		text, _ := reader.ReadString('\n')
		fmt.Fprintf(c, text + "\n")
		message, _ := bufio.NewReader(c).ReadString('\n')
		fmt.Print("->: " + message)
		if strings.TrimSpace(string(text)) == "STOP" {
			fmt.Println("TCPクライアントを終了します...")
			return
		}
	}
}

7. Go による UDP 機能の実装#

7.1 UDP サーバー#

package main

import (
	"fmt"
	"net"
)

func main() {
	// リスナーを作成
	socket, err := net.ListenUDP("udp4", &net.UDPAddr{
		IP:   net.IPv4(0, 0, 0, 0),
		Port: 8080,
	})
	if err != nil {
		fmt.Println("リスニングに失敗しました!", err)
		return
	}
	defer socket.Close()

	for {
		// データをループして読み取る
		data := make([]byte, 4096)
		read, remoteAddr, err := socket.ReadFromUDP(data)
		if err != nil {
			fmt.Println("データの読み取りに失敗しました!", err)
			continue
		}
		fmt.Println(read, remoteAddr)
		fmt.Printf("%s\n\n", data)

		// データを送信
		senddata := []byte("hello client!")
		_, err = socket.WriteToUDP(senddata, remoteAddr)
		if err != nil {
			return
			fmt.Println("データの送信に失敗しました!", err)
		}
	}
}

7.2 UDP クライアント#

package main

import (
	"fmt"
	"net"
)

func main() {
	// 接続を作成
	socket, err := net.DialUDP("udp4", nil, &net.UDPAddr{
		IP:   net.IPv4(192, 168, 110, 110),
		Port: 8080,
	})
	if err != nil {
		fmt.Println("接続に失敗しました!", err)
		return
	}
	defer socket.Close()

	// データを送信
	senddata := []byte("hello server!")
	_, err = socket.Write(senddata)
	if err != nil {
		fmt.Println("データの送信に失敗しました!", err)
		return
	}

	// データを受信
	data := make([]byte, 4096)
	read, remoteAddr, err := socket.ReadFromUDP(data)
	if err != nil {
		fmt.Println("データの読み取りに失敗しました!", err)
		return
	}
	fmt.Println(read, remoteAddr)
	fmt.Printf("%s\n", data)
}

8. まとめ#

この記事では、Go バージョンの Web サーバー、クライアント、TCP サーバー、クライアント、UDP サーバー、クライアントを主に紹介し、ネットワークの階層の概念、Go バージョンの DNS クエリ、およびいくつかのネットワークパッケージについても説明しました。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。