分为UdpServer类和UdpConn类。

struct UdpServer : public std::enable_shared_from_this<UdpServer>, private noncopyable {
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);
} void onMsg(const UdpSvrCallBack& cb) {
msgcb_ = cb;
} private:
EventBase* base_;
EventBases* bases_;
Ip4Addr addr_;
Channel* channel_;
UdpSvrCallBack msgcb_;
};

其中:

typedef std::shared_ptr<UdpConn> UdpConnPtr;
typedef std::shared_ptr<UdpServer> UdpServerPtr;
typedef std::function<void(const UdpConnPtr&, Buffer)> UdpCallBack;
typedef std::function<void(const UdpServerPtr&, Buffer, Ip4Addr)> UdpSvrCallBack;
struct UdpConn : public std::enable_shared_from_this<UdpConn>, private noncopyable {
UdpConn() {}
virtual ~UdpConn() { close(); }
static UdpConnPtr createConnection(EventBase* base, const std::string& host, unsigned short port); template <class T>
T& context() {
return ctx_.context<T>();
} EventBase* getBase() {
return base_;
} Channel* getChannel() {
return channel_;
} void send(Buffer msg) {
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(); std::string str() {
return peer_.toString();
} public:
void handleRead(const UdpConnPtr&);
EventBase* base_;
Channel* channel_;
Ip4Addr local_, peer_;
AutoContext ctx_;
std::string destHost_;
int destPort_;
UdpCallback cb_;
};

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

udp服务器部分:

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

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

udp客户端部分:

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

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

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

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

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

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

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设置回调函数加入到事件循环进行调用。
});
}

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

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设置。
});
}

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. 阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第3节 注解_18_注解_案例_简单的测试框架

    定义计算器的类 用注解的方式去测试计算器类里面 所有的方法 想验证哪个方法 就在方法的上面加上注解@check 执行TestCheck验证方法 控制台的输出 根目录生成了一个 bug.txt文件 重写 ...

  2. Shiro Demo 示例(SpringMVC-Mybatis-Shiro-redis)

    Shiro Demo 准备工作 运行前申明 请看完本页面的所有细节,对你掌握这个项目来说很重要,别一上来就搞,你不爽,我也不爽. 本项目需要一定的Java功底,需要对SpringMvc,Mybatis ...

  3. mybatisProxy

    config.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configurati ...

  4. dotnet core排序异常,本地测试和linux上结果不一致

    根据汉字排序,本地测试结构正常,发到docker之后,发现汉字升序降序和本地相反,检查代码后,没找到任何可能出现问题的点. 然后去翻文档:字符串比较操作 看到了这一句,会区分区域性 然后猜测应该是do ...

  5. Linux 系统安装 python

    Centos 7 Centos 7 安装 python3 (不要卸载python2 因为yum 要用) https://phoenixnap.com/kb/how-to-install-python- ...

  6. 使用powershell管理Docker Toolbox

    打开PowerShell,输入: docker-machine ls 我们可以看到我们当前的Docker虚拟机的状态.如果什么都没有的话,那么我们可以使用以下命令创建一个Docker虚拟机. dock ...

  7. jQuery中this与$(this)的区别总结

    这里就谈谈this与$(this)的区别. 1.jQuery中this与$(this)的区别 $("#textbox").hover( function() { this.titl ...

  8. 创建React脚手架

    node版本10.14.2 下载地址 如果是其版本的话会出错 css-loader 会不兼容 主要是8.x的版本不兼容 npm install -g create-react-app 全局安装 cre ...

  9. 主机(windows10)虚拟机(ubuntu18)arm板(linux3.4)相互ping通

    实际中在主机上安装虚拟机,并在主机上通过网线连接arm板进行调试. 用网线将主机和arm板直接物理连接,且主机和arm必须处于同一个网段.(我们知道主机中的网卡具有路由器的功能) 其中arm板IP地址 ...

  10. A+B and A*B problem 大数相加 相乘 模拟

    A+B and A*B problem 大数相加 相乘 模拟 题意 给你两个数a和b,这两个数很大,然后输出这两个数相加的和,相乘的积. 解题思路 模拟,但是还是搜了搜代码实现,发现这个大佬写的是真的 ...