일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Hyper-V
- 레파지토리
- Replication
- BATCH
- 자원측정
- dockercontainer
- psutil
- FastAPI
- Thread
- Finance
- asyncio
- docker
- APT
- repo
- QT_QPA_PLATFORM_PLUGIN_PATH
- 가상화 중첩화
- 스트리밍
- golang
- go.mod
- Python
- apt-get
- 자식프로세스
- Windows
- pip
- 주식
- go
- python3.9
- mariadb
- 영상스트리밍
- 파이썬
- Today
- Total
검색하기귀찮아서만든블로그
[GO] 간단한 proxy 서버 만들기 본문
지난번 포스팅 (https://access-violation.tistory.com/37)에서 GO 언어의 IDE를 구성했으니 이제 간단한 코딩을 통해서 go 언어에 대한 간략한 문법을 익히고자 한다.
프로젝트는 프록시 서버를 만들어 보려고 한다. 기능 자체는 간단하다. VM A, VM B를 준비하고, 사용자가 A로 접속하면 B로 패킷을 포워딩해서 사용자가 B로 접속하게 만들어주는 기능이다. 사용자는 A로 접속을 시도하지만 실제로는 B로 접속하는 것이다. 서비스는 RDP 서비스를 사용해서 테스트할 생각이고, VM 구성은 Hyper-V를 사용하여 구성한다.
(Hyepr-V VM 구성은 다음 포스팅을 참고하세요. https://access-violation.tistory.com/31, https://access-violation.tistory.com/33)
아래는 RDP 프록시 서버의 간단한 샘플 코드이다.
package main
import (
"fmt"
"io"
"net"
)
func handleProxyConnection(sockClient net.Conn, strTargetAddr string) {
// 대상 장비로 접속을 시도한다.
sockTarget, err := net.Dial("tcp", strTargetAddr)
if err != nil {
fmt.Printf("Error connecting to target: %s\n", err.Error())
sockClient.Close()
return
}
fmt.Printf("Start proxy operation. %v <-> %v\n", sockClient.RemoteAddr().String(), sockTarget.RemoteAddr().String())
// goroutine으로 실행할 익명 함수 (비동기)
go func() {
// 함수가 종료될 때 양측 소켓을 소멸한다.
defer sockClient.Close()
defer sockTarget.Close()
// 클라이언트에서 받은 데이터를 대상으로 전달하도록 I/O를 복사한다.
io.Copy(sockTarget, sockClient)
}() // 익명 함수를 즉시 호출한다는 의미
// goroutine으로 실행할 익명 함수 (비동기)
go func() {
defer sockClient.Close()
defer sockTarget.Close()
// 대상에서 받은 데이터를 클라이언트로 전달하도록 I/O를 복사한다.
io.Copy(sockClient, sockTarget)
}() // 익명 함수를 즉시 호출한다는 의미
}
func main() {
// 최대 4개의 CPU 코어를 사용하도록 설정
runtime.GOMAXPROCS(4)
// proxy 리스을 시작한다.
sockListen, err := net.Listen("tcp", ":23389")
if err != nil {
fmt.Printf("Failed to setup listener: %v\n", err)
}
fmt.Printf("Start waiting for connection. (23389)\n")
for {
// 클라이언트 접속 대기
sockClient, err := sockListen.Accept()
if err != nil {
fmt.Printf("Failed to accept connection: %v\n", err)
continue
}
fmt.Printf("Client connected: %v\n", sockClient.RemoteAddr().String())
// 클라이언트소켓을 대상과 연결한다.
go handleProxyConnection(sockClient, "VM-YMKANG:13389")
}
}
gopls 툴 때문에 그런지 모르겠지만 코드를 작성하고 ctrl+s 를 누르면 자동으로 줄도 맞춰주고 신텍스 오류도 하이라이팅 돼서 코드 작성 및 생산성 향상에 장점이 있다고 생각되었다.
코드에 대한 설명을 주석으로 작성해 두었다. 함수 내부에 go라는 goroutine 비동기 함수를 사용해서 양쪽 소켓의 I/O를 연결시켜주고 호출만 해주면 간단하게 프록시 기능이 동작한다고 한다. 그리고 defer를 사용해서 함수가 종료될 때 처리해야 할 내용을 작성할 수 있도록 제공해 주었다. 간단한 코드라서 그 외에 다른 특징은 못 느꼈지만 언어에 대한 진입 장벽이 높게 느껴지지 않아서 좋았다.
먼저 go run main.go 명령어를 사용해서 로컬에서 실행했을 때 방화벽 예외 등록 팝업이 뜨는 걸 봐서는 정상적으로 실행은 되는 것으로 보인다. 이제 VM-A에서 실행하기 위해서 exe 파일을 생성해 보자. go build -o rdp_proxy.exe main.go 명령어를 사용해서 빌드 하면 rdp_proxy.exe 파일이 생성되는 것을 볼 수 있다.
Local PC 에서 mstsc.exe를 실행해서 VM-A:23389로 접속하면 실제로는 VM-B:13389 호스트로 접속되는 것을 볼 수 있다. rdp_proxy.exe 콘솔 화면과 netstat -ano |findstr 3389를 확인해 보면 RDP 접속에 대해서 프록시 동작을 하는 것을 볼 수 있다. 비동기로 처리되기 때문에 다중 프록시에 대한 기능도 동작할 것으로 생각된다.
지금까지 go 언어를 사용해서 간단하게 rdp 프록시 서버를 만들어 보았다. 하위 레벨 언어로 작성할 경우 tcp 소켓의 버퍼 풀이나 세션 관리에 번거로운 작업들을 많이 해야 가능한 기능이 go 언어로 개발했을 때 정만 간단하게 구현이 되고, 성능도 좋다고 하니 생산성이 상당히 올라간다고 생각된다. goroutine으로 작성된 동시성 함수는 runtime.GOMAXPROCS() 설정에 따라서 멀티 프로세싱까지 처리가 가능하다고 한다. (미검증) python에서 멀티프로세싱을 사용하려면 다소 귀찮은 작업들을 해줘야 했는데.. (최근은 개선되었을 수 있음) 물론 파인썬 이하 저수준 언어들은 더 복잡..
go는 서버 개발에 적합한 언어라고 하니 대규모 서버 구현에 사용해 볼 만한 가치가 충분하다고 생각된다.
'개발' 카테고리의 다른 글
[GO] IDE 구성하기 (0) | 2023.09.28 |
---|---|
[mariadb] 양방향 replication (0) | 2023.06.17 |
[mariadb] replication (0) | 2023.06.17 |
[Docker] mariadb 컨테이너 구축 (0) | 2023.06.17 |
[ML] CNN 알고리즘에 대한 이해와 고찰 (0) | 2022.11.29 |