이번 시간에는 go언어
소켓 프로그래밍을 다뤄보겠습니다.
net
패키지
go
의 네트워크 통신 관련 함수는 net
패키지에 정의되어 있습니다.
import "net"
다음 함수들은 기본적으로 동기 적으로 작동합니다.
net.Dial()
클라이언트가 서버에게 연결을 요청할 때 net.Dial()
함수를 사용합니다.
첫 번째 인자로 통신 프로토콜을, 두 번째 인자로 주소를 입력합니다.
지원하는 프로토콜
tcp
,tcp4
,tcp6
udp
,udp4
,udp6
ip
,ip4
,ip6
unix
,unixgram
,unixpacket
tcp
는 ipv4
, ipv6
전부 사용, tcp4
는 ipv4
만 사용, tcp6
은 ipv6
만 사용합니다.
tcp 예제
func main() {
conn, err := net.Dial("tcp", "localhost:http")
if err != nil {
log.Fatal("dial error: " + err.Error())
}
defer conn.Close()
// ...
}
udp 예제
func main() {
conn, err := net.Dial("udp", "[2001:db8::1]:50000")
if err != nil {
log.Fatal("dial error: " + err.Error())
}
// ...
}
unix 예제
func main() {
conn, err := net.Dial("unix", "./sock")
if err != nil {
log.Fatal("dial error: " + err.Error())
}
// ...
}
net.Conn
net.Dial
함수는 서버와 연결 후, net.Conn
을 리턴 합니다.
이를 사용해서 서버와 입출력을 하면 됩니다.
net.Conn
은 io.Reader
, io.Writer
인터페이스를 따릅니다.
net.Conn
소켓에 데이터 쓰기
io
, ioutil
, bytes
, strings
패키지를 활용할 수 있습니다.
// n - 소켓에 작성한 바이트 수
// err - 에러
n, err := conn.Write([]byte("hello"))
reader := strings.NewReader("hello world")
reader.WriteTo(conn)
net.Conn
소켓에 데이터 읽기
b := make([]byte, 2048)
// n - 소켓에서 읽어낸 바이트 수
// err - 에러
n, err := conn.Read(b)
buffer := bytes.NewBuffer(nil)
buffer.ReadFrom(conn)
f, _ := os.OpenFile("./data", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0600)
io.Copy(f, conn)
net.Listen()
서버 소켓을 열고자 할 때 사용하는 함수입니다.
listener, err := net.Listen("tcp", "localhost:50000")
if err != nil {
log.Fatal(err.Error())
}
defer listener.Close()
net.Listener
, Accept()
net.Listne()
함수는 net.Listener
를 리턴 합니다.
Listener.Accept()
이 함수 실행 시, 클라이언트로 부터 연결 요청이 있을 때 까지 프로세스를 대기 시킵니다.
만약, 연결 요청 시, Accept()
함수는 클라이언트 소켓을 리턴하고, 함수를 종료합니다.
clientConn, err := listener.Accept()
if err != nil {
log.Print(err.Error())
}
연결을 요청한 클라이언트와 통신하는 clientConn
이 생성 되었습니다. net.Dial()
이 리턴 하는 net.Conn
과 사용법이 같습니다.
예제: 에코 서버
package main
import (
"fmt"
"log"
"net"
)
func main() {
listener, err := net.Listen("tcp", "localhost:50000")
if err != nil {
log.Fatal(err.Error())
}
defer listener.Close()
go func() {
// 쓰레드 시작
// 클라이언트 소켓 생성
conn, _ := net.Dial("tcp", "localhost:50000")
// 데이터를 저장할 버퍼
buf := make([]byte, 512)
// 서버로부터 데이터를 읽어옴
conn.Read(buf)
// 데이터 출력
fmt.Printf("%s\n", buf)
}()
for {
// 클라이언트 연결 요청까지 대기
accept, err := listener.Accept()
// 예외처리
if err != nil {
log.Print(err.Error())
}
// 클라이언트 소켓 핸들러
go handleClient(accept)
// 반복문 처음으로 돌아감
}
}
func handleClient(conn net.Conn) {
// hello 메세지와 클라이언트의 주소를 전달함
_, err := conn.Write([]byte("hello " + conn.RemoteAddr().String()))
// 예외처리
if err != nil {
log.Print(err.Error())
}
}