Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
관리 메뉴

Code Habit

go ) TCP 통신으로 구조체 데이터 읽기 본문

카테고리 없음

go ) TCP 통신으로 구조체 데이터 읽기

코드베어 2020. 9. 2. 16:57

go 로 작성된 서버/클라이언트 끼리 구조체 데이터를 주고 받고 싶으면 "gob"패키지 같은 go에서 제공하는 패키지를 사용하면 편리하다. 그런데 만약 go로 서버를 만들되 클라이언트를 다른언어 ( c ) 로 만들게 되면 위와 같은 패키지는 사용할 수 없게 된다. 이렇게 되면 문제가 하나 발생하는데 go에서는 포인터 개념도 없고, 문자열을 표현하는 string 자료형이 가변이라 약속된 크기의 데이터를 주고 받기가 꽤 까다롭다는 것이다.

 

그래서 필자는 아래와 같은 두가지 방법을 생각해 보았다.

 

    1. 사이즈 전송 후 구조체 데이터를 json데이터로 전송

    2. 사이즈 전송 후 구조체 데이터의 각 필드를 고정된 크기의 []byte형으로 전송

 

네트워크 통신에서 (특히 http 통신)에서 많이 사용하는 json 데이터를 파싱하는 라이브러들은 많으나 어차피 미리 약속된 데이터와 사이즈를 정해놓고 통신하는 tcp 통신에는 적합하지 않다고 생각했다. 더군다나 구조체 데이터들이 복잡하다면 모를까 만약 간단하다면 json데이터를 만들고 파싱하는 과정보다는 직접 구조체의 각 필드를 []byte형으로 변환해 주고 받는 것이 간단하다고 생각했다.

 

그러므로 2번 방식으로 당첨 ! 다음은 구현 예제이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
func ConnHandler(conn net.Conn) {
    sizeBuf := make([]byte4)
    var bodyBuf []byte
 
    for {
        // get size
        n, err := conn.Read(sizeBuf)
        if nil != err {
            if io.EOF == err {
                log.Println("err(eof):", err)
                break
            }
            log.Println("err:", err)
            break
        }
        size := binary.BigEndian.Uint32(sizeBuf)
 
        // get body
        recvBuf := make([]bytesize)
        readsize := size
        for readsize > 0 {
            n, err = conn.Read(recvBuf)
            if nil != err {
                if io.EOF == err {
                    log.Println("err(eof):", err)
                    break
                }
                log.Println("err:", err)
                break
            }
            if n > 0 {
                bodyBuf = append(bodyBuf, recvBuf...)
                readsize -= uint32(n)
            }
        }
 
        // parsing
        // idx : 4byte (uint32)
        // name : 32byte (string)
        // address : 128 byte (string)
        idx := binary.BigEndian.Uint32(bodyBuf[:4])
        name := string(bodyBuf[4:36])
        address := string(bodyBuf[36:164])
 
        // parsing
        log.Println("idx:", idx)
        log.Println("name:", name)
        log.Println("address", address)
    }
}

size 정보를 먼저 받고 본 데이터를 받는 과정은 앞에 포스팅한 내용과 같다. 받고나서 []byte 데이터를 쪼개서 차례대로 uint32, string 자료형에 담는다.  41 ~ 43줄을 보면 바이트 배열에 접근하는데 인덱스가 조금 이상하다고 생각할 수 있는데 "bodyBuf[:4]가 아니라 bodyBuf[:3]이어야 하지 않는가 ?! 그래야 0~3으로 4개 가져오지 !" 바이트 배열에서 []연산자로 부분 데이터를 가져올 때 원래 끝 인덱스는 +1 해서 가져오는 것이 규칙이다.

 

이런식으로 구현해 놓으면 go 뿐만 아니라 타 언어와 tcp방식으로 구조체 데이터를 주고 받을 수 있다.