我正在聊天。出于某种原因,在其他客户端之间分发用户的消息时,由Server::msgHandler
async_write
对Connection::write
进行屠宰的字符串,使其仅显示为部分该字符串实际上已被读取。例如:
构造信息:"杰克"你好的人 出现为:"由Jack"
即string str=Hello people
未打印出来。起初我认为这与隐含的\0
结尾有关,但是这并没有任何意义,而且,当我在消息中尝试了各种字符串位置时,我注意到{{1}在其他文本之前,文本将完全显示为str
,或将其放置在意外的位置。 E.g。
str
将显示为:
有人说 by" name" Hello People
完整,简约,可编辑的例子:
writeMsg("It was said: \n"+str+" by \"name\"\n");
UPD
事实证明#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <iostream>
#include <vector>
#include <deque>
typedef boost::asio::io_service io_service;
typedef boost::asio::ip::tcp tcp;
class Server;
class Connection : public boost::enable_shared_from_this<Connection> {
io_service::strand strand;
tcp::socket soc;
std::deque<std::string> msgBuff;
boost::asio::streambuf buf;
Server* server;
void(Server::*serverHandler)(std::string);
private:
Connection(io_service& service) :soc(service), strand(service){
}
void writeStranded(std::string msg){
msgBuff.push_back(msg);
if (msgBuff.size() > 1)return;
write();
}
void write(){
std::string& tmpMsg = msgBuff[0];
boost::asio::async_write(
soc,
boost::asio::buffer(tmpMsg.c_str(), tmpMsg.size()),
strand.wrap(
boost::bind(&Connection::handleWrite,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)
)
);
}
void handleWrite(const boost::system::error_code&, size_t s){
msgBuff.pop_front();
if (!msgBuff.empty())write();
}
void handleRead(const boost::system::error_code&, size_t s){
std::istream is(&buf);
std::string tmpMsg;
std::getline(is, tmpMsg);
(server->*serverHandler)(tmpMsg);
readMsg();
}
public:
typedef boost::shared_ptr<Connection> pointer;
static pointer createInstance(io_service& service){
return pointer(new Connection(service));
}
void init(Server* server, void(Server::*serverHandler)(std::string)){
this->server = server;
this->serverHandler = serverHandler;
writeMsg("hello\n");
readMsg();
}
void writeMsg(std::string msg){
strand.dispatch(boost::bind(&Connection::writeStranded, this, msg));
}
void readMsg(){
const char delim = '\n';
boost::asio::async_read_until(soc, buf, delim,
boost::bind(&Connection::handleRead, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
tcp::socket& getSocket(){
return soc;
}
};
class Server{
tcp::acceptor accept;
std::vector<Connection::pointer> connections;
public:
Server(io_service& io_service, int port = 23) :accept(io_service, tcp::endpoint(tcp::v4(), port)){
awaitConnection();
};
private:
void awaitConnection(){
Connection::pointer con = Connection::createInstance(accept .get_io_service());
accept.async_accept(con->getSocket(), boost::bind(&Server::conAcceptor, this, con, boost::asio::placeholders::error));
}
void conAcceptor(Connection::pointer con, const boost::system::error_code& err){
if (err)return;
con->init(this, &Server::msgHandler);
awaitConnection();
connections.push_back(con);
}
void msgHandler(std::string str){
for (Connection::pointer ptr : connections){
ptr->writeMsg(str+" by \"name\"\n");
}
}
};
int main(){
io_service io_service;
Server s(io_service);
io_service.run();
system("pause");
}
附加了带回车符的字符串,该字符串在async_read
字符串中的分隔符之前添加时存储,并且每次我尝试使名称出现时,其前面的所有内容都将得到被随后的一切覆盖。有时候回车会变得疯狂,并且会跳过名字前面的一些字符,这使得对这个错误的搜索变得更加复杂。
答案 0 :(得分:1)
我跑了。我不得不为它写一个客户......
在投入生产之前,您需要查看生命周期处理。通常的方法是连接对象在其绑定处理程序中将shared_ptr保存到自身。
我使用了c ++ 14 lambdas,因为我发现它们比boost :: bind更加繁重。
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <iostream>
#include <vector>
#include <deque>
#include <iterator>
typedef boost::asio::io_service io_service;
typedef boost::asio::ip::tcp tcp;
class Server;
class Connection
: public boost::enable_shared_from_this<Connection>
{
io_service::strand strand;
tcp::socket soc;
// use double-buffering for the message sending
std::deque<std::string> sending, to_send;
boost::asio::streambuf buf;
Server *server;
void (Server::*serverHandler)(std::string);
private:
Connection(io_service& service)
: strand(service)
, soc(service)
{
}
void writeStranded(std::string msg)
{
assert(strand.running_in_this_thread()); // sanity check
to_send.push_back(std::move(msg));
maybe_write();
}
void maybe_write()
{
assert(strand.running_in_this_thread()); // sanity check
if (sending.empty() and not to_send.empty()) {
sending.swap(to_send);
// make a buffer sequence
auto buffers = std::vector<boost::asio::const_buffers_1>();
buffers.reserve(sending.size());
for (auto& data : sending) {
buffers.push_back(boost::asio::buffer(data));
}
boost::asio::async_write(soc, buffers,
strand.wrap([this](auto&& ec, size_t size)
{
this->sending.clear();
if (not ec) maybe_write();
}));
}
}
void handleRead(const boost::system::error_code&, size_t s)
{
std::istream is(&buf);
std::string tmpMsg;
std::getline(is, tmpMsg);
(server->*serverHandler)(tmpMsg);
readMsg();
}
public:
typedef boost::shared_ptr<Connection> pointer;
static pointer createInstance(io_service& service)
{
return pointer(new Connection(service));
}
void init(Server *server, void(Server::*serverHandler)(std::string))
{
this->server = server;
this->serverHandler = serverHandler;
writeMsg("hello\n");
readMsg();
}
void writeMsg(std::string msg)
{
strand.dispatch(boost::bind(&Connection::writeStranded, this, msg));
}
void readMsg()
{
const char delim = '\n';
boost::asio::async_read_until(soc, buf, delim,
boost::bind(&Connection::handleRead, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
tcp::socket& getSocket()
{
return soc;
}
};
class Server
{
tcp::acceptor accept;
std::vector<Connection::pointer> connections;
public:
Server(io_service& io_service, int port = 2333)
: accept(io_service, tcp::endpoint(tcp::v4(), port))
{
awaitConnection();
};
private:
void awaitConnection()
{
Connection::pointer con = Connection::createInstance(accept.get_io_service());
accept.async_accept(con->getSocket(),
boost::bind(&Server::conAcceptor, this, con, boost::asio::placeholders::error));
}
void conAcceptor(Connection::pointer con, const boost::system::error_code& err)
{
if (err)return;
con->init(this, &Server::msgHandler);
awaitConnection();
connections.push_back(con);
}
void msgHandler(std::string str)
{
for (Connection::pointer ptr : connections) {
ptr->writeMsg(str + " by \"name\"\n");
}
}
};
struct Client
{
using protocol = boost::asio::ip::tcp;
Client(boost::asio::io_service& exec)
: executor_(exec) {}
void run(int port)
{
resolver_.async_resolve(protocol::resolver::query("localhost", std::to_string(port)),
strand_.wrap([this](auto&& ec, auto iter)
{
std::cout << "resolve: " << ec.message() << std::endl;
if (not ec) start_connect(iter);
}));
}
void start_connect(protocol::resolver::iterator iter)
{
boost::asio::async_connect(socket_, iter,
strand_.wrap([this](auto&& ec, auto iter)
{
std::cout << "connect: " << ec.message() << std::endl;
if (not ec) {
this->start_reading();
auto data = std::make_shared<std::string>(
"The quick brown fox jumps over the lazy dog\n"
"Farmer bob has a cool tractor\n");
boost::asio::async_write(socket_, boost::asio::buffer(*data),
strand_
.wrap([data](auto&& ec, auto size)
{
std::cout << "written: "
<< size
<< std::endl;
}));
}
}));
}
void start_reading()
{
auto buffer = read_buffer_.prepare(1024);
socket_.async_read_some(read_buffer_.prepare(1024), [this](auto&& ec, auto size)
{
read_buffer_.commit(size);
std::istream is(std::addressof(read_buffer_));
std::string s;
while(std::getline(is, s)) {
std::cout << s << std::endl;
}
start_reading();
});
}
boost::asio::io_service& executor_;
boost::asio::io_service::strand strand_{executor_};
protocol::resolver resolver_{executor_};
protocol::socket socket_{executor_};
boost::asio::streambuf read_buffer_;
};
int main()
{
io_service io_service;
Server s(io_service);
Client c(io_service);
c.run(2333);
io_service.run();
system("pause");
}
输出(程序不会终止):
resolve: Undefined error: 0
connect: Undefined error: 0
written: 74
hello
The quick brown fox jumps over the lazy dog by "name"
Farmer bob has a cool tractor by "name"
答案 1 :(得分:0)
请注意,void readMsg()
中的delim
被设置为'\n'
。尽管看上去无害,但它没有考虑到在Windows中换行符表示为CR + LF的事实,即:“ \ r \ n ” 。因此,每次您使用read_until(delim)
从套接字读取内容时,除定界符外的所有内容都会保留在缓冲区中,包括\r
(回车)。如果以后再附加类似的字符串,则可以预期\r
之后的所有内容都会覆盖原始文本。