基于epoll系统调用实现非阻塞的echoserver: 这部分代码是GoBIONIO探讨(7):IO多路复用之epoll的附录 packagemainimport(fmtlognetosossignalsyscall)funcipToSockaddrInet4(ipnet。IP,portint)(syscall。SockaddrInet4,error){iflen(ip)0{ipnet。IPv4zero}ip4:ip。To4()ifip4nil{returnsyscall。SockaddrInet4{},net。AddrError{Err:nonIPv4address,Addr:ip。String()}}sa:syscall。SockaddrInet4{Port:port}copy(sa。Addr〔:〕,ip4)returnsa,nil}funcisError(evuint32)bool{return(evuint32(syscall。EPOLLERR))0(evuint32(syscall。EPOLLHUP))0(evsyscall。EPOLLIN)0}funcmain(){var(familysyscall。AFINETsotypesyscall。SOCKSTREAMtcplistenBacklogsyscall。SOMAXCONNserveripnet。IPv4(0,0,0,0)serverport8080)创建套接字sockfd,err:syscall。Socket(family,sotype,0)iferr!nil{panic(fmt。Errorf(failstocreatesocket:s,err))}syscall。CloseOnExec(sockfd)epolledgetriggered模式支持nonblockiferr:syscall。SetNonblock(sockfd,true);err!nil{syscall。Close(sockfd)panic(fmt。Errorf(setnonblockerrorv,err))}接收到CtrlC信号后,关闭socketc:make(chanos。Signal)signal。Notify(c,os。Interrupt,syscall。SIGTERM)gofunc(){clog。Println(rCtrlCpressedinTerminal)iferr:syscall。Close(sockfd);err!nil{log。Printf(Closesockfddfails,errv,sockfd,err)}else{log。Printf(Serverstoppedsuccessfully!!!)}收到信号后需要处理,否则程序会永久hang住,需要kill9pidos。Exit会导致所有goroutine都会立即停止执行os。Exit(0)}()addr,err:ipToSockaddrInet4(serverip,serverport)iferr!nil{panic(fmt。Sprintf(failstoconvertaddresss:dtosocketaddr,errs,serverip,serverport,err))}iferr:syscall。Bind(sockfd,addr);err!nil{panic(fmt。Sprintf(failstobindsocketdtoaddresss:d,errs,sockfd,serverip,serverport,err))}iferr:syscall。Listen(sockfd,listenBacklog);err!nil{log。Printf(listensockfddtoaddrerrorv,sockfd,err)panic(fmt。Sprintf(failstolistensocketd,sockfd))}else{log。Printf(Startedlisteningons:d,serverip,serverport)}epfd,err:syscall。EpollCreate1(0)iferr!nil{panic(fmt。Sprintf(createepollinstancefails,errs,err))}默认是leveltriggered,效率更高的pollepEvent:syscall。EpollEvent{Fd:int32(sockfd),Events:uint32(syscall。EPOLLIN)uint32(syscall。EPOLLET),}iferr:syscall。EpollCtl(epfd,syscall。EPOLLCTLADD,sockfd,epEvent);err!nil{panic(fmt。Errorf(epollctlvfails,errs,epfd,err))}events:make(〔〕syscall。EpollEvent,128,128)varbuf〔321024〕bytefor{msec0,EpollWait会被阻塞直到有一个fd可用nReady,err:syscall。EpollWait(epfd,events,1)iferr!nil{log。Printf(epollwaiterrorv,err)panic(fmt。Errorf(epollwaiterrorv,err))}fori:0;inReady;i{ev:events〔i〕ifisError(ev。Events){Anerrorhasoccuredonthisfd,orthesocketisnotreadyforreading(whywerewenotifiedthen?)log。Printf(epollerror:s,err)取消监听syscall。EpollCtl(epfd,syscall。EPOLLCTLDEL,int(ev。Fd),ev)syscall。Close(int(events〔i〕。Fd))continue}ifev。Fdint32(sockfd){for{监听套接字(server端套接字clientfd,,err:syscall。Accept(sockfd)iferrsyscall。EAGAINerrsyscall。EWOULDBLOCK{所有新创建的tcpconn均已被处理break}设置为nonblockiferr:syscall。SetNonblock(clientfd,true);err!nil{log。Printf(failstosetclientsocketvasnonblock,errs,clientfd,err)continue}epEvent。Fdint32(clientfd)epEvent。Eventsuint32(syscall。EPOLLIN)uint32(syscall。EPOLLET)iferr:syscall。EpollCtl(epfd,syscall。EPOLLCTLADD,clientfd,epEvent);err!nil{log。Printf(registerclientsocketvfails,errs,clientfd,err)syscall。Close(clientfd)continue}}}else{已连接套接字tcpconnfor{nRead,err:syscall。Read(int(ev。Fd),buf〔:〕)iferrsyscall。EAGAINerrsyscall。EWOULDBLOCK{数据已经读完了break}elseiferr!nil{log。Printf(failstoreaddatafromsockfdd,errv,ev。Fd,err)syscall。EpollCtl(epfd,syscall。EPOLLCTLDEL,int(ev。Fd),ev)syscall。Close(int(ev。Fd))break}elseifnRead0{EOFClientclosedlog。Printf(clientsockdclosed,ev。Fd)syscall。EpollCtl(epfd,syscall。EPOLLCTLDEL,int(ev。Fd),ev)syscall。Close(int(ev。Fd))break}else{log。Printf(readdbytesfromsockd,nRead,ev。Fd)if,err:syscall。Write(int(ev。Fd),buf〔:nRead〕);err!nil{log。Printf(failstowritedatasintosockfdd,errv,buf〔:nRead〕,sockfd,err)}}}}}}}