Help us understand the problem. What is going on with this article?

GoでTCPとUDPのデータの扱い方を比較してみた

More than 1 year has passed since last update.

この記事は 東京理科大学 Advent Calendar 2018 6日目の記事です。

はじめに

今回Goのnetパッケージを使ってTCPソケット通信を扱う機会があったので、同じnetパッケージで扱うことのできるUDPソケット通信と比較してみました。

比較方法

クライアント側からサーバ側にhelloとたくさん送り、そのまま出力した時の両者の違いをみます。

TCPソケット通信

TCPによる通信ではデータをバイトストリームと呼ばれるbyte型のひと続きのデータとして扱います。

・クライアント

クライアントのコードは以下です。

tcp_client.go
package main

import (
    "log"
    "net"
)

func main() {
    conn, err := net.Dial("tcp", "localhost:8088")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    // メッセージを送信する
    for {
        msg := "hello"
        conn.Write([]byte(msg))
    }
}

・サーバ

サーバのコードは以下です。

tcp_server.go
package main

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

func main() {
    listener, err := net.Listen("tcp", "localhost:8088")
    if err != nil {
        log.Fatal(err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Print(err)
            continue
        }
        defer conn.Close()

        go handleconn(conn)
    }
}

func handleconn(conn net.Conn) {
    buf := make([]byte, 1024) // 1024バイトのスライスを用意
    // 送られてきたメッセージを出力
    for {
        n, _ := conn.Read(buf)
        fmt.Println(string(buf[:n]))
    }
}

実行結果

zsh
$ go run tcp_server.go
hellohellohellohellohellohellohellohellohello
hellohellohellohellohellohello
hello
hellohellohellohello
hellohello
hello
hello
hello
...

UDPソケット通信

UDPによる通信ではデータを独立したパケットのデータグラムとして扱います。

・クライアント

クライアントのコードは以下です。

udp_client.go
package main

import (
    "log"
    "net"
)

func main() {
    conn, err := net.Dial("udp", "localhost:8088")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    // メッセージを送信する
    for {
        msg := "hello"
        conn.Write([]byte(msg))
    }
}

・サーバ

サーバのコードは以下です。

udp_server.go
package main

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

func main() {
    udpAddr, err := net.ResolveUDPAddr("udp", ":8088")
    if err != nil {
        log.Fatal(err)
    }
    conn, err := net.ListenUDP("udp", udpAddr)
    if err != nil {
        log.Fatal(err)
    }
    for {
        defer conn.Close()

        go handleconn(conn)
    }
}

func handleconn(conn *net.UDPConn) {
    buf := make([]byte, 1024) // 1024バイトのスライス
    // 送られてきたメッセージを出力
    for {
        n, _ := conn.Read(buf)
        fmt.Println(string(buf[:n]))
    }
}

実行結果

zsh
$ go run udp_server.go
hello
hello
hello
hello
hello
hello
hello
hello
...

まとめ

以上の結果から、TCPではデータをストリームとして扱っているので何も処理をせずにそのまま出力した場合はhelloという文字列が連結されて出力される場合があることがわかった。
また、UDPではデータをパケットごとにデータグラムとして扱っているためそのまま出力してもhelloという文字列が区切られて出力されることがわかった。

蛇足

UDPと同じ出力結果になるようにTCPで処理をしようとした場合、
データの末尾に\nなどの区切り文字を用意する
データのヘッダにデータのバイト数をつけて送信する
ことで処理ができます。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away