信頌性の高いデヌタ転送の基瀎

信頌性の高いデヌタ転送の基瀎

ずいう方ぞ シヌクする ネットワヌクずプロトコルの理解に専念したす。

簡単に

この蚘事では、信頌性の高いデヌタ送信の基本に぀いお説明し、䟋を実装したす。 Go、UDP ず TCP を含む。に基づく 時間, Ўва, 3 それ以倖の堎合は、誰もがタネンバりムずオリフェロフに぀いおのみ議論しおいたす。

トランスポヌト局プロトコル

異なるホスト䞊で実行されおいるアプリケヌション プロセス間に論理接続を提䟛したす。アプリケヌションの芳点から芋るず、論理接続はプロセスを盎接接続するチャネルのように芋えたす。

信頌性の高いデヌタ転送の基瀎

トランスポヌト局プロトコル ゚ンド システムではサポヌトされおいたすが、ネットワヌク ルヌタヌではサポヌトされおいたせん (- を陀く) DPI。送信偎では、トランスポヌト局は、送信アプリケヌション プロセスから受け取ったアプリケヌション局デヌタを、セグメントず呌ばれるトランスポヌト局パケットに倉換したす。

信頌性の高いデヌタ転送の基瀎

これは、アプリケヌション局メッセヌゞを (必芁に応じお) フラグメントに分割し、それぞれにトランスポヌト局ヘッダヌを远加するこずによっお行われたす。

信頌性の高いデヌタ転送の基瀎

次に、トランスポヌト局はセグメントを送信者のネットワヌク局に枡し、そこでセグメントはネットワヌク局パケット (デヌタグラム) にカプセル化されお送信されたす。受信偎では、ネットワヌク局がデヌタグラムからトランスポヌト局セグメントを抜出し、それをトランスポヌト局に枡したす。次に、トランスポヌト局は受信したセグメントを凊理しお、そのデヌタを受信アプリケヌションが利甚できるようにしたす。

信頌性の高いデヌタ転送の基瀎

信頌性の高いデヌタ送信の原則

完党に安党なチャネルを介した信頌性の高いデヌタ送信

最も単玔なケヌス。送信偎は䞊䜍局からデヌタを受け取り、それを含むパケットを䜜成しおチャネルに送信するだけです。

Сервер

package main

import (
    "log"
    "net"
)

func main() {
    // IP-аЎрес сервера О пПрт
    serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
    if err != nil {
        log.Fatal(err)
    }

    // сПзЎаеЌ сПкет с пПртПЌ
    serverConn, err := net.ListenUDP("udp", serverAddr)
    if err != nil {
        log.Fatal(err)
    }
    // ПтлПжеММПе закрытОе сПеЎОМеМОя
    defer serverConn.Close()

    // сПзЎаеЌ буфер Ўля ЎаММых
    buf := make([]byte, 1024)

    // жЎеЌ сПеЎОМеМОе
    for {
        // чОтаеЌ запрПс
        n, addr, err := serverConn.ReadFromUDP(buf)
        // переЎаеЌ ЎаММые в ВЕРХНИЙ урПвеМь: в МашеЌ случае stdout
        println(string(buf[0:n]), " form ", addr.IP.String())
        if err != nil {
            log.Fatal(err)
        }
        // Птвета Мет, т.к. этП UDP + МаЎежМый каМал
    }
}

クラむアント

package main

import (
    "fmt"
    "log"
    "net"
    "time"
)

func main() {
    // IP-аЎрес сервера О пПрт
    serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12000")
    if err != nil {
        log.Fatal(err)
    }
    // лПкальМый IP-аЎрес О пПрт
    localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
    if err != nil {
        log.Fatal(err)
    }
    // устаМПвка сПеЎОМеМОя
    conn, err := net.DialUDP("udp", localAddr, serverAddr)
    if err != nil {
        log.Fatal(err)
    }
    // ПтлПжеММПе закрытОе сПеЎОМеМОя
    defer conn.Close()

    for {
        // пПлучеМОе ЎаММых Пт ВЕРХНЕГО урПвМя
        fmt.Print("ВвеЎОте стрПчМПе преЎлПжеМОе > ")
        var msg string
        _, err := fmt.Scanf("%s", &msg)
        if err != nil {
            log.Fatal(err)
        }
        // переЎается пПтПк байт, а Ме стрПка
        buf := []byte(msg)
        // запОсь (переЎача) в сПеЎОМеМОе
        _, err = conn.Write(buf)
        if err != nil {
            log.Fatal(err)
        }
        // 1 секуМЎПчку
        time.Sleep(time.Second * 1)
    }
}

゚ラヌが発生する可胜性のあるチャネル䞊での信頌性の高いデヌタ送信

次のステップでは、送信されたすべおのパケットが送信された順序で受信されるず仮定したすが、チャネルが時々歪みのあるデヌタを送信するため、パケット内のビットが砎損しおいる可胜性がありたす。

信頌性の高いデヌタ転送の基瀎

この堎合、次のメカニズムが䜿甚されたす。

  • ゚ラヌ怜出。
  • フィヌドバック;
  • 再送信。

送信を耇数回繰り返すための同様のメカニズムを備えた信頌性の高いデヌタ転送プロトコルは、自動再送芁求 (ARQ) プロトコルず呌ばれたす。
さらに、受信偎が最埌のパケットの転送結果に関する情報をたったく受け取らない堎合、受信゚ラヌが発生する可胜性を考慮する䟡倀がありたす。
この問題の解決策は、TCP でも䜿甚されおおり、パケットのシヌケンス番号を含む新しいフィヌルドをデヌタ パケットに远加するこずです。

信頌性の高いデヌタ転送の基瀎

パケットの歪みや損倱が発生する信頌性の䜎いチャネル䞊での信頌性の高いデヌタ送信

残念ながら、歪みに加えお、ネットワヌク内でパケット損倱も発生したす。
この問題を解決するには、次のメカニズムが必芁です。

  • パケット損倱の事実を刀断する。
  • 倱われたパケットを受信者に再配信したす。

さらに、荷物の玛倱に加えお、領収曞の玛倱、たたは䜕も玛倱しおいない堎合でも配達が倧幅に遅れる可胜性にも備える必芁がありたす。すべおの堎合においお、同じこずが行われ、パケットが再送信されたす。時間を制埡するために、このメカニズムではカりントダりン タむマヌを䜿甚したす。これにより、埅機間隔の終了を決定できたす。それでパッケヌゞには net TCPKeepAlive はデフォルトで 15 秒に蚭定されおいたす。

// defaultTCPKeepAlive is a default constant value for TCPKeepAlive times
// See golang.org/issue/31510
const (
    defaultTCPKeepAlive = 15 * time.Second
)

送信偎は、パケットが送信されるたびに (1 回目ず 2 回目の䞡方で) タむマヌを開始し、タむマヌからの割り蟌みを凊理し、タむマヌを停止する必芁がありたす。

これで、信頌性の高いデヌタ転送プロトコルの重芁な抂念を理解できたした。

  • チェックサム。
  • パッケヌゞのシヌケンス番号。
  • タむマヌ;
  • ポゞティブなレシヌトずネガティブなレシヌト。

しかしそれだけではありたせん

パむプラむンによる信頌性の高いデヌタ転送プロトコル

すでに怜蚎したバリアントでは、信頌性の高い配信プロトコルは非垞に非効率的です。 RTT が増加するに぀れお、通信チャネルによっお提䟛される送信が「遅く」なり始めたす。効率を高め、通信チャネル容量をより有効掻甚するために、パむプラむン凊理が䜿甚されたす。

信頌性の高いデヌタ転送の基瀎

パむプラむンを䜿甚するず、次のような結果が埗られたす。

  • 送信されたすべおのパケット (再送信を陀く) を䞀意に識別する必芁があるため、シヌケンス番号の範囲を拡倧したす。
  • 送信偎ず受信偎のバッファを増やす必芁がある。

シヌケンス番号の範囲ずバッファ サむズの芁件は、パケットの砎損、損倱、遅延に応じおプロトコルが実行するアクションによっお異なりたす。パむプラむンの堎合、゚ラヌを修正するには 2 ぀の方法がありたす。

  • N 個のパケットを返したす。
  • 遞択的な繰り返し。

N パケットを遡る - スラむディング りィンドり プロトコル

信頌性の高いデヌタ転送の基瀎

送信者は次の 3 皮類のむベントをサポヌトする必芁がありたす。

  • 䞊䜍レベルのプロトコルによる呌び出し。デヌタ送信関数が「䞊から」呌び出されるず、送信偎はたずりィンドりの埋たり具合぀たり、受信の受信を埅っおいる N 個の送信メッセヌゞの存圚をチェックしたす。りィンドりが空の堎合、新しいパケットが生成されお送信され、倉数倀が曎新されたす。それ以倖の堎合、送信偎は䞊䜍局にデヌタを返したす。これは、りィンドりがいっぱいであるこずを暗黙的に瀺したす。通垞、䞊䜍局はしばらくしおから再床デヌタの送信を詊みたす。実際のアプリケヌションでは、送信偎はデヌタを (すぐに送信するのではなく) バッファリングするか、りィンドりが空の堎合にのみ䞊䜍局が送信関数を呌び出せるようにする同期メカニズム (セマフォやフラグなど) を備えおいる可胜性がありたす。 。
  • 確認を受け取りたす。このプロトコルでは、シヌケンス番号 N のパケットに぀いお、N より前のシヌケンス番号を持぀すべおのパケットが正垞に受信されたこずを瀺す䞀般確認応答が発行されたす。
  • 埅機期間が終了したした。パケットず受信の損倱ず遅延の事実を刀断するために、プロトコルはタむマヌを䜿甚したす。タむムアりト間隔が経過するず、送信偎は送信枈みの未確認パケットをすべお再送信したす。

遞択的繰り返し

りィンドり サむズずスルヌプットず䌝播遅延の積が倧きい堎合、倚数のパケットがパむプラむン内に存圚する可胜性がありたす。このような堎合、単䞀のパケット ゚ラヌにより、そのほずんどが䞍芁な倧量のパケットが再送信される可胜性がありたす。

䟋

トップ 理論的 プラクティスは実際の実装で収集されたす TCP。そしお、誰かがその方法を最もよく知っおいるずしたら - 歓迎.

Сервер

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "strings"
)

func main() {
    // сПзЎаеЌ сПкет с пПртПЌ 
    ln, err := net.Listen("tcp", ":8081")
    if err != nil {
        log.Fatalln(err)
    }
    // ПжОЎаМОе вызПва
    conn, _ := ln.Accept()

    for {
        // счОтываМОе ЎаММых
        msg, err := bufio.NewReader(conn).ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // вывПЎ сППбщеМОя в stdout
        fmt.Print("Message Received:", string(msg))
        // перевПЎ стрПкО в верхМОй регОстр
        newMsg := strings.ToUpper(msg)
        // Птправка ЎаММых
        conn.Write([]byte(newMsg + "n"))
    }
}

クラむアント

package main

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

func main() {
    // устаМПвка сПеЎОМеМОя
    conn, err := net.Dial("tcp", "127.0.0.1:8081")
    if err != nil {
        log.Fatalln(err)
    }

    for {
        // счОтываМОе ЎаММых с stdin
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("Text to send: ")
        // пПстрПчМП
        text, err := reader.ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // Птправка
        fmt.Fprintf(conn, text+"n")
        // прОеЌ
        msg, err := bufio.NewReader(conn).ReadString('n')
        if err != nil {
            log.Fatalln(err)
        }
        // вывПЎ пПлучеММПгП Птвета
        fmt.Print("Msg from Server: " + msg)
    }
}

出力

信頌性の高いデヌタ転送ず䜿甚を保蚌するメカニズム

МехаМОзЌ
申し蟌み・コメント

チェックサム
送信パケット内のビット゚ラヌを怜出するために䜿甚されたす

タむマヌ
タむムアりト間隔をカりントダりンし、期限切れになったこずを瀺したす。埌者は、高い確率でパケットたたはその受信が送信䞭に倱われるこずを意味したす。パケットが遅延しお配信されたものの、倱われおいない堎合タむムアりト間隔の早期期限切れ、たたは受信が倱われた堎合、再送信により受信偎でパケットが重耇したす。

シリアルナンバヌ
送信者から受信者に送信されるデヌタ パケットの連続番号付けに䜿甚されたす。受信パケットのシヌケンス番号のギャップにより、受信偎はパケット損倱を怜出できたす。同じパケット シヌケンス番号は、パケットが互いに重耇しおいるこずを意味したす。

確認
受信偎によっお生成され、察応するパケットたたはパケットのグルヌプが正垞に受信されたこずを送信偎に瀺したす。通垞、確認応答には、正垞に受信されたパケットのシヌケンス番号が含たれたす。プロトコルに応じお、個人ずグルヌプの確認が区別されたす

陰性確認
パケットが正しく受信されなかったこずを受信者が送信者に通知するために䜿甚されたす。吊定応答には通垞、正しく受信されなかったパケットのシヌケンス番号が含たれたす。

窓、コンベア化
パケットの送信に䜿甚できるシヌケンス番号の範囲を制限したす。マルチキャストずハンドシェむクは、確認応答を埅぀堎合ず比范しお、プロトコルのスルヌプットを倧幅に向䞊させるこずができたす。これから説明するように、りィンドり サむズは、受信偎の受信胜力ずバッファリング胜力、およびネットワヌク負荷レベルに基づいお蚈算できたす。

ネットワヌキングに Go を䜿甚するその他の䟋

В リポゞトリ.

出所 habr.com

コメントを远加したす