C++ boost asio tcp-套接字写入数据与缓冲区中的数据不同-可能存在线程不安全
我正在探索boost asio产品 客户端发送一个1字节的头,指示要跟随的字节长度 相关服务器代码:C++ boost asio tcp-套接字写入数据与缓冲区中的数据不同-可能存在线程不安全,c++,boost,tcp,boost-asio,C++,Boost,Tcp,Boost Asio,我正在探索boost asio产品 客户端发送一个1字节的头,指示要跟随的字节长度 相关服务器代码: enum {max_length=1}; void handle_read(const boost::system::error_code & error, const size_t & bytes_transferred){ if (! error){ ++ctr; std::string inc_data_str(this->inc_data
enum {max_length=1};
void handle_read(const boost::system::error_code & error, const size_t & bytes_transferred){
if (! error){
++ctr;
std::string inc_data_str(this->inc_data.begin(),this->inc_data.end());
std::cout<<"received string: "<<inc_data_str<<" with size "<<inc_data_str.size()
<<" bytes_transferred: "<<bytes_transferred<<" ctr: "<<ctr<<std::endl;
int size_inc_next = boost::lexical_cast<int>(inc_data_str);
int offset = 0;
//std::cout<<"incoming integer of size "<<size_inc_next<<" processed from string: "<<inc_data_str<<std::endl;
std::vector<char> next_inc_data(size_inc_next+offset);
boost::asio::read(this->socket,boost::asio::buffer(next_inc_data),boost::asio::transfer_exactly(size_inc_next+offset));
std::string int_recvd(next_inc_data.begin(),next_inc_data.begin()+size_inc_next);
//std::cout<<boost::posix_time::microsec_clock::local_time()<<std::endl;
//std::cout<<"received integer: "<<int_recvd<<" from string "<<int_recvd<<" of size "<<int_recvd.size()<<std::endl;
this->process_connection();
} // ! error
} // handle_read
void process_connection(){
boost::asio::async_read(this->socket,boost::asio::buffer(this->inc_data),boost::asio::transfer_exactly(max_length),
boost::bind(&Connection::handle_read,shared_from_this(),boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
客户端进程的预期打印输出示例:
15 ctr: 356293
这样的预期输出会产生一段时间,但假设在356293客户机迭代之后(这个ctr数字在反复试验过程中肉眼是不确定的),服务器会出现以下错误:
received string: with size 1 bytes_transferred: 1 ctr: 159686
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_lexical_cast> >'
what(): bad lexical cast: source type value could not be interpreted as target
这里发生了什么?为什么?如何解决
strace后的进一步编辑:
客户端跟踪:
sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 2
epoll_wait(4, {}, 128, 0) = 0
write(1, "15 ctr: 204441\n", 1515 ctr: 204441) = 15
sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 2
epoll_wait(4, {}, 128, 0) = 0
write(1, "15 ctr: 204442\n", 1515 ctr: 204442) = 15
sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = -1 EAGAIN (Resource temporarily \
unavailable)
epoll_wait(4, {{EPOLLOUT, {u32=167539936, u64=167539936}}}, 128, -1) = 1
sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"\0\0", 2}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 2
write(1, "15 ctr: 204443\n", 1515 ctr: 204443) = 15
sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 2
epoll_wait(4, {}, 128, 0) = 0
write(1, "15 ctr: 204444\n", 1515 ctr: 204444) = 15
sendmsg(6, {msg_name(0)=NULL, msg_iov(1)=[{"15", 2}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 2
epoll_wait(4, {}, 128, 0) = 0
write(1, "15 ctr: 204445\n", 1515 ctr: 204445) = 15
服务器跟踪:
write(1, "received string: 1 with size 1 b"..., 64received string: 1 with size 1 bytes_transferred: 1 ctr: 204441) = 64
write(1, "incoming integer of size 1 proce"..., 52incoming integer of size 1 processed from string: 1) = 52
recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"5", 1}], msg_controllen=0, msg_flags=0},0) = 1
write(1, "received integer: 5 from string "..., 44received integer: 5 from string 5 of size 1) = 44
recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"1", 1}], msg_controllen=0, msg_flags=0},0) = 1
epoll_wait(4, {}, 128, 0) = 0
write(1, "received string: 1 with size 1 b"..., 64received string: 1 with size 1 bytes_transferred: 1 ctr: 204442) = 64
write(1, "incoming integer of size 1 proce"..., 52incoming integer of size 1 processed from string: 1) = 52
recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"5", 1}], msg_controllen=0, msg_flags=0}, 0) = 1
write(1, "received integer: 5 from string "..., 44received integer: 5 from string 5 of size 1) = 44
recvmsg(7, {msg_name(0)=NULL, msg_iov(1)=[{"\0", 1}], msg_controllen=0, msg_flags=0}, 0) = 1
epoll_wait(4, {}, 128, 0) = 0
write(1, "received string: \0 with size 1 b"..., 64received string: ^@ with size 1 bytes_transferred: 1 ctr: 204443) = 64
futex(0xb76640fc, FUTEX_WAKE_PRIVATE, 2147483647) = 0
write(1, "inc_data_str\n", 13inc_data_str) = 13
对于客户端进程,在错误的“\0\0”发送之前的epoll\U等待与其他epoll\U等待调用不同(u32=..,u64=..)。。。但我不知道这意味着什么
总结一下令人费解的部分,strace表示正在传输的空值,但下一行strace表示对标准输出的写系统调用,其文字为“15”,这意味着传输数据向量中就是这个
重新编辑:
最后我插入了一个
boost::this_thread::sleep(boost::posix_time::microseconds(200));
就在客户端on_write函数中的write语句之前
这样,就不会有任何问题。那么asio对象会出现什么样的并发问题呢?是套接字吗?嗯,异常消息说,有一个错误的词法转换。我在您的服务器代码中看到一个:
int size_inc_next = boost::lexical_cast<int>(inc_data_str);
int size\u inc\u next=boost::lexical\u cast(inc\u data\u str);
也许你应该在那里设置一个断点并进行调试。当你收到信息时
接收到的字符串:X,大小为1字节\u传输:1个ctr:159686
这显然是词法\u cast
之前的行的输出。然后,inc\u data\u str
似乎是“X”-将其转换为int应该会触发错误转换异常
为什么有一个“X”来自连接的另一端,我不知道。那么,异常消息说,有一个错误的词法转换。我在您的服务器代码中看到一个:
int size_inc_next = boost::lexical_cast<int>(inc_data_str);
int size\u inc\u next=boost::lexical\u cast(inc\u data\u str);
也许你应该在那里设置一个断点并进行调试。当你收到信息时
接收到的字符串:X,大小为1字节\u传输:1个ctr:159686
这显然是词法\u cast
之前的行的输出。然后,inc\u data\u str
似乎是“X”-将其转换为int应该会触发错误转换异常
为什么有一个“X”来自连接的另一端,我不知道。您的客户端由于缓冲区寿命而中断
void
on_write(
const boost::system::error_code& error_code
)
{
if ( !error_code ) {
std::string transfer_data("15");
std::vector<char> v_td(transfer_data.begin(), transfer_data.end());
// ^
// \------ goes out of scope before async_write() returns
boost::asio::async_write(
this->socket,
boost::asio::buffer(v_td),
boost::asio::transfer_exactly(2),
boost::bind(
&Client::on_write,
shared_from_this(),
boost::asio::placeholders::error
)
);
}
}
void
论你的写作(
常量boost::系统::错误代码和错误代码
)
{
如果(!错误代码){
std::字符串传输_数据(“15”);
std::vector v_td(transfer_data.begin(),transfer_data.end());
// ^
//\----在async\u write()返回之前超出范围
boost::asio::异步写入(
这个->插座,
boost::asio::buffer(v_td),
boost::asio::transfer_正好(2),
boost::bind(
&客户:在写的时候,
从_this()共享了_,
boost::asio::占位符::错误
)
);
}
}
您需要确保提供给的缓冲区在调用完成处理程序之前保持有效:
缓冲区包含要写入的数据的一个或多个缓冲区。尽管可以根据需要复制缓冲区对象,但
底层内存块由调用者保留,调用者必须
确保它们在调用处理程序之前保持有效。处理者
您的客户端因缓冲区寿命而中断
void
on_write(
const boost::system::error_code& error_code
)
{
if ( !error_code ) {
std::string transfer_data("15");
std::vector<char> v_td(transfer_data.begin(), transfer_data.end());
// ^
// \------ goes out of scope before async_write() returns
boost::asio::async_write(
this->socket,
boost::asio::buffer(v_td),
boost::asio::transfer_exactly(2),
boost::bind(
&Client::on_write,
shared_from_this(),
boost::asio::placeholders::error
)
);
}
}
void
论你的写作(
常量boost::系统::错误代码和错误代码
)
{
如果(!错误代码){
std::字符串传输_数据(“15”);
std::vector v_td(transfer_data.begin(),transfer_data.end());
// ^
//\----在async\u write()返回之前超出范围
boost::asio::异步写入(
这个->插座,
boost::asio::buffer(v_td),
boost::asio::transfer_正好(2),
boost::bind(
&客户:在写的时候,
从_this()共享了_,
boost::asio::占位符::错误
)
);
}
}
您需要确保提供给的缓冲区在调用完成处理程序之前保持有效:
缓冲区包含要写入的数据的一个或多个缓冲区。尽管可以根据需要复制缓冲区对象,但
底层内存块由调用者保留,调用者必须
确保它们在调用处理程序之前保持有效。处理者
没错!这是令人困惑的,因为客户端有一个硬编码的“15”正在被传输…我没有看到在您的服务器中调用
async\u read
,所以我不得不猜测:是否客户端在第一次运行时没有只传输“1”?或者通常您发送的数据不“同步”?在服务器的读取处理程序中第一次调用read()
之后,int\u recvd
中有什么内容?你必须调试它。我对两个进程都做了一次扫描。客户端总是写入“15”,但在服务器出现错误时,它读取的是空的“\O”,而不是“1”。这是一个错误的复制粘贴的异步_读,刚刚编辑。谢谢,等等!客户端在中间写一个“\ 0 \ 0”,然后再次回复到“15”…怎么做?缓冲区似乎是空的。因为你希望客户机准确地写两个字符,所以它写零。是的,准确地说!这是令人困惑的,因为客户端有一个硬编码的“15”正在被传输…我没有看到在您的服务器中调用async\u read
,所以我不得不猜测:是否客户端在第一次运行时没有只传输“1”?或者一般来说,您需要的数据
void
on_write(
const boost::system::error_code& error_code
)
{
if ( !error_code ) {
std::string transfer_data("15");
std::vector<char> v_td(transfer_data.begin(), transfer_data.end());
// ^
// \------ goes out of scope before async_write() returns
boost::asio::async_write(
this->socket,
boost::asio::buffer(v_td),
boost::asio::transfer_exactly(2),
boost::bind(
&Client::on_write,
shared_from_this(),
boost::asio::placeholders::error
)
);
}
}