Go代码epollechoserver代码实现
基于 epoll 系统调用实现非阻塞的echo server:
这部分代码是 Go BIO/NIO探讨(7):IO多路复用之epoll 的附录
package main import ( "fmt" "log" "net" "os" "os/signal" "syscall" ) func ipToSockaddrInet4(ip net.IP, port int) (syscall.SockaddrInet4, error) { if len(ip) == 0 { ip = net.IPv4zero } ip4 := ip.To4() if ip4 == nil { return syscall.SockaddrInet4{}, &net.AddrError{Err: "non-IPv4 address", Addr: ip.String()} } sa := syscall.SockaddrInet4{Port: port} copy(sa.Addr[:], ip4) return sa, nil } func isError(ev uint32) bool { return (ev&uint32(syscall.EPOLLERR)) > 0 || (ev&uint32(syscall.EPOLLHUP)) > 0 || (ev&syscall.EPOLLIN) == 0 } func main() { var ( family = syscall.AF_INET sotype = syscall.SOCK_STREAM _ = "tcp" listenBacklog = syscall.SOMAXCONN serverip = net.IPv4(0, 0, 0, 0) serverport = 8080 ) // 创建套接字 sockfd, err := syscall.Socket(family, sotype, 0) if err != nil { panic(fmt.Errorf("fails to create socket: %s", err)) } syscall.CloseOnExec(sockfd) // epoll edge-triggered 模式支持nonblock if err := syscall.SetNonblock(sockfd, true); err != nil { syscall.Close(sockfd) panic(fmt.Errorf("setnonblock error=%v", err)) } // 接收到Ctrl+C信号后,关闭socket c := make(chan os.Signal) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c log.Println("r- Ctrl+C pressed in Terminal") if err := syscall.Close(sockfd); err != nil { log.Printf("Close sockfd %d fails, err=%v ", sockfd, err) } else { log.Printf("Server stopped successfully!!!") } // 收到信号后需要处理, 否则程序会永久hang住, 需要kill -9 // os.Exit 会导致所有goroutine都会立即停止执行 os.Exit(0) }() addr, err := ipToSockaddrInet4(serverip, serverport) if err != nil { panic(fmt.Sprintf("fails to convert address %s:%d to socket addr, err=%s", serverip, serverport, err)) } if err := syscall.Bind(sockfd, &addr); err != nil { panic(fmt.Sprintf("fails to bind socket %d to address %s:%d, err=%s", sockfd, serverip, serverport, err)) } if err := syscall.Listen(sockfd, listenBacklog); err != nil { log.Printf("listen sockfd %d to addr error=%v ", sockfd, err) panic(fmt.Sprintf("fails to listen socket %d", sockfd)) } else { log.Printf("Started listening on %s:%d", serverip, serverport) } epfd, err := syscall.EpollCreate1(0) if err != nil { panic(fmt.Sprintf("create epoll instance fails, err=%s", err)) } // 默认是 level-triggered,效率更高的poll epEvent := syscall.EpollEvent{ Fd: int32(sockfd), Events: uint32(syscall.EPOLLIN) | uint32(-syscall.EPOLLET), } if err := syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, sockfd, &epEvent); err != nil { panic(fmt.Errorf("epoll_ctl %v fails, err=%s", epfd, err)) } events := make([]syscall.EpollEvent, 128, 128) var buf [32 * 1024]byte for { // msec < 0, EpollWait 会被阻塞直到有一个 fd 可用 nReady, err := syscall.EpollWait(epfd, events, -1) if err != nil { log.Printf("epoll_wait error=%v ", err) panic(fmt.Errorf("epoll_wait error=%v", err)) } for i := 0; i < nReady; i++ { ev := &events[i] if isError(ev.Events) { /* An error has occured on this fd, or the socket is not ready for reading (why were we notified then?) */ log.Printf("epoll error: %s ", err) // 取消监听 _ = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_DEL, int(ev.Fd), ev) _ = syscall.Close(int(events[i].Fd)) continue } if ev.Fd == int32(sockfd) { for { // 监听套接字(server端套接字 clientfd, _, err := syscall.Accept(sockfd) if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK { // 所有新创建的tcp conn均已被处理 break } // 设置为nonblock if err := syscall.SetNonblock(clientfd, true); err != nil { log.Printf("fails to set client socket %v as nonblock, err=%s ", clientfd, err) continue } epEvent.Fd = int32(clientfd) epEvent.Events = uint32(syscall.EPOLLIN) | uint32(-syscall.EPOLLET) if err := syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, clientfd, &epEvent); err != nil { log.Printf("register client socket %v fails, err=%s ", clientfd, err) syscall.Close(clientfd) continue } } } else { // 已连接套接字 tcp conn for { nRead, err := syscall.Read(int(ev.Fd), buf[:]) if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK { // 数据已经读完了 break } else if err != nil { log.Printf("fails to read data from sockfd %d, err=%v ", ev.Fd, err) _ = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_DEL, int(ev.Fd), ev) _ = syscall.Close(int(ev.Fd)) break } else if nRead == 0 { // EOF // Client closed log.Printf("client sock %d closed ", ev.Fd) _ = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_DEL, int(ev.Fd), ev) _ = syscall.Close(int(ev.Fd)) break } else { log.Printf("read %d bytes from sock %d ", nRead, ev.Fd) if _, err := syscall.Write(int(ev.Fd), buf[:nRead]); err != nil { log.Printf("fails to write data %s into sockfd %d, err=%v ", buf[:nRead], sockfd, err) } } } } } } }
四十六岁了怀孕了,生还是不生?生吧,现在国家鼓励生三胎,况且你正是婚龄阶段,也正是生孩子的年龄。当下老龄化社会逐渐明显,年轻人的社会负担也逐渐加重,未来的劳动力也会逐渐缺乏,如果人口数量下降,会对整个社会有很大
怎么让宝宝爱上辅食呢?最近也有很多宝妈留言说宝宝不爱吃辅食,总结了一下我家两个小饭霸的养成记,供大家参考一下哈。说在前面宝宝肯定会有个体差异,我们讲的喂养原则可能不适应于个别的宝宝,宝爸宝妈们也不要过于
怀孕16周可以打掉吗?怀孕16周可以打掉吗?怀孕16周可以打掉,但由于胎儿月份比较大,所以需要到大型的正规医院住院进行引产。怀孕16周月份比较大,所以这个时候打掉胎儿,就需要到大型的正规医院住院进行引产
想不明白,为什么那么多人看不惯陈亚男呢?中国几千年传统文化,讲究从一而终,不因富贵贫贱抛弃结发妻,不因功成名就而招摇过市。反过来看陈亚楠,依靠大衣哥名声能挣钱了百般刁难朱小伟,目的就一个,踹了朱小伟,直播在粉丝面前说老朱
萌新买了个lol账号发现里面有DNF角色,大家帮我看看这样角色值多少钱?赚了吗?大家好,我是吃瓜。今天要说的话题是萌新买了个lol账号发现里面有DNF角色,大家帮我看看这样角色值多少钱?赚了吗?DNF账号买卖是正常的事情,而且由于当初玩家众多现在可以说很多人q
和平精英如何用好AK?谢邀在和平精英中,AK不仅威力大,后坐力也不是一般人能掌控,所以玩家们都不是很喜欢用它。经常吐槽的一句话就是AK压不住,如果能为AK多一个配件槽,想都不想肯定是加一个握把了,但现阶
澳门赛陈梦和王曼昱半决赛狭路相逢,陈梦能赢得复仇之战吗?WTT澳门赛,上半区女单半决赛对阵揭晓,世界第一陈梦将再次挑战新科世乒赛女单冠军王曼昱。这是两个人继世乒赛之后,再次在半决赛相遇。已经5连败于王曼昱的陈梦,能赢得复仇之战吗?下面也
国足沦落到如今这般田地,问题到底出在哪里?要说好的球员,咱不是没有过,要说好的教练,咱们也不是没请过那么问题到底在哪呢?莫非真的是造化弄人,好的球员没遇到好的教练或者好教练没赶上好球员?足协,你来说说呗这问题到底谁出在哪了
詹姆斯被质疑!威少被球队孤立,湖人队该何去何从呢?一个烂队,集齐一帮马上过气的老家伙,混个关注,增加点流量,赚点钱得了,还不自量力去争夺什么总冠军,战绩出现反差,实际上是战略认知上出现偏差,期望值定的过高,而球队人员结构的老化,已
gucci都有哪些好看的鞋子推荐啊?其实Gucci的鞋子种类比较多,也被很多女孩子追捧,下面我就为大家推荐几款今年比较时尚的款式!1小白鞋,19FW水钻箭头,以前主要是小蜜蜂为主,每一个时代的小白鞋都在更新,这款小白
男人多久洗一次头发最科学?别看洗头发简单,这里面学问大着呢。虽说男人头发短,修剪起来快,不像女人头发,理发难度高,洗头发也是比较麻烦。但是,不论男女,洗头发麻烦与否,都得掌握一定的频率。我认为间隔两天到三天