我正在基于给定的“Hello World”示例使用 cpp-netlib 编写异步 HTTP 服务器和客户端。服务器可以处理GET
请求,但无法获取请求的请求体POST
。然后我看到了cpp-netlib的Issue-823并添加了一个异步连接处理程序。结构如下(从“文件上传”示例更改):
///
/// Custom exception type
///
struct file_uploader_exception : public std::runtime_error {
file_uploader_exception(const std::string err) :
std::runtime_error(err) {
}
};
///
/// Encapsulates request & connection
///
struct file_uploader : std::enable_shared_from_this<file_uploader> {
const server::request& req;
server::connection_ptr conn;
std::mutex mtx;
std::condition_variable condvar;
//FILE* fp = NULL;
std::string body;
public:
file_uploader(const server::request& req, const server::connection_ptr& conn)
: req(req)
, conn(conn) {
/*const std::string dest = destination(req);
if (dest.find("/upload") != std::string::npos) {
auto queries = get_queries(dest);
auto fname = queries.find("filename");
if (fname != queries.end()) {
fp = ::fopen(fname->second.c_str(), "wb");
if (!fp) {
throw file_uploader_exception("Failed to open file to write");
}
}
else {
throw file_uploader_exception("'filename' cannot be empty");
}
}*/
}
~file_uploader() {
/*if (fp) {
::fflush(fp);
::fclose(fp);
}*/
}
///
/// Non blocking call to initiate the data transfer
///
void async_recv() {
std::cout << "async_recv()" << std::endl;
std::size_t content_length = 0;
auto const& headers = req.headers;
for (auto item : headers) {
if (boost::to_lower_copy(item.name) == "content-length") {
content_length = std::stoll(item.value);
break;
}
}
read_chunk(conn, content_length);
}
///
/// The client shall wait by calling this until the transfer is done by
/// the IO threadpool
///
void wait_for_completion() {
std::cout << "wait_for_completion()" << std::endl;
std::unique_lock<std::mutex> _(mtx);
condvar.wait(_);
}
private:
///
/// Parses the string and gets the query as a key-value pair
///
/// @param [in] dest String containing the path and the queries, without the fragment,
/// of the form "/path?key1=value1&key2=value2"
///
std::map<std::string, std::string> get_queries(const std::string dest) {
std::cout << "get_queries()" << std::endl;
std::size_t pos = dest.find_first_of("?");
std::map<std::string, std::string> queries;
if (pos != std::string::npos) {
std::string query_string = dest.substr(pos + 1);
// Replace '&' with space
for (pos = 0; pos < query_string.size(); pos++) {
if (query_string[pos] == '&') {
query_string[pos] = ' ';
}
}
std::istringstream sin(query_string);
while (sin >> query_string) {
pos = query_string.find_first_of("=");
if (pos != std::string::npos) {
const std::string key = query_string.substr(0, pos);
const std::string value = query_string.substr(pos + 1);
queries[key] = value;
}
}
}
return queries;
}
///
/// Reads a chunk of data
///
/// @param [in] conn Connection to read from
/// @param [in] left2read Size to read
///
void read_chunk(server::connection_ptr conn, std::size_t left2read) {
std::cout << "read_chunk()" << std::endl;
conn->read(boost::bind(&file_uploader::on_data_ready,
file_uploader::shared_from_this(),
_1, _2, _3, conn, left2read));
}
///
/// Callback that gets called when the data is ready to be consumed
///
void on_data_ready(server::connection::input_range range,
boost::system::error_code error,
std::size_t size,
server::connection_ptr conn,
std::size_t left2read) {
std::cout << "on_data_ready()" << std::endl;
if (!error) {
//::fwrite(boost::begin(range), size, 1, fp);
body.append(boost::begin(range), boost::begin(range) + size);
std::size_t left = left2read - size;
if (left > 0)
read_chunk(conn, left);
else
wakeup();
}
}
///
/// Wakesup the waiting thread
///
void wakeup() {
std::cout << "wakeup()" << std::endl;
std::unique_lock<std::mutex> _(mtx);
condvar.notify_one();
}
};
当我使用客户端或 curl 执行POST
请求时,服务器输出如下:
PS F:\CBPLibary\x64\Release> .\ServerTest.exe 0.0.0.0 40000
async_recv()
read_chunk()
wait_for_completion()
然后它卡住了。服务器不响应任何其他请求,客户端也卡住了。我想知道为什么会发生这种情况以及如何解决它。我的服务器代码:
struct hello_world {
void operator()(server::request const& request, server::connection_ptr connection) {
std::shared_ptr<file_uploader> uploader(new file_uploader(request, connection));
uploader->async_recv();
uploader->wait_for_completion();
std::cout << uploader->body << std::endl;
server::string_type ip = source(request);
server::response_header headers[] = { {"Connection","close"} ,{"Content-Type", "text/plain"} };
unsigned int port = request.source_port;
std::ostringstream data;
data << "Hello, " << ip << '[' << port << "]!";
connection->set_headers(boost::make_iterator_range(headers, headers + 2));
connection->set_status(server::connection::ok);
connection->write(data.str());
}
};
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " address port" << std::endl;
return 1;
}
try {
hello_world handler;
server::options options(handler);
options.thread_pool(std::make_shared<boost::network::utils::thread_pool>());
server server_(options.address(argv[1]).port(argv[2]));
std::thread t_server([&server_] { server_.run(); });
//server_.run();
t_server.detach();
char ch;
do
{
ch = getchar();
} while (ch != '0');
server_.stop();
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}
我的客户代码:
namespace http = boost::network::http;
using header = std::pair<std::string, std::string>;
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " address port" << std::endl;
return 1;
}
try {
std::string post_body = "test";
http::client client;
std::ostringstream url;
url << "http://" << argv[1] << ":" << argv[2] << "/command";
http::client::request request(url.str());
request.add_header(header("Connection", "keep-alive"));
request.add_header(header("Content-Type", "text/plain"));
//http::client::response response = client.get(request);
http::client::response response = client.post(request, post_body);
std::cout << body(response) << std::endl;
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}
我使用的 curl 命令:
curl.exe -d "test" -X POST http://127.0.0.1:40000/command
我的开发环境:
OS: Windows 11 build 22504
boost library version: 1.77.0
cpp-netlib version: 0.13.0
IDE: Visual Studio 2022 (MSVC17,Build tool v143)