其他分享
首页 > 其他分享> > [netplus]初见,Netplus快速开始之PingPong Example

[netplus]初见,Netplus快速开始之PingPong Example

作者:互联网

继上篇 初心 已一星期,我们仍尚未谋面,我已迫不及待,是否还记得我曾经对你的承诺,我说,要让人人能编写高性能网络服务器,当然,这只是我一相情愿的告白,我不知道有没有被你看上,也不知道你是否还愿意在这条道上与我走一走,我们一起谈一谈,未来…

看起来这是一个梦啊,梦是当不得真的,但梦想还是可以做做的。

在这所有的所有的一切开始之前,我们还是不落俗套地见上一见吧,你说,自古套路得人心,而大家又总倾心于Hello World,要不,我们换一个,Ping Pong可好。

请你忘记你关于网络编程的想象,保留那么一点点CPP的印象,因为,好的忘记,是新的开始,而CPP正是我们的主角。

要懂的真的不多,稍微去了解一下,你甚至可能会怀疑以前的自己是不是走了很多弯路,虽然我们需要学习的概念并不多,但,还是有那么一点点,我们开始吧。


1. 网络中信息传递的基本问题

没错,网络信息传递,只有两个基本问题,即建立连接,然后传递消息。 而且这两项的工作,都已经被各操作系统实现好了,各家的技术细节并不一样,暴露出来的接口虽有相似之处,但仍然难以做到一份代码,到处运行。一些代码库,如libuv, libevent,在事件通知及系统兼容上面做得非常优秀,然,从实际应用开发者的角度来讲,它们遗留了很多需要开发者自己去解决的问题,如跨平台的一些细节,IO事件的处理,线程安全及性能调试,应用层协议的解析,而这些问题都涉及更多的知识点以及编程经验,这就是门槛啊,任何试图降低门槛的努力都是有价值的,也肯定是值得的。

Netplus试图往前走一步,将平台相关,IO事件处理,线程相关,性能相关,这些各APP里面都需要考虑的共性,进行抽象、封装。提供一个可扩展的机制,让各APP能通过为Channel添加自定义的Handler,在Handler里面做APP自己的协议解析,生成,以及业务处理,通过这种方式,APP开发者,只需要专注于自己的业务本身,就能轻松开发出基于网络通迅的应用程序。

同时,鉴于某些协议的流行程度,为了便于快速开发,Netplus也提供了直接的支持,如http/https,websocket,当然,也欢迎各位朋友为其添加其它的协议,让Netplu日渐丰满,我们的目标始终是,开箱即用,统统一把梭。

Netplus借鉴了netty的设计,那些熟悉 netty的朋友,就算不熟悉c++,应该也能快速上手。

2. Netplus里的基本概念

2.1 netp::ref_base & netp::ref_ptr

下面是一个关于ref_base, ref_ptr如何使用example

class ref_example: public netp::ref_base
{
 int number;
 public:
 ref_example(int i):number(i){}
 
 int get_number() {return number;}
 void set_number(int i) {number=i;}
};
 
//创建,记住,不能直接new/delete, 只能借助 netp::make_ref 函数;
netp::ref_ptr<ref_example> ref_example_1 = netp::make_ref<ref_example>(10);
 
//可像裸指针一样访问对象
ref_example_1 ->set_number(10);
int number_value = ref_example_1 ->get_number();
 
//赋值,它是线程安全的
netp::ref_ptr<ref_example> ref_example_2 = ref_example_1;

{
//它是容器安全的,可在各容器里面自由存储
   std::vector<netp::ref_ptr<ref_example>> ref_example_vector; 
   ref_example_vector.push_back(ref_example_2);
}

//比较,注意,这里比较的是指向的地址
if( ref_example_2 == ref_example_1 ) {
//比较两个ref_ptr对象是否指向同一个对象
}

//ref_ptr对象的大小 == 机器的地址宽度,试想一下,将有何好处?
//sizeof(netp::ref_ptr<ref_example>) == sizeof(int*);

//销毁, 不需要的时候,我们直接置nullptr即可
ref_example_1 = nullptr;
ref_example_2 = nullptr;

//借助于C++ RAII特性,局部变量,我们甚至都不用去置nullptr
{
    netp::ref_ptr<ref_example> ref_example_3 = netp::make_ref<ref_example>(10);
//离开此作用域后, ref_example_3指向的内存将自动被释放
}

2.2 Packet

//创建一个packet对象
netp::ref_ptr<netp::packet> p = netp::make_ref<netp::packet>();

//写入一个字符串
p->write("hello", netp::strlen("hello") );

//将字符串读入buf
//注:byte_t 实为unsigned char
netp::byte_t hello_buf[10]={0};
netp::u32_t read_count = p->read(hello_buf, 10);

//在packet buf的左边写入一个u32_t大小的整数 
p->write_left<netp::u32_t>(103);

//将刚刚写入的整数读入i103
netp::u32_t i103 = p->read<u32_t>();

//销毁packet对象
p = nullptr;

2.3 Channel

socket read -> channel -> tail_handler -> handler1 -> handler2 -> ...
head_handler -> ... -> handler2 -> handler1 -> tail_handler -> channel -> socket write

2.3 Channel Handler

void read(netp::ref_ptr<netp::channel_handler_context> const& ctx, netp::ref_ptr<netp::packet> const& income);
void connected(netp::ref_ptr<netp::channel_handler_context> const& ctx);

3. Netplus收发消息的基本流程

3.1 启动一个服务 (server端):

  1. 实现自己的Channel Handler
  2. 在ip:port处监听服务
  3. 当Accept成功新的Channel后,为Channel添加Channel Handler

3.2 连接一个服务(client端):

  1. 实现自己的Channel Handler
  2. 拨号至ip:port
  3. 当拨号成功时,为Channel添加Channel Handler

好了,就这些东西,没有更多了。

4. PINGPONG

4.1 PINGPONG服务器

  1. 监听在tcp://127.0.0.1:13103端口
  2. 收到到来自远端的的连接的时候,为Channel添加一个Pong Handler
  3. Pong Handler: 此Handler只做一个事情,当收到来自客户端的消息后,回复PONG,代码如下:
class Pong :
	public netp::channel_handler_abstract {
public:
	Pong() : 
		channel_handler_abstract(netp::CH_INBOUND_READ)
	{}
	//for inbound
	void read(netp::ref_ptr<netp::channel_handler_context> const& ctx, netp::ref_ptr<netp::packet> const& income) {
		//reply with PONG
		const std::string pong = "PONG";
		netp::ref_ptr<netp::packet> PONG = netp::make_ref<netp::packet>(pong.c_str(), pong.length());
		netp::ref_ptr<netp::promise<int>> write_promise = ctx->write(PONG);

		//check the reply status once the write operation is done
		write_promise->if_done([](int reply_rt) {
			NETP_INFO("[PONG]reply PONG, rt: %d", reply_rt );
		});
	}
};

4.2 PINGPONG客户端

  1. 拨号到tcp://127.0.0.1:13103端口
  2. 当拨号成功之后,为Channel添加一个Ping Handler
  3. Ping Handler: 当连接成功时,向服务器发送PING, 当成功收到回复的消息(PONG)后,继续发送PING,代码如下:
class Ping : 
	public netp::channel_handler_abstract {
public:
	Ping():
		channel_handler_abstract(netp::CH_ACTIVITY_CONNECTED|netp::CH_INBOUND_READ)
	{}
	void connected(netp::ref_ptr<netp::channel_handler_context> const& ctx) {
		NETP_INFO("[PING]connected");
		//initial PING
		do_ping(ctx);
	}
	void read(netp::ref_ptr<netp::channel_handler_context> const& ctx, netp::ref_ptr<netp::packet> const& income) {
		NETP_INFO("[PING]reply income");
		do_ping(ctx);
	}
	void do_ping(netp::ref_ptr<netp::channel_handler_context> const& ctx) {
		const std::string ping = "PING";
		netp::ref_ptr<netp::packet> message_ping = netp::make_ref<netp::packet>();
		message_ping->write(ping.c_str(), ping.length());
		netp::ref_ptr<netp::promise<int>> write_p = ctx->write(message_ping);
		write_p->if_done([]( int rt ) {
			NETP_INFO("[PING]write PING, rt: %d", rt );
		});
	}
};

 

你看,服务器,客户端,都是三步曲

  1. 实现handler
  2. 监听/拨号
  3. 设置handler

So easy!

4.3 PING PONG的总体执行逻辑

  1. 服务器监听tcp://127.0.0.1:13103
  2. 当服务器有新的channel连接进来时,为新的channel添加handler
  3. 客户端拨号到tcp://127.0.0.1:13103
  4. 客户端拨号成功后,添加handler

4.4 main.cpp 完整代码如下:

int main(int argc, char** argv) {
	//initialize a netplus app instance
	netp::app app;
	std::string host = "tcp://127.0.0.1:13103";

	netp::ref_ptr<netp::channel_listen_promise> listenp = netp::socket::listen_on(host, [](netp::ref_ptr<netp::channel>const& ch) {
		ch->pipeline()->add_last( netp::make_ref<netp::handler::hlen>());
		ch->pipeline()->add_last( netp::make_ref<Pong>() );
	});
	int listenrt = std::get<0>(listenp->get());
	if (listenrt != netp::OK) {
		NETP_INFO("listen on host: %s failed, fail code: %d", host.c_str(), listenrt);
		return listenrt;
	}

	netp::ref_ptr<netp::channel_dial_promise> dialp = netp::socket::dial(host, [](netp::ref_ptr<netp::channel> const& ch ) {
		ch->pipeline()->add_last( netp::make_ref<netp::handler::hlen>() );
		ch->pipeline()->add_last( netp::make_ref<Ping>() );
	});
	int dialrt = std::get<0>(dialp->get());
	if (dialrt != netp::OK) {
		//close listen channel and return
		std::get<1>(listenp->get())->ch_close();
		return dialrt;
	}

	//wait for signal to exit
	//Ctrl+C on windows
	//kill -15 on linux
	app.run();

	//close listen channel
	std::get<1>(listenp->get())->ch_close();

	//close dial channel
	std::get<1>(dialp->get())->ch_close();
	return 0;
}

 

4.5 代码解读

4.5.1 netp::app

所有的netplus应用,netp::app app总是第一行代码,app实例代表着一个netplus对象,用处初始化netplus系统,设置信号处理。

app.run() 等待退出信号

4.5.2 Channel Handler hlen

 

 

(此篇文章待进一步整理)

完整的工程地址如下:https://link.zhihu.com/?target=https%3A//github.com/netplus/netplus/tree/main/test/pingpong

 

知识库:https://link.zhihu.com/?target=https%3A//github.com/netplus/netplus/wiki

 

 

如果你喜欢我的文章,请加个关注,点个赞,谢谢。

如果你有其它相关知识想要了解的,请直接,可以给我留言。

 

写代码的冰冰

姑苏城里平江路,入夜细雨扰我心,再会。

 

标签:netplus,PingPong,example,Netplus,Handler,ref,ptr,Channel,netp
来源: https://blog.csdn.net/ycopy2515/article/details/114217106