golang - epoll 예제 코드
Golang에서 epoll API를 호출해서 서버 프로그램을 만드는 예제 코드이다.
출처
epoll.go
package main
import (
"syscall"
"fmt"
)
const (
EPOLLET = 1 << 31
MaxEpollEvents = 32
)
type epoll struct {
fd int
}
func initEpoll() (epoll, error) {
epfd, err := syscall.EpollCreate1(0)
if err != nil {
return epoll{}, err
}
return epoll{fd: epfd}, nil
}
func (ep *epoll) close() {
syscall.Close(ep.fd)
}
func (ep *epoll) wait() ([]syscall.EpollEvent, error) {
var events [MaxEpollEvents]syscall.EpollEvent
nevents, err := syscall.EpollWait(ep.fd, events[:], -1)
if err != nil {
return []syscall.EpollEvent{}, err
}
return events[:nevents], nil
}
func (ep *epoll) add(fd int, eventOperations uint32, edgeMode bool) error {
fmt.Println("epoll add:", fd)
var event syscall.EpollEvent
event.Events = eventOperations
if edgeMode {
event.Events |= EPOLLET
}
event.Fd = int32(fd)
if err := syscall.EpollCtl(ep.fd, syscall.EPOLL_CTL_ADD, fd, &event); err != nil {
return err
}
return nil
}
main.go 일부
// 이벤트 핸들러
// syscall.EpollEvent의 events에 의해서 실행하는 처리를 구분한다
// 먼저 echo 동작을 위한 구현
func handleConnectedEvent(event syscall.EpollEvent) {
fmt.Println("event:", event)
switch event.Events {
case syscall.EPOLLIN:
echo(int(event.Fd))
case syscall.EPOLLIN | syscall.EPOLLRDHUP:
fmt.Println("connection close: ", event.Fd)
syscall.Close(int(event.Fd))
}
}
// 소켓에서 read 한 내용을 그대로 보낸다
func echo(fd int) {
var buf [32 * 1024]byte
nbytes, _ := syscall.Read(fd, buf[:])
if nbytes > 0 {
fmt.Printf(">>> %s", buf)
syscall.Write(fd, buf[:nbytes])
}
}
func main() {
var listenFd int
var err error
listenFd, err = initListenFd("0.0.0.0", 3000)
if err != nil {
exit(err)
}
defer syscall.Close(listenFd)
var ep epoll
ep, err = initEpoll()
if err != nil {
exit(err)
}
defer ep.close()
add := ep.add(listenFd, syscall.EPOLLIN,false)
if err = add; err != nil {
exit(err)
}
var events []syscall.EpollEvent
for {
// 이벤트 통지 대기
events, err = ep.wait()
if err != nil {
exit(err)
}
for _, event := range events {
// 파일 디스크립터가 신규 접속 대기를 하는 것이라면 신규 접속 처리
if int(event.Fd) == listenFd {
connFd, _, err := syscall.Accept(listenFd)
if err != nil {
exit(err)
}
defer syscall.Close(connFd)
// 연결 확립 후의 통신 용 소켓의 파일 디스크립터를 epoll에 등록한다
if e := ep.add(connFd, syscall.EPOLLIN | syscall.EPOLLRDHUP,true); e != nil {
exit(err)
}
} else {
// コネクション確率済みの処理は handleConnectedEvent を呼び出す
handleConnectedEvent(event)
}
}
}
}
goroutine을 사용하지 않기 때문에 모든 것을 메인 스레드에서 처리하고 있다.
그러나 이 구현은 이벤트 구동으로 블로킹이 아니다.
이 글은 2020-10-08에 작성되었습니다.