在我的博客中我以前已经用 Python 描述过 Epoll 了,但是在 Go 语言中还没有尝试过,很大一个原因是 Go 里面自带 Goroutine,但是 EPoll 仍然有更高的性能,所以这里我就尝试一下。

我之前讲述的 Python 代码版本为:select,poll 和 epoll 概述

下面就直接贴一下 Go 版本,这份代码不是原创,是抄来的:gist.github.com/tevino/3a4f4ec4ea9d0ca66d4f

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "os"
  6. "syscall"
  7. )
  8. const (
  9. EPOLLET = 1 << 31
  10. MaxEpollEvents = 32
  11. )
  12. func echo(fd int) {
  13. defer syscall.Close(fd)
  14. var buf [32 * 1024]byte
  15. for {
  16. nbytes, e := syscall.Read(fd, buf[:])
  17. if nbytes > 0 {
  18. fmt.Printf(">>> %s", buf)
  19. syscall.Write(fd, buf[:nbytes])
  20. fmt.Printf("<<< %s", buf)
  21. }
  22. if e != nil {
  23. break
  24. }
  25. }
  26. }
  27. func main() {
  28. var event syscall.EpollEvent
  29. var events [MaxEpollEvents]syscall.EpollEvent
  30. fd, err := syscall.Socket(syscall.AF_INET, syscall.O_NONBLOCK|syscall.SOCK_STREAM, 0)
  31. if err != nil {
  32. fmt.Println(err)
  33. os.Exit(1)
  34. }
  35. defer syscall.Close(fd)
  36. if err = syscall.SetNonblock(fd, true); err != nil {
  37. fmt.Println("setnonblock1: ", err)
  38. os.Exit(1)
  39. }
  40. addr := syscall.SockaddrInet4{Port: 2000}
  41. copy(addr.Addr[:], net.ParseIP("0.0.0.0").To4())
  42. syscall.Bind(fd, &addr)
  43. syscall.Listen(fd, 10)
  44. epfd, e := syscall.EpollCreate1(0)
  45. if e != nil {
  46. fmt.Println("epoll_create1: ", e)
  47. os.Exit(1)
  48. }
  49. defer syscall.Close(epfd)
  50. event.Events = syscall.EPOLLIN
  51. event.Fd = int32(fd)
  52. if e = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, fd, &event); e != nil {
  53. fmt.Println("epoll_ctl: ", e)
  54. os.Exit(1)
  55. }
  56. for {
  57. nevents, e := syscall.EpollWait(epfd, events[:], -1)
  58. if e != nil && e != syscall.EINTR{
  59. fmt.Println("epoll_wait: ", e)
  60. break
  61. }
  62. for ev := 0; ev < nevents; ev++ {
  63. if int(events[ev].Fd) == fd {
  64. connFd, _, err := syscall.Accept(fd)
  65. if err != nil {
  66. fmt.Println("accept: ", err)
  67. continue
  68. }
  69. syscall.SetNonblock(connFd, true)
  70. event.Events = syscall.EPOLLIN | EPOLLET
  71. event.Fd = int32(connFd)
  72. if err := syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, connFd, &event); err != nil {
  73. fmt.Print("epoll_ctl: ", connFd, err)
  74. os.Exit(1)
  75. }
  76. } else {
  77. go echo(int(events[ev].Fd))
  78. }
  79. }
  80. }
  81. }