好吧,我对套接字很陌生,我正在尝试用多个同步客户端做一个异步服务器。问题是,我的服务器无法从客户端读取任何数据,而且也没有报错!
这是我的服务器类:
#define READ_BUF_SIZE 512
struct Connection {
boost::asio::ip::tcp::socket socket;
boost::asio::streambuf read_buffer;
Connection(boost::asio::io_service & io_service) : socket(io_service), read_buffer() { }
Connection(boost::asio::io_service & io_service, size_t max_buffer_size) : socket(io_service), read_buffer(max_buffer_size) { }
};
class CServer {
boost::asio::io_service m_ioservice;
boost::asio::ip::tcp::acceptor m_acceptor;
std::list<Connection> m_connections;
using con_handle_t = std::list<Connection>::iterator;
public:
CServer() : m_ioservice(), m_acceptor(m_ioservice), m_connections() { }
void handle_read(con_handle_t con_handle, boost::system::error_code const & err, size_t bytes_transfered) {
if (bytes_transfered > 0) {
std::istream is(&con_handle->read_buffer);
std::string line;
std::getline(is, line);
std::cout << "Message Received: " << line << std::endl;
}
if (!err) {
do_async_read(con_handle);
}
else {
std::cerr << "Error on read: " << err.message() << std::endl;
m_connections.erase(con_handle);
}
}
void do_async_read(con_handle_t con_handle) {
auto handler = boost::bind(&CServer::handle_read, this, con_handle, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred);
boost::asio::async_read(con_handle->socket, con_handle->read_buffer, boost::asio::transfer_exactly(READ_BUF_SIZE), handler);
}
void handle_write(con_handle_t con_handle, std::shared_ptr<std::string> msg_buffer, boost::system::error_code const & err) {
if (!err) {
std::cout << "Finished sending message\n";
if (con_handle->socket.is_open()) {
// Write completed successfully and connection is open
}
}
else {
std::cerr << "Error on write: " << err.message() << std::endl;
m_connections.erase(con_handle);
}
}
void handle_accept(con_handle_t con_handle, boost::system::error_code const & err) {
if (!err) {
std::cout << "Connection from: " << con_handle->socket.remote_endpoint().address().to_string() << "\n";
std::cout << "Sending message\n";
auto buff = std::make_shared<std::string>("Hello World!\r\n\r\n");
auto handler = boost::bind(&CServer::handle_write, this, con_handle, buff, boost::asio::placeholders::error);
boost::asio::async_write(con_handle->socket, boost::asio::buffer(*buff), handler);
do_async_read(con_handle);
}
else {
std::cerr << "We had an error: " << err.message() << std::endl;
m_connections.erase(con_handle);
}
start_accept();
}
void start_accept() {
auto con_handle = m_connections.emplace(m_connections.begin(), m_ioservice);
auto handler = boost::bind(&CServer::handle_accept, this, con_handle, boost::asio::placeholders::error);
m_acceptor.async_accept(con_handle->socket, handler);
}
void listen(uint16_t port) {
auto endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port);
m_acceptor.open(endpoint.protocol());
m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
m_acceptor.bind(endpoint);
m_acceptor.listen();
start_accept();
}
void run() {
m_ioservice.run();
}
};
此类首先通过构造函数创建,然后首先使用其上的listen
函数调用,然后使用其run
指令调用。
虽然“Hello World!”发送测试消息,服务器没有收到客户端返回的任何信息(handle_read()
没有被调用)
Client::Client() : io_context(), resolver(io_context), endpoints(resolver.resolve("localhost", "daytime")), socket(io_context)
{
try
{
boost::asio::connect(socket, endpoints);
boost::array<unsigned char, PACKET_LENGTH> buf;
boost::system::error_code error;
socket.read_some(boost::asio::buffer(buf), error);
std::cout << "Got message." << std::endl;
boost::asio::write(socket, boost::asio::buffer("test message"), error);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
最后,这是我的客户端类。它包含一个 io_context
对象、一个套接字、一个解析器和一个名为 endpoints
的 tcp::resolver::results_type
对象类型。
调试“Got message”条目实际上输出到控制台,因此服务器可以写入客户端,而“test message”条目实际上从未在服务器中看到,大概是因为它无法读取它。
这里的问题在哪里?提前致谢。
最佳答案
问题来了
boost::asio::async_read(con_handle->socket, con_handle->read_buffer,
boost::asio::transfer_exactly(READ_BUF_SIZE), handler);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ where READ_BUF_SIZE is 512
当恰好读取了 512 个字节或客户端的套接字被销毁或其发送操作被 socket.shutdown()
禁用时,将调用您的处理程序。
所以重点是客户端代码中Client
对象的生命周期。如果您的客户端代码如下所示:
int main() {
Client c; // send data, delete object, socket is destroyed
return 0;
}
在服务器中 handle_read
被调用 error_code == boost::asio::error::eof (end of file or stream)
这意味着套接字在客户端被销毁。但是你可以检查streambuf的内容,它包含“test message”。
但是如果你的客户的代码是这样的:
int main() {
Client c;
// wait here for X seconds
return 0;
}
在服务器端你不会看到任何输出,直到 X 秒过去。你可以使用
boost::asio::write(socket, boost::asio::buffer("test message"), error);
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send); // <---
shutdown
使 end_of_file
被发送 到服务器,读取操作中止,handle_read
的处理程序被调用,可以检查error_code是否为error::eof
,如果是,可以打印strambuf的内容。
如果你不知道发送了多少字节,你可以使用 async_read_until()
函数和分隔符来指示消息的结束(在这个例子中它是换行符):
// server side
boost::asio::async_read_until(con_handle->socket, con_handle->read_buffer, "\n", handler);
^^^^ delimiter
// client side
boost::asio::write(socket, boost::asio::buffer("test message\n"), error);
^^^^ added delimiter
关于c++ - 来自异步服务器的套接字写入 - 但不读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54498847/