[Go] TCP 소켓통신하기
간단한 TCP 서버와 클라이언트를 구현하는 것으로 소켓통신 프로그램의 구조를 살펴보도록 하겠다.
서버부터 짜보자.
tcp 소켓을 사용하는 것에 필요한 패키지는 io와 net이다.
log는 콘솔 출력용이다.
그리고 가장 먼저 해야할건 리스너 객체를 생성하는 것이다. 첫번째 인자로 tcp임을 알리고, 두번째 인자로 서버의 포트값을 써준다.

그리고 연결을 무한히 받을 수 있도록 루프를 열고,
리스터의 Accept 메서드로 연결을 기다린다.
이건 계속 블럭해서 기다리다가 연결이 들어왔을 경우에 값을 반환한다.
사실 여기서 그냥 연결에서 바로 값을 읽어오거나 써도 되지만, 원활한 순환을 위해 고루틴을 사용했다.
고루틴을 사용하지 않고 그냥 바로 연결에 대한 작업을 수행하면, 하나를 처리하고 있을 때는 다른 연결을 받을 수가 없다.
그래서 나는 익명함수를 생성해서 바로 고루틴을 돌렸다.
따로 함수를 분리해도 좋다.
그리고 저 안에서는
값을 읽어와서 저장할 버퍼를 하나 두고,
값을 읽어와서 콘솔에 출력한다.
좀 길긴 하지만 어려울 것은 없다.
이번엔 클라이언트를 보자.
아주 단순하게 구성해놨다. 별건없다.
Dial로 연결 객체를 생성, Write 메서드로 서버에 텍스트를 byte열로 전송한다.
그리고 작업을 다 끝내면 Close로 연결을 종료하는 것을 절대 잊지 말자.
다 짰으면 테스트를 해보자.
서버를 먼저 실행해놓고

클라이언트를 실행하면

반응이 올 것이다.

여기선 소켓서버가 일방적으로 받기만 하고, 클라이언트가 일방적으로 보내기도 하지만, 연결이 유지된 동안에는 끝없이 서로 주고받는 게 가능하다.
응용만 더하면 멋진 소켓서버를 구성할 수 있을 것이다.
서버 코드
package main
import (
"io"
"log"
"net"
)
func main() {
//통신 방식과 포트값을 전달해 리스너 객체 생성
listener, error := net.Listen("tcp", ":10000")
//예외처리
if nil != error {
log.Fatalf("fail to bind address to 5032; err: %v", error)
}
defer listener.Close()
log.Printf("## 프로그램 시작")
//메인 루프
for {
//연결 대기
connection, error := listener.Accept()
//연결 실패
if nil != error {
log.Printf("연결 실패: %v", error)
continue
} else {
log.Printf("클라이언트 연결: %v", connection.RemoteAddr())
}
//각 연결에 대한 처리를 고루틴으로 실행
go func() {
buffer := make([]byte, 1000) //버퍼
//다 받을때까지 반복하며 읽음
for {
//입력
count, error := connection.Read(buffer)
if nil != error {
//입력이 종료되면 중료
if io.EOF == error {
log.Printf("연결 종료: %v", connection.RemoteAddr().String())
} else {
log.Printf("수신 실패: %v", error)
}
return
}
if 0 < count {
//받아온 길이만큼 슬라이스를 잘라서 출력
data := buffer[:count]
log.Println(string(data))
}
}
}()
}
}
클라이언트 코드
package main
import (
"net"
"log"
)
func main(){
connection, error := net.Dial("tcp", ":10000")
if nil != error {
log.Printf("접속 실패")
} else {
_, error := connection.Write([]byte("붐"))
if nil == error {
log.Printf("전송 성공")
}
connection.Close()
}
}