(一)gof 通过Epoll模型管理连接


当前第2页 返回上一页

而随着linux的更新,epoll对于句柄的承载量变得不再有限制。但是由于linux的兼容性,依然要传入一个大于等于0的数字。

4.保存epFd

在创建成功之后,我们需要将epFd保存下来,因为全局只需要实例化这一次epoll对象,以后的所有操作都是基于该对象完成的。
创建一个结构体,在其中包含以下内容:

type EpollObj struct {
    socket    int        //socket连接
    epId      int        //epoll 创建的唯一描述符
    ip        string     //socket监听的地址
    port      int        //socket监听的端口
    eventPool *sync.Pool //接收epoll消息
}

socket就是我们创建的GlobalFd句柄。
epId就是我们的Epoll对象的句柄。
ip和port是我们在实例化时需要传入的ip和端口。
eventPoll在接收消息的时候才会用到,之后再详细说明。

5.实现epoll对象中句柄的添加、删除以及消息通知的方法

5.1 实现epoll对象的添加方法。

添加方法是通过syscall中的EpollCtl方法来完成的。

//EpollADD方法,添加、删除监听的fd
//fd 需要监听的fd对象
//status syscall.EPOLL_CTL_ADD添加
func (e *EpollObj) eAdd(fd int) {
    //通过EpollCtl将epfd加入到Epoll中,去监听
    if err := syscall.EpollCtl(
                            e.epId, 
                            syscall.EPOLL_CTL_ADD, 
                            fd, 
                            &syscall.EpollEvent{Events: EPOLLLISTENER, Fd: int32(fd)}); err != nil {
        Log.Error("epoll_ctl add err:%+v,fd:%+v", err, fd)
        os.Exit(1)
    }
}

第一个参数是我们的Epoll对象的句柄。
第二个参数是我们的操作方式,添加方法为:syscall.EPOLL_CTL_ADD,
第三个参数是我们需要加入到Epoll模型中的句柄。
第四个参数是一个EpollEvent,其中第一个参数的 EPOLLLISTENER 是指我们需要对于哪些类型的句柄进行监听,第二个参数为当前的fd。

EPOLLLISTENER = syscall.EPOLLIN | syscall.EPOLLPRI | syscall.EPOLLERR | syscall.EPOLLHUP | unix.EPOLLET

其中,各个参数的意义为:

EPOLLIN 有可读数据到来。
EPOLLOUT 有数据要写。
EPOLLERR 该文件描述符发生错误。
EPOLLHUP 该文件描述符被挂断。常见 socket 被关闭(read == 0)。
EPOLLRDHUP 对端已关闭链接,或者用 shutdown 关闭了写链接。
EPOLLEXCLUSIVE 唯一唤醒事件,主要为了解决 epoll_wait 惊群问题。多线程下多个 epoll_wait 同时等待,只唤醒一个 epoll_wait 执行。 该事件只支持 epoll_ctl 添加操作 EPOLL_CTL_ADD。
EPOLLET 边缘触发模式。

5.2 数据删除操作

直接上代码:

// syscall.EPOLL_CTL_DEL删除
func (e *EpollObj) eDel(fd int) {
    //通过EpollCtl将epfd加入到Epoll中,去监听
    if err := syscall.EpollCtl(
                            e.epId, 
                            syscall.EPOLL_CTL_DEL, 
                            fd, 
                            &syscall.EpollEvent{Events: EPOLLLISTENER, Fd: int32(fd)}); err != nil {
        Log.Error("epoll_ctl del err:%+v,fd:%+v", err, fd)
        os.Exit(1)
    }
}

删除操作中只是将syscall.EpollCtl 的第二个参数更改为:syscall.EPOLL_CTL_DEL。

5.3 接收消息操作

在接收Epoll消息的过程中,我们需要通过 syscall.EpollWait来进行。
该方法是一个阻塞的方法,只有当Epoll检测到某个fd中有内容的时候,才会推送给我们。
但是该方法并不是完全的事件通知模式,因此需要我们手动去获取Epoll对象中的内容。
获取的流程为:
1、创建一个syscall.EpollEvent的切片,在这里我们通过缓冲池的方式来进行创建,这样的好处是无需每次进来都分配一块内存,减少系统gc。
2、通过syscall.EpollWait方法来获取当前是否有新的消息。该方法返回两个参数,第一个参数为需要处理的连接个数,第二个为错误信息。如果第一个参数的值大于0,那么我们就可以判定当前epoll中一定是有新消息的。因为epoll每次返回的消息是一个数组,因此我们需要进行循环处理。
3、如果新消息的fd是GlobalFd,那么它就是一个新的连接,否则,就是用户的连接中有了新的内容。我们需要分别进行操作。

/*******************************************************************/
//ConnStatus 是一个枚举类型,定义在common.go中。
//type ConnStatus int
//const (
//  CONN_NEW     ConnStatus = 1 //新连接
//  CONN_CLOSE   ConnStatus = 2 //关闭连接
//  CONN_MESSAGE ConnStatus = 3 //处理消息
//)
/*******************************************************************/
func (e *EpollObj) eWait(handle func(fd int, connType ConnStatus)) error {
    events := e.eventPool.Get().([]syscall.EpollEvent)
    defer func() {
        events := make([]syscall.EpollEvent, 1024)
        e.eventPool.Put(events)
    }()
    n, err := syscall.EpollWait(e.epId, events[:], -1)
    if err != nil {
        Log.Error("epoll_wait err:%+v", err)
        return err
    }
    if n > 0 {
        fmt.Printf("events fds :%+v\n", events[:5])
    }
    for i := 0; i < n; i++ {
        //如果是系统描述符,就建立一个新的连接
        connType := CONN_MESSAGE //默认是读内容
        if int(events[i].Fd) == e.socket {
            connType = CONN_NEW
        }
        handle(int(events[i].Fd), connType)
    }
    return nil
}

通过这三个函数,我们可以实现对于句柄的添加、删除还有接收消息的操作。


本文来自:简书

感谢作者:公式般欢笑

查看原文:(一)gof 通过Epoll模型管理连接

返回前面的内容

相关阅读 >>

Go 中使用控制流

Golang map需要make吗

slice

Go select

这可能是最容易理解的 Go mutex 源码剖析

Golang开发用什么ide

redis的bitmap如何在Golang中使用

Go安装各种第三方库、包的命令

Go - 实现项目内链路追踪(二)

Go语言 Goto 语句

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...