Boost Asio 不完整写入套接字
Boost Asio incomplete write to socket
我正在使用 Boost Asio 编写一个非常简单的玩具键值存储,并且发生了一些非常奇怪的事情。
基于字符串的协议是这样的:
S <key> <value> // to set a key
G <key> // to get the key value
L // to list all key-value pairs
写入是同步的,使用
boost::asio::write(socket_,boost::asio::buffer(resp, len));
其中 socket_ 是一个 boost::asio::ip::tcp::socket - 异步写入故事显然没有改变。
问题是有时它没有将它应该写入的所有字节写入套接字,或者输出以某种方式被破坏...
损坏的列表输出示例(在本地主机上,使用 nc、echo 和 hexdump):
> echo S a 12 | nc localhost 5000
A
> echo S b 23 | nc localhost 5000
A
> echo L | nc localhost 5000 | hexdump -C
00000000 61 3a 20 31 32 3b 20 62 60 00 00 00 00 00 |a: 12; b`.....|
0000000e
> echo L | nc localhost 5000 | hexdump -C
00000000 61 3a 20 31 32 3b 20 62 3a 20 32 33 3b 20 |a: 12; b: 23; |
0000000e
我正在使用 Ubuntu 14.10 存储库中的 Boost 1.55。
遵循为客户端服务的功能代码。
提前感谢您的任何提示!
void ClientSession::handle_read(const boost::system::error_code& error, size_t bytes_transferred) {
if (!error) {
std::string cmd(data_, bytes_transferred);
cmd = trim_str(cmd);
const char* resp = NULL;
int len = 0;
switch(cmd.at(0)) {
case SET: {
std::size_t k_pos = cmd.find(" ") + 1;
std::size_t v_pos = cmd.find(" ", k_pos+1) + 1;
std::string key = trim_str(cmd.substr(k_pos, v_pos-3));
std::string value = trim_str(cmd.substr(v_pos, cmd.length()-1));
cout << "SET key " << key << ", value " << value << "*" <<endl;
kvs->db[key] = std::atoi(value.c_str());
resp = "A";
len = 1;
break;
}
case GET: {
std::size_t k_pos = cmd.find(" ") + 1;
std::string key = trim_str(cmd.substr(k_pos, cmd.length()));
cout << "GET key " << key << "*" << endl;
int value = kvs->db[key];
char str[5];
sprintf(str, "%d", value);
resp = (const char*) str;
len = strlen(resp);
break;
}
case LIST: {
ostringstream os;
for (std::map<string, int>::iterator iter = kvs->db.begin();
iter != kvs->db.end(); ++iter )
os << iter->first << ": " << iter->second << "; ";
cout << "list: " << os.str().c_str() << endl;
resp = os.str().c_str();
len = strlen(resp);
break;
}
case DEL: {
std::size_t k_pos = cmd.find(" ") + 1;
std::string key = trim_str(cmd.substr(k_pos, cmd.length()));
kvs->db.erase(key);
resp = "A";
len = 1;
break;
}
default: {
resp = "NACK.";
len = 5;
}
}
cout << "resp: " << resp << "*" << endl;
cout << "len: " << len << "*" << endl;
std::size_t written = boost::asio::write(socket_,
boost::asio::buffer(resp, len));
cout << "written: " << written << endl;
boost::system::error_code ignored_ec;
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
socket_.close();
} else
delete this;
至少你有未定义的行为,因为在 case GET
:
中悬空 resp
指针
{
// ...
char str[5];
resp = (const char*) str; // WHOOOOOOOOOOOPS
len = strlen(resp);
}
case LIST
下完全相同的东西:
{
std::ostringstream os;
// ....
resp = os.str().c_str(); // WHOOOOOOOOOOOPS
}
当您有 Undefined Behaviour.
时,关于该程序的所有推理都不再有用
解决这些问题(可能还有更多我没有寻找的问题),然后重新测试。 运行 在 valgrind 下。使用静态分析工具。
更新:修复了单线程版本:https://gist.github.com/sehe/69379e17350fb718892f#comment-1428235
测试运行输出:
$ for a in S{a..d}\ $RANDOM Gnonexisting L; do echo "$a -> $(netcat 127.0.0.1 5000 <<< "$a")"; done | nl
1 Sa 15936 -> A
2 Sb 3671 -> A
3 Sc 10550 -> A
4 Sd 7741 -> A
5 Gnonexisting -> 0
6 L -> 1: 1; 2: 2; a: 15936; asdasd: 0; b: 3671; c: 10550; d: 7741; nonexisting: 0;
handle_read
的代码如下所示:
void ClientSession::handle_read(const boost::system::error_code& error, size_t bytes_transferred) {
if (!error) {
std::istringstream request(std::string(data_, bytes_transferred));
boost::asio::streambuf resp;
std::ostream os(&resp);
char cmd_char = 0;
std::string key;
int value;
if (request >> cmd_char) switch(cmd_char) {
case SET:
if (request >> key >> value)
kvs->db[key] = value;
os << "A";
break;
case GET:
if (request >> key)
os << kvs->db[key];
break;
case LIST:
for (auto const& e : kvs->db)
os << e.first << ": " << e.second << "; ";
break;
case DEL:
if (request >> key)
kvs->db.erase(key);
os << "A";
break;
default:
os << "NACK.";
}
cout << "resp: " << &resp << "*" << endl;
cout << "len: " << resp.size() << "*" << endl;
std::size_t written = boost::asio::write(socket_, resp);
cout << "written: " << written << endl;
boost::system::error_code ignored_ec;
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
socket_.close();
} else
delete this;
}
我正在使用 Boost Asio 编写一个非常简单的玩具键值存储,并且发生了一些非常奇怪的事情。
基于字符串的协议是这样的:
S <key> <value> // to set a key
G <key> // to get the key value
L // to list all key-value pairs
写入是同步的,使用
boost::asio::write(socket_,boost::asio::buffer(resp, len));
其中 socket_ 是一个 boost::asio::ip::tcp::socket - 异步写入故事显然没有改变。
问题是有时它没有将它应该写入的所有字节写入套接字,或者输出以某种方式被破坏...
损坏的列表输出示例(在本地主机上,使用 nc、echo 和 hexdump):
> echo S a 12 | nc localhost 5000
A
> echo S b 23 | nc localhost 5000
A
> echo L | nc localhost 5000 | hexdump -C
00000000 61 3a 20 31 32 3b 20 62 60 00 00 00 00 00 |a: 12; b`.....|
0000000e
> echo L | nc localhost 5000 | hexdump -C
00000000 61 3a 20 31 32 3b 20 62 3a 20 32 33 3b 20 |a: 12; b: 23; |
0000000e
我正在使用 Ubuntu 14.10 存储库中的 Boost 1.55。 遵循为客户端服务的功能代码。
提前感谢您的任何提示!
void ClientSession::handle_read(const boost::system::error_code& error, size_t bytes_transferred) {
if (!error) {
std::string cmd(data_, bytes_transferred);
cmd = trim_str(cmd);
const char* resp = NULL;
int len = 0;
switch(cmd.at(0)) {
case SET: {
std::size_t k_pos = cmd.find(" ") + 1;
std::size_t v_pos = cmd.find(" ", k_pos+1) + 1;
std::string key = trim_str(cmd.substr(k_pos, v_pos-3));
std::string value = trim_str(cmd.substr(v_pos, cmd.length()-1));
cout << "SET key " << key << ", value " << value << "*" <<endl;
kvs->db[key] = std::atoi(value.c_str());
resp = "A";
len = 1;
break;
}
case GET: {
std::size_t k_pos = cmd.find(" ") + 1;
std::string key = trim_str(cmd.substr(k_pos, cmd.length()));
cout << "GET key " << key << "*" << endl;
int value = kvs->db[key];
char str[5];
sprintf(str, "%d", value);
resp = (const char*) str;
len = strlen(resp);
break;
}
case LIST: {
ostringstream os;
for (std::map<string, int>::iterator iter = kvs->db.begin();
iter != kvs->db.end(); ++iter )
os << iter->first << ": " << iter->second << "; ";
cout << "list: " << os.str().c_str() << endl;
resp = os.str().c_str();
len = strlen(resp);
break;
}
case DEL: {
std::size_t k_pos = cmd.find(" ") + 1;
std::string key = trim_str(cmd.substr(k_pos, cmd.length()));
kvs->db.erase(key);
resp = "A";
len = 1;
break;
}
default: {
resp = "NACK.";
len = 5;
}
}
cout << "resp: " << resp << "*" << endl;
cout << "len: " << len << "*" << endl;
std::size_t written = boost::asio::write(socket_,
boost::asio::buffer(resp, len));
cout << "written: " << written << endl;
boost::system::error_code ignored_ec;
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
socket_.close();
} else
delete this;
至少你有未定义的行为,因为在 case GET
:
resp
指针
{
// ...
char str[5];
resp = (const char*) str; // WHOOOOOOOOOOOPS
len = strlen(resp);
}
case LIST
下完全相同的东西:
{
std::ostringstream os;
// ....
resp = os.str().c_str(); // WHOOOOOOOOOOOPS
}
当您有 Undefined Behaviour.
时,关于该程序的所有推理都不再有用解决这些问题(可能还有更多我没有寻找的问题),然后重新测试。 运行 在 valgrind 下。使用静态分析工具。
更新:修复了单线程版本:https://gist.github.com/sehe/69379e17350fb718892f#comment-1428235
测试运行输出:
$ for a in S{a..d}\ $RANDOM Gnonexisting L; do echo "$a -> $(netcat 127.0.0.1 5000 <<< "$a")"; done | nl
1 Sa 15936 -> A
2 Sb 3671 -> A
3 Sc 10550 -> A
4 Sd 7741 -> A
5 Gnonexisting -> 0
6 L -> 1: 1; 2: 2; a: 15936; asdasd: 0; b: 3671; c: 10550; d: 7741; nonexisting: 0;
handle_read
的代码如下所示:
void ClientSession::handle_read(const boost::system::error_code& error, size_t bytes_transferred) {
if (!error) {
std::istringstream request(std::string(data_, bytes_transferred));
boost::asio::streambuf resp;
std::ostream os(&resp);
char cmd_char = 0;
std::string key;
int value;
if (request >> cmd_char) switch(cmd_char) {
case SET:
if (request >> key >> value)
kvs->db[key] = value;
os << "A";
break;
case GET:
if (request >> key)
os << kvs->db[key];
break;
case LIST:
for (auto const& e : kvs->db)
os << e.first << ": " << e.second << "; ";
break;
case DEL:
if (request >> key)
kvs->db.erase(key);
os << "A";
break;
default:
os << "NACK.";
}
cout << "resp: " << &resp << "*" << endl;
cout << "len: " << resp.size() << "*" << endl;
std::size_t written = boost::asio::write(socket_, resp);
cout << "written: " << written << endl;
boost::system::error_code ignored_ec;
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
socket_.close();
} else
delete this;
}