Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

workflow http server在公网,client终端设备在私网。当建立http长连接以后,server如何主动给client发消息? #929

Closed
dtiny opened this issue Jun 2, 2022 · 4 comments

Comments

@dtiny
Copy link

dtiny commented Jun 2, 2022

你好!现在遇到一个例外的问题,描述如下:
问题描述:workflow是一款高效http服务器,一般http server主要是这样的一个处理流程:先与客户端建立连接、接收客户端发送的请求,最后给客户端返回请求响应。现在的需求是,在满足以上流程功能外,在与客户端建立长连接和保持心跳以后,还要求http server 能够通过长连接主动的给client发送消息。
场景描述:http server在公网,http client 在私网。私网可以找到公网server ip,所以client可以主动发起与server的长连接请求,并建立长连接,client可以向server发送数据和心跳等请求,也能正常收到server的响应。但是现在server需要给client发送消息命令(http client支持的命令),由于client在私网无法找到client 的ip,所以如何通过server 与client的长连接给client发送消息命令。
client限制:由于client终端设备出厂设定,所以无法更改连接方式。现在只支持http一种连接方式,不支持websocket等连接。
OS:Linux
语言:C++
盼解决,谢谢!

@Barenboim
Copy link
Contributor

Barenboim commented Jun 2, 2022

你好。http client应该不是workflow的吧?
http协议本身不支持server主动发请求。但我们的server支持一个push接口,可以在一次交互之内,向client同步的多次推送数据。例如,想每1秒向client发送一块数据,可以用下面的方法:

void timer_callback(WFTimerTask *timer)
{
    SeriesWork *series = series_of(timer);
    WFHttpTask *server_task = (WFHttpTask *)series->get_context();
    server_task->push("4\r\ntest\r\n", strlen(" ... "));  // chunked编码的数据块
    timer = WFTaskFactory::create_timer_task(1, 0, timer_callback);
    series->push_back(timer);
}

void process(WFHttpTask *server_task)
{
    SeriesWork *series = series_of(server_task);
    series->set_context(server_task);
    server_task->push("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n", strlen(" ... "));  // 需要自行构建http header
    WFTimerTask *timer = WFTaskFactory::create_timer_task(1, 0, timer_callback);
    server_task->noreply(); // 无需发送原来的http response了
    series->push_back(timer);
}

这个就实现了server向client定期发送数据。但注意push接口是一个同步调用,如果数据超过tcp send buf大小,push返回实际写入的数据,而无法自动进行异步写。
在这个模式里,client的请求已经发送完毕,所以,整个处理过程中client不可能再写server发送数据了,否则已经超过了http协议支持的范围了。

@dtiny
Copy link
Author

dtiny commented Jun 4, 2022

谢谢回复!
首先补充一下,http client不是workflow 的client。这个是终端设备的一个基于tcp长连接私有协议,支持设备数据向server的推送和接收server下发的控制信息。
对于您的回复,有几点想跟您确认一下。
1、一次交互之内是不是指在process()函数的回调结束之前,process()函数回调结束了也意味着本次的交互结束,如果要想一直保持这种长连接交互,就要阻塞回调的结束?
2、可不可以在有需求的再向client发送信息,而不是采用定时任务。
3、workflow server有没有接口提供创建连接的控制,我意思是在与client创建连接的时候,将需要控制的长连接保存下来,不关闭连接,在有需求的时候通过长连接将信息发送给client。

@Barenboim
Copy link
Contributor

谢谢回复! 首先补充一下,http client不是workflow 的client。这个是终端设备的一个基于tcp长连接私有协议,支持设备数据向server的推送和接收server下发的控制信息。 对于您的回复,有几点想跟您确认一下。 1、一次交互之内是不是指在process()函数的回调结束之前,process()函数回调结束了也意味着本次的交互结束,如果要想一直保持这种长连接交互,就要阻塞回调的结束? 2、可不可以在有需求的再向client发送信息,而不是采用定时任务。 3、workflow server有没有接口提供创建连接的控制,我意思是在与client创建连接的时候,将需要控制的长连接保存下来,不关闭连接,在有需求的时候通过长连接将信息发送给client。

我回复的这个示例,就是解决了你的这几个问题啊😂
首先,不是不让process函数结束。workflow server处理逻辑的结束,并不是process函数完成,而是series里没有任务了。上面timer的示例,就是在series里不断添加timer,达到定时发数据的效果。
如果你并非定时,只要是异步过程,都可以包装成workflow的任务,放进series里。对于非本框架的异步过程,也可以配合counter来实现。counter的文档有描述这个方法。
只要series没结束,就相当于你持有这个连接了。

@Barenboim
Copy link
Contributor

@dtiny

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants