C++ 带超时的Asio同步客户端
我正在尝试使用线程作为超时控件,构建一个具有超时的同步FTP客户端代码。线程将在每个事务上启动,并在超时时关闭套接字-这将强制同步调用返回错误 这是我的代码:C++ 带超时的Asio同步客户端,c++,multithreading,boost,boost-asio,C++,Multithreading,Boost,Boost Asio,我正在尝试使用线程作为超时控件,构建一个具有超时的同步FTP客户端代码。线程将在每个事务上启动,并在超时时关闭套接字-这将强制同步调用返回错误 这是我的代码: #include <cstdlib> #include <cstring> #include <iostream> #include <thread> #include <chrono> #include <boost/asio.hpp> #define TIMEO
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <boost/asio.hpp>
#define TIMEOUT_SECONDS 5
#define MAX_MESSAGE_SIZE 4096
using boost::asio::ip::tcp;
enum { max_length = 1024 };
bool timerOn;
void socket_timer(tcp::socket& s, int seconds)
{
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
while (timerOn)
{
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
auto interval = std::chrono::duration_cast<std::chrono::seconds>(now - start).count();
if (interval > seconds)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Not to run in 100% CPU
}
if (timerOn)
s.close();
}
void start_timer(int seconds, tcp::socket& s)
{
timerOn = true;
std::thread t(socket_timer, s, seconds);
t.detach();
}
void stop_timer()
{
timerOn = false;
}
int main(int argc, char* argv[])
{
std::string address;
while(address != "END")
{
try
{
boost::asio::io_service io_service;
std::cout << "Enter FTP server address to connect or END to finish: " << std::endl;
std::cin >> address;
if (address == "END")
break;
tcp::socket s(io_service);
tcp::resolver resolver(io_service);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(address), 21);
start_timer(TIMEOUT_SECONDS, s);
boost::system::error_code ec;
s.connect(endpoint, ec);
stop_timer();
if (ec)
{
throw std::runtime_error("Error connecting to server.");
}
std::cout << "Connected to " << s.remote_endpoint().address().to_string() << std::endl;
char reply[max_length];
start_timer(TIMEOUT_SECONDS, s);
size_t bytes = s.receive(boost::asio::buffer(reply, MAX_MESSAGE_SIZE), 0, ec);
stop_timer();
if (ec)
{
throw std::runtime_error("Error receiving message.");
}
std::cout << "Received message is: ";
std::cout.write(reply, bytes);
std::cout << "\n";
std::cout << "Enter message: ";
char request[max_length];
std::cin.getline(request, max_length);
size_t request_length = std::strlen(request);
start_timer(TIMEOUT_SECONDS, s);
boost::asio::write(s, boost::asio::buffer(request, request_length));
stop_timer();
if (ec)
{
throw std::runtime_error("Error sending message.");
}
}
catch (std::exception& e)
{
std::cerr << "COMMUNICATIONS ERROR." << "\n";
std::cerr << "Exception: " << e.what() << "\n";
}
}
return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#定义超时时间为5秒
#定义最大消息大小4096
使用boost::asio::ip::tcp;
枚举{max_length=1024};
布尔泰姆隆;
无效套接字计时器(tcp::套接字和s,整数秒)
{
std::chrono::system_clock::time_point start=std::chrono::system_clock::now();
while(timerOn)
{
std::chrono::system_clock::time_point now=std::chrono::system_clock::now();
自动间隔=std::chrono::duration_cast(现在-开始).count();
如果(间隔>秒)
打破
std::this_thread::sleep_for(std::chrono::毫秒(10));//不在100%CPU中运行
}
if(timerOn)
s、 close();
}
无效启动计时器(整数秒,tcp::套接字和s)
{
timerOn=true;
std::线程t(套接字计时器,s,秒);
t、 分离();
}
无效停止计时器()
{
timerOn=false;
}
int main(int argc,char*argv[])
{
std::字符串地址;
while(地址!=“结束”)
{
尝试
{
boost::asio::io_服务io_服务;
std::cout地址;
如果(地址=“结束”)
打破
tcp::套接字s(io_服务);
tcp::解析器解析器(io_服务);
boost::asio::ip::tcp::endpoint端点(boost::asio::ip::address::from_string(address),21);
启动计时器(超时秒,s);
boost::system::error_code ec;
s、 连接(端点,ec);
停止计时器();
国际单项体育联合会(欧共体)
{
抛出std::runtime_错误(“连接到服务器时出错”);
}
std::cout with
1> [
1> IoObjectService=boost::asio::流\u套接字\u服务
1> ]
1> 此诊断发生在编译器生成的函数“boost::asio::basic_socket::basic_socket(const boost::asio::basic_socket&)”中
1> 与
1> [
1> 协议=boost::asio::ip::tcp,
1> SocketService=boost::asio::stream\u socket\u服务
1> ]
======生成:0成功,1失败,9最新,0跳过==========
所以,我想知道两件事:
a) 我在代码中做错了什么
b) 这种在平行线程上关闭套接字的方法是否会对套接字超时起作用?请随意评论
感谢您的帮助。我已经制作了一个助手工具,可以在这里以超时“同步”执行任何Asio异步操作,请查找
等待\u操作
:
- 您没有解析地址(实际上需要用户键入IP地址)
- 您没有确保命令已用换行符关闭
- 您没有处理任何类型的输入错误
wait_操作
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#define TIMEOUT std::chrono::seconds(5)
#define MAX_MESSAGE_SIZE 4096
using boost::asio::ip::tcp;
enum { max_length = 2048 };
struct Service {
using error_code = boost::system::error_code;
template<typename AllowTime, typename Cancel> void await_operation_ex(AllowTime const& deadline_or_duration, Cancel&& cancel) {
using namespace boost::asio;
ioservice.reset();
{
high_resolution_timer tm(ioservice, deadline_or_duration);
tm.async_wait([&cancel](error_code ec) { if (ec != error::operation_aborted) std::forward<Cancel>(cancel)(); });
ioservice.run_one();
}
ioservice.run();
}
template<typename AllowTime, typename ServiceObject> void await_operation(AllowTime const& deadline_or_duration, ServiceObject& so) {
return await_operation_ex(deadline_or_duration, [&so]{ so.cancel(); });
}
boost::asio::io_service ioservice;
};
int main()
{
while(true)
{
try
{
Service service;
std::cout << "Enter FTP server address to connect or END to finish: " << std::endl;
std::string address;
if (std::cin >> address) {
if (address == "END") break;
} else {
if (std::cin.eof())
break;
std::cerr << "Invalid input ignored\n";
std::cin.clear();
std::cin.ignore(1024, '\n');
continue;
}
tcp::socket s(service.ioservice);
tcp::resolver resolver(service.ioservice);
boost::asio::async_connect(s, resolver.resolve({address, "21"}), [](boost::system::error_code ec, tcp::resolver::iterator it) {
if (ec) throw std::runtime_error("Error connecting to server: " + ec.message());
std::cout << "Connected to " << it->endpoint() << std::endl;
});
service.await_operation_ex(TIMEOUT, [&]{
throw std::runtime_error("Error connecting to server: timeout\n");
});
auto receive = [&] {
boost::asio::streambuf sb;
size_t bytes;
boost::asio::async_read_until(s, sb, '\n', [&](boost::system::error_code ec, size_t bytes_transferred) {
if (ec) throw std::runtime_error("Error receiving message: " + ec.message());
bytes = bytes_transferred;
std::cout << "Received message is: " << &sb;
});
service.await_operation(TIMEOUT, s);
return bytes;
};
receive(); // banner
auto send = [&](std::string cmd) {
boost::asio::async_write(s, boost::asio::buffer(cmd), [](boost::system::error_code ec, size_t /*bytes_transferred*/) {
if (ec) throw std::runtime_error("Error sending message: " + ec.message());
});
service.await_operation(TIMEOUT, s);
};
auto ftp_command = [&](std::string cmd) {
send(cmd + "\r\n");
receive(); // response
};
//ftp_command("USER bob");
//ftp_command("PASS hello");
while (true) {
std::cout << "Enter command: ";
std::string request;
if (!std::getline(std::cin, request))
break;
ftp_command(request);
}
}
catch (std::exception const& e)
{
std::cerr << "COMMUNICATIONS ERROR " << e.what() << "\n";
}
}
return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义超时标准::计时::秒(5)
#定义最大消息大小4096
使用boost::asio::ip::tcp;
枚举{max_length=2048};
结构服务{
使用error\u code=boost::system::error\u code;
模板无效等待操作(允许时间常数和截止日期或持续时间、取消和取消){
使用名称空间boost::asio;
ioservice.reset();
{
高分辨率定时器tm(iService、截止时间或持续时间);
tm.async_wait([&cancel](错误代码ec){if(ec!=error::operation_aborted)std::forward(cancel)(;});
ioservice.run_one();
}
ioservice.run();
}
模板void wait_操作(允许时间常数和截止时间或持续时间,ServiceObject&so){
return wait_operation_ex(deadline_或_duration,[&so]{so.cancel();});
}
boost::asio::io_服务ioservice;
};
int main()
{
while(true)
{
尝试
{
服务;
std::cout地址){
如果(地址==“结束”)中断;
}否则{
if(std::cin.eof())
打破
std::cerr添加了一个演示。捕获lambda的数量表明有明显的机会创建一个类FtpClient
。此外,如果您想传输任何真实数据,我强烈建议使用适当的异步IO,因为这样会更简单。sehe:
虽然这是一个很好的示例,但这并不能回答我的原始问题问题是因为你把整个代码都变成了异步的。事实上,我正在构建一个FtpClient
类,它需要同步-一个接一个的操作-连接、列出文件、下载文件、发送文件、关闭…用于远程客户端上载和下载数据文件…Mendes:请密切注意。我没有将代码设置为同步。谁在乎函数调用的名称是什么?正如我非常明确地解释的那样,我以同步方式使用它们。我这样做正是因为这回答了您的问题(“带超时的同步客户端”)来回答问题a)我想您需要std::ref(s)
中的std::thread t(套接字计时器,std::ref(s),秒);
因为套接字不可复制。真的……现在代码可以编译并且运行良好。。。
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#define TIMEOUT std::chrono::seconds(5)
#define MAX_MESSAGE_SIZE 4096
using boost::asio::ip::tcp;
enum { max_length = 2048 };
struct Service {
using error_code = boost::system::error_code;
template<typename AllowTime, typename Cancel> void await_operation_ex(AllowTime const& deadline_or_duration, Cancel&& cancel) {
using namespace boost::asio;
ioservice.reset();
{
high_resolution_timer tm(ioservice, deadline_or_duration);
tm.async_wait([&cancel](error_code ec) { if (ec != error::operation_aborted) std::forward<Cancel>(cancel)(); });
ioservice.run_one();
}
ioservice.run();
}
template<typename AllowTime, typename ServiceObject> void await_operation(AllowTime const& deadline_or_duration, ServiceObject& so) {
return await_operation_ex(deadline_or_duration, [&so]{ so.cancel(); });
}
boost::asio::io_service ioservice;
};
int main()
{
while(true)
{
try
{
Service service;
std::cout << "Enter FTP server address to connect or END to finish: " << std::endl;
std::string address;
if (std::cin >> address) {
if (address == "END") break;
} else {
if (std::cin.eof())
break;
std::cerr << "Invalid input ignored\n";
std::cin.clear();
std::cin.ignore(1024, '\n');
continue;
}
tcp::socket s(service.ioservice);
tcp::resolver resolver(service.ioservice);
boost::asio::async_connect(s, resolver.resolve({address, "21"}), [](boost::system::error_code ec, tcp::resolver::iterator it) {
if (ec) throw std::runtime_error("Error connecting to server: " + ec.message());
std::cout << "Connected to " << it->endpoint() << std::endl;
});
service.await_operation_ex(TIMEOUT, [&]{
throw std::runtime_error("Error connecting to server: timeout\n");
});
auto receive = [&] {
boost::asio::streambuf sb;
size_t bytes;
boost::asio::async_read_until(s, sb, '\n', [&](boost::system::error_code ec, size_t bytes_transferred) {
if (ec) throw std::runtime_error("Error receiving message: " + ec.message());
bytes = bytes_transferred;
std::cout << "Received message is: " << &sb;
});
service.await_operation(TIMEOUT, s);
return bytes;
};
receive(); // banner
auto send = [&](std::string cmd) {
boost::asio::async_write(s, boost::asio::buffer(cmd), [](boost::system::error_code ec, size_t /*bytes_transferred*/) {
if (ec) throw std::runtime_error("Error sending message: " + ec.message());
});
service.await_operation(TIMEOUT, s);
};
auto ftp_command = [&](std::string cmd) {
send(cmd + "\r\n");
receive(); // response
};
//ftp_command("USER bob");
//ftp_command("PASS hello");
while (true) {
std::cout << "Enter command: ";
std::string request;
if (!std::getline(std::cin, request))
break;
ftp_command(request);
}
}
catch (std::exception const& e)
{
std::cerr << "COMMUNICATIONS ERROR " << e.what() << "\n";
}
}
return 0;
}