Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- write
- go언어
- Golang
- API
- 리뷰
- json
- 영화
- Linux
- mutex
- 책
- window
- FOR
- go
- Close
- package
- channel
- Sync
- install
- windows
- c++
- JavaScript
- File
- GO 언어
- C
- tcp
- Callback
- bitcoin
- http
- range
- Python
Archives
- Today
- Total
Code Habit
go ) 간단한 TCP echo 서버/클라이언트 예제 본문
go를 이용하여 간단한 tcp echo 서버/클라이언트를 만들어 보겠다.
server
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
51
52
53
|
// main.go
package main
import (
"io"
"log"
"net"
)
func main() {
l, err := net.Listen("tcp", ":8000")
if nil != err {
log.Println(err)
}
defer l.Close()
for {
conn, err := l.Accept()
if nil != err {
log.Println(err)
continue
}
defer conn.Close()
go ConnHandler(conn)
}
}
func ConnHandler(conn net.Conn) {
recvBuf := make([]byte, 4096)
for {
n, err := conn.Read(recvBuf)
if nil != err {
if io.EOF == err {
log.Println("err(eof):", err)
break
}
log.Println("err:", err)
break
}
if 0 < n {
data := recvBuf[:n]
log.Println(string(data))
_, err = conn.Write(data[:n])
if err != nil {
log.Println("err:", err)
break
}
}
}
}
|
- 11) l, err = net.Listen("tcp", ":8000") : 로컬에 8000번 포트로 tcp 프로토콜 방식의 서버를 연다.
- 18) conn, err= l.Accept() : 클라이언트 연결을 대기한다. 연결되면 연결 정보가 conn에 반환되며 이는 c의 socket처럼 쓰인다.
- 32) n, err := conn.Read(recvBuf) : 입력버퍼에 데이터가 들어올 때까지 대기한다. 즉, 클라이언트의 전송을 대기한다. 데이터를 읽으면 recvBuf에 n데이터 길이만큼 []byte 타입으로 데이터가 채워진다.
- 45) _, err = conn.Write(data[:n]) : 받은 데이터를 출력 후 그대로 클라이언트로 전송한다. ( echo 기능 )
client
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
|
// client.go
package main
import (
"fmt"
"log"
"net"
)
func main() {
conn, err := net.Dial("tcp", ":8000")
if nil != err {
log.Println("err:", err)
return
}
go func() {
data := make([]byte, 4096)
for {
n, err := conn.Read(data)
if err != nil {
log.Println(err)
break
}
log.Println("Server send:" + string(data[:n]))
}
}()
for {
var s string
fmt.Scanln(&s)
conn.Write([]byte(s))
}
}
|
- 11) conn, err := net.Dial("tcp", ":8000") : localhost에 8000번 포트로 tcp 방식으로 연결 요청한다. 연결되면 연결 정보가 conn에 반환된다.
- 20) n, err := conn.Read(data) : 입력버퍼에 데이터가 들어오기를 기다린다. 해당 함수는 go 루틴 내 반복문내에 위치해 데이터가 들어올때마다 읽어들일 수 있다.
- 33) conn.Write([]byte(s)) : 사용자에게 입력받은 데이터 (s)를 tcp 출력버퍼에 보낸다. 즉, server로 전송한다.
메시지를 입력하면 그대로 돌려 받는 간단한 에코 예제를 작성해 보았다. 이렇게 go 에서는 c에 비해 비교적 간단하게 tcp 서버/클라이언트를 구현할 수 있다. 서버를 열고, 접속하는 함수 및 인자들이 보다 깔끔하고 직관적이며 go 루틴을 사용하여 멀티로 접속하는 클라이언트들을 보다 쉽게 처리할 수 있다.
위 예제는 기본 예제로 몇가지 문제점이 존재하는데 그 중 가장 큰 문제는 주고 받는 데이터의 크기를 알 수 없다는 것이다. 예제를 돌려보면 워낙 데이터 양이 작기도 하고 local에서 테스트를 하기 때문에 별 문제 없겠지만 실제 서버를 돌리다보면 부하가 발생시 클라이언트에서 보내주는 데이터를 한번에 다 받지 못할 수도 있다. 이렇게 되면 데이터가 짤릴 수 있어 의도한대로 프로그램이 동작하지 않을 수 있다.
이 예제에서는 go에서 tcp 프로토콜을 어떻게 다루는지 기본적인 부분을 살펴본 것이므로 다음 포스팅에서 위 문제를 해결할 수 있는 방법과 코드를 제시해보겠다.