1. 概要#
ネットワークプログラミングについては非常に広範な分野であり、この記事では Golang のネットワークパッケージを使用して通信を行う方法を簡単に示します。
- tcp ソケットプログラミング(ネットワークプログラミングの主流で、基盤は tcp/ip)
- b/s 構造の http プログラミング(ブラウザでウェブサイトにアクセスする際に使用されるのが http プロトコルであり、http の基盤も tcp ソケットで実現されています)
tcp ソケットプログラミング:
- サーバー処理の流れ:
- ポートをリッスン
- クライアントの tcp リクエストを受け入れ、クライアントとの接続を確立
- goroutine を作成し、その接続のリクエストを処理
- クライアント処理の流れ:
- サーバーとの接続を確立
- リクエストデータを送信し、サーバーからの結果を受け取る
- 接続を閉じる
2. インターネットの階層モデル#
インターネットの論理実装は数層に分かれており、それぞれの層には自分が担当する内容があります。まるで建物のように、各層は下層の支えを必要とします。私たちが普段接触するのは主に最上部の層であり、それは何層にもわたってカプセル化され、最も友好的な方法で私たちの前に提示されます。
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 クエリ、およびいくつかのネットワークパッケージについても説明しました。