分为UdpServer类和UdpConn类。

  1. struct UdpServer : public std::enable_shared_from_this<UdpServer>, private noncopyable {
  2. UdpServer(EventBases* bases);
    int bind(const std::string& host, unsigned short port, bool reusePort = false);
    static UpdServerPtr startServer(EventBases* bases, const std::string& short, unsigned short port, bool reusePort = false);
    ~UdpServer() {
    delete channel_;
    }
    Ip4Addr getAddr() {
    return addr_;
    }
    EventBase* getBase() {
    return base_;
    }
    void sendTo(Buffer msg, Ip4Addr addr) {
    sendTo(msg.data(), msg.size(), addr);
    msg.clear();
    }
    void sendTo(const char* buf, size_t len, Ip4Addr addr);
    void sendTo(const std::string& s, Ip4Addr addr) {
    sendTo(s.data(), s.size(), addr);
    }
    void sendTo(const char* s, Ip4Addr addr) {
    sendTo(s, strlen(s), addr);
    }
  3.  
  4. void onMsg(const UdpSvrCallBack& cb) {
    msgcb_ = cb;
    }
  5.  
  6. private:
    EventBase* base_;
    EventBases* bases_;
    Ip4Addr addr_;
    Channel* channel_;
    UdpSvrCallBack msgcb_;
  7. };

其中:

  1. typedef std::shared_ptr<UdpConn> UdpConnPtr;
  2. typedef std::shared_ptr<UdpServer> UdpServerPtr;
  3. typedef std::function<void(const UdpConnPtr&, Buffer)> UdpCallBack;
  4. typedef std::function<void(const UdpServerPtr&, Buffer, Ip4Addr)> UdpSvrCallBack;
  1. struct UdpConn : public std::enable_shared_from_this<UdpConn>, private noncopyable {
  2. UdpConn() {}
  3. virtual ~UdpConn() { close(); }
  4. static UdpConnPtr createConnection(EventBase* base, const std::string& host, unsigned short port);
  5.  
  6. template <class T>
  7. T& context() {
  8. return ctx_.context<T>();
  9. }
  10.  
  11. EventBase* getBase() {
  12. return base_;
  13. }
  14.  
  15. Channel* getChannel() {
  16. return channel_;
  17. }
  18.  
  19. void send(Buffer msg) {
  20. send(msg.data(), msg.size());
    msg.clear();
    }
    void send(const char* buf, size_t len);
    void send(const std::string& s) {
    send(s.data(), s.size());
    }
    void send(const char* s) {
    send(s, strlen(s));
    }
    void onMsg(const UdpCallBack& cb) {
    cb_ = cb;
    }
    void close();
  21.  
  22. std::string str() {
    return peer_.toString();
    }
  23.  
  24. public:
    void handleRead(const UdpConnPtr&);
    EventBase* base_;
    Channel* channel_;
    Ip4Addr local_, peer_;
    AutoContext ctx_;
    std::string destHost_;
    int destPort_;
    UdpCallback cb_;
  25. };

下面看一下具体的使用方法,从中可以得知udp相关类的设计及实现的想法。

udp服务器部分:

  1. int main(int argc, const char* argv[]) {
  2. EventBase base;
  3. Signal::signal(SIGINT, [&] { base.exit(); });
  4. UdpServerPtr svr = UdpServer::startServer(&base, "", );
  5. svr->onMsg([](const UdpServerPtr& p, Buffer buf, Ip4Addr peer) {
  6. p->sendTo(buf, peer);
  7. });
  8. base.loop();
  9. return ;
  10. }

可以看出,在指定端口启动服务器后,回调函数被加入到事件循环中去,意为当收到客户端的消息时,调用onMsg设置的回调函数,相应的会调用sendto,向客户端发送一条消息。base.loop()的作用即为启动该事件循环,具体的为调用loop_once进而调用Poller的loop函数,进而会调用epoll_wait去监听套接字上的可读事件。

udp客户端部分:

  1. int main(int argc, const char* argv[]) {
  2. EventBase base;
  3. Signal::signal(SIGINT, [&] { base.exit(); });
  4. UdpConnPtr con = UdpConn::createConnection(&base, "127.0.0.1", );
  5. con->onMsg([](const UdpConnPtr& p, Buffer buf) { info("udp recved %lu bytes", buf.size()); });
  6. base.runAfter(, [=]() { con->send("hello"); }, );
  7. base.loop();
  8. return ;
  9. }

在客户端部分,首先连接到服务器端口,然后设置回调函数,此处的base.loop()不光有上述的调用epoll函数的功能,还有定时调用runAfter中函数对象的功能。

总的过程为:启动服务端和客户端后,base.loop()进行事件循环,客户端的runAfter会定时向服务端发送消息,服务器端在收到该消息后,会调用onMsg设定的回调函数,该回调函数是向客户端发送消息,客户端在收到消息后,将其打印出来。

下面说一下如何将回调函数加入时间循环。

其实可以看一下Channel类的实现,Channel类内置了一个文件描述符,每创建一个新的Channel对象,都会将该文件描述符及其监听的事件类型加入到epoll事件循环中,调用对应事件类型的函数,只要在此函数中加入会回调函数的调用,就可以实现上述所说的功能。

下面是服务器和客户端的相应部分的实现(简化):

  1. int UdpServer::bind(const std::string& host, unsigned short port, bool reusePort) {
    addr_ = Ip4Addr(host, port);
    ::bind(fd, (struct sockaddr*)&addr_.getAddr(), sizeof(struct sockaddr));
    channel_ = new Channel(base_, fd, kReadEvent); //创建Channel对象,将服务器端的套接字传入。
    channel_->onRead([this] { //在epoll_wait读到读事件时,会调用onRead设置的函数。
    Buffer buf;
    struct sockaddr_in raddr;
    socklen_t rsz = sizeof(raddr);
    int fd = channel_->fd;
    ssize_t rn = recvfrom(fd, buf.makeRoom(kUdpPacketSize), kUdpPacketSize, 0, (sockaddr*)&raddr, &rsz); //接收消息。
    buf.addSize(rn);
    this->msgcb_(shared_from_this(), buf, raddr); //此为回调函数的调用,在文件中可以看到msgcb_由onMsg设置,就可以实现由自己调用函数onMsg设置回调函数加入到事件循环进行调用。
    });
  2. }

客户端的部分实现(简化):

  1. UdpConnPtr udpConn::createConnection(EventBase* base, const string& host, unsigned short port) {
    Ip4Addr addr(host, port);
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    ::connect(fd, (sockaddr*)&addr.getAddr(), sizeof(sockaddr_in));
    UdpConnPtr con(new UdpConn);
    Channel* ch = new Channel(base, fd, kReadEvent); //创建Channel对象,将客户端套接字传入。
    conn->channel_ = ch;
    ch->onRead([con] { //在epoll_wait读到读事件时,会调用onRead设置的函数。
    Buffer input;
    int fd = con->channel_->fd();
    int rn = ::read(fd, input.makeRoom(kUdpPacketSize), kUdpPackeSize);
    input.addSize(rn);
    con->cb_(con, input); //回调函数的调用,头文件中可以看到cb_由onMsg设置。
    });
  2. }

handy源码阅读(六):udp类的更多相关文章

  1. handy源码阅读(六):tcp类

    首先是tcpconn和tcpserver类: struct TcpConn : public std::enable_shared_from_this<TcpConn>, private ...

  2. Spark常用函数(源码阅读六)

    源码层面整理下我们常用的操作RDD数据处理与分析的函数,从而能更好的应用于工作中. 连接Hbase,读取hbase的过程,首先代码如下: def tableInitByTime(sc : SparkC ...

  3. handy源码阅读(四):Channel类

    通道,封装了可以进行epoll的一个fd. struct Channel: private noncopyable { Channel(EventBase* base, int fd, int eve ...

  4. handy源码阅读(三):SafeQueue类

    SafeQueue类继承与信号量mutex(用于加锁),nonocopyable 定义如下: template <typename T> struct SafeQueue : privat ...

  5. handy源码阅读(二):EventsImp类

    EventsImp用于完成事件的处理. class EventsImp { EventBase* base_; PollerBase* poller_; std::atomic<bool> ...

  6. handy源码阅读(一):EventBase类

    类EventBase继承于类EventBases,继承于noncopyable.  其中noncopyable是一个去除了拷贝构造和赋值构造的类. noncopyable: class noncopy ...

  7. handy源码阅读(五):PollerBase类

    使用poll内核函数等待事件发生: struct PollerBase: private noncopyable { int64_t id_; int lastActive_; PollerBase( ...

  8. JDK源码阅读:Object类阅读笔记

    Object 1. @HotSpotIntrinsicCandidate @HotSpotIntrinsicCandidate public final native Class<?> g ...

  9. klee源码阅读笔记1--STPBuilder类

    初始化过程中四个数据成员中的两个数据成员被初始化: 一.vc被初始化为STP提供的C调用接口函数vc_createValidityChecker(): 二.optimizeDivides被初始化为fa ...

随机推荐

  1. proteus 与 keil 的安装及联调

    proteus 安装 Win10 系统的下载链接可以参考这里:https://tieba.baidu.com/p/5644915130?traceid= 百度网盘地址 链接1: http://pan. ...

  2. FiddlerCore修改http返回结果

    static void FiddlerApplication_BeforeRequest(Session oSession) { oSession.bBufferResponse = true; } ...

  3. WEB框架实战总结

    Django 在新一代的 Web框架 中非常出色 使用Python开发Web,最简单,原始和直接的办法是使用CGI标准,可以用WSGI接口 一.WSGI接口实现web页面 运行WSGI服务 我们先编写 ...

  4. 【Linux开发】如何更改linux文件的拥有者及用户组(chown和chgrp)

    本文整理自: http://blog.163.com/yanenshun@126/blog/static/128388169201203011157308/ http://ydlmlh.iteye.c ...

  5. 第十七周周总结 Swing

    考试系统 1.登录功能 用户和密码存在在哪里? 文件 2.考试功能 考试题目和答案存在哪? 文件 3.展示功能 GUI Graphical User Interface图形用户接口 #GUI Java ...

  6. [转帖]PostgreSQL ident和peer基于操作系统用户的认证

    PostgreSQL ident和peer基于操作系统用户的认证 https://yq.aliyun.com/articles/55898 其实 local和127. 还是有区别的 这里面应该就是对应 ...

  7. [转帖]使用ping钥匙临时开启SSH:22端口,实现远程安全SSH登录管理就这么简单

    使用ping钥匙临时开启SSH:22端口,实现远程安全SSH登录管理就这么简单 https://www.cnblogs.com/martinzhang/p/5348769.html good good ...

  8. 在git bash 中配置git用户名和邮箱及查看配置信息

    Administrator@LuoTong- MINGW32 ~ $ git config --global user.name "mrluotong" Administrator ...

  9. docker配置文件不生效

    1.查看docker配置文件位置 systemctl status docker.service 2.修改docker配置文件 vim /lib/systemd/system/docker.servi ...

  10. [Codeforces 1214A]Optimal Currency Exchange(贪心)

    [Codeforces 1214A]Optimal Currency Exchange(贪心) 题面 题面较长,略 分析 这个A题稍微有点思维难度,比赛的时候被孙了一下 贪心的思路是,我们换面值越小的 ...