我需要使用ASIO的并行同步TCP解决方案。我试图从这些示例中获取示例代码:https://github.com/jvillasante/asio-network-programming-cookbook/tree/master/src(使用ch04中的服务器:02_Sync_parallel_tcp_server.cpp和ch03:01_Sync_tcp_client.cpp中的客户端)。
我唯一改变的是记录附加到文本文件。
问题是,当服务器运行正常时,客户端在从服务器返回单个响应后死亡:
SELECT *
FROM mytable t
WHERE (t.a = aID and aPARAM = 'a')
or (t.b = aID and aPARAM = 'b')
or (t.c = aID and aPARAM = 'c')
or (aPARAM not in ('a','b','c'))
服务器代码:
libc++abi.dylib: terminating with uncaught exception of type boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >: shutdown: Socket is not connected
客户代码:
#include <boost/asio.hpp>
#include <atomic>
#include <memory>
#include <thread>
#include <iostream>
#include <fstream>
using namespace boost;
class Service {
public:
Service() = default;
void StartHandlingClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
std::thread th{[this, sock]() { HandleClient(sock); }};
th.detach();
}
private:
void HandleClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
try {
asio::streambuf request;
asio::read_until(*sock.get(), request, '\n');
std::istream is(&request);
std::string line;
std::getline(is, line);
std::ofstream log("logfile2.txt", std::ios_base::app | std::ios_base::out);
log << "Request: " << line << "\n" << std::flush;
// Emulate request processing.
int i = 0;
while (i != 1000000) i++;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// Sending response.
std::string response = "Response\n";
asio::write(*sock.get(), asio::buffer(response));
} catch (std::system_error& e) {
std::ofstream log("logfile1.txt", std::ios_base::app | std::ios_base::out);
log << "Error occurred! Error code = " << e.code().value() << ". Message: " << e.what() << "\n" << std::flush;
}
// Clean up
delete this;
}
};
class Acceptor {
public:
Acceptor(asio::io_service& ios, unsigned short port_num)
: m_ios{ios}, m_acceptor{m_ios, asio::ip::tcp::endpoint{asio::ip::address_v4::any(), port_num}} {
m_acceptor.listen();
}
void Accept() {
auto sock = std::make_shared<asio::ip::tcp::socket>(m_ios);
m_acceptor.accept(*sock.get());
(new Service)->StartHandlingClient(sock);
}
private:
asio::io_service& m_ios;
asio::ip::tcp::acceptor m_acceptor;
};
class Server {
public:
Server() : m_stop{false} {}
void Start(unsigned short port_num) {
m_thread.reset(new std::thread([this, port_num]() { Run(port_num); }));
}
void Stop() {
m_stop.store(true);
m_thread->join();
}
private:
void Run(unsigned short port_num) {
Acceptor acc{m_ios, port_num};
while (!m_stop.load()) {
acc.Accept();
}
}
private:
std::unique_ptr<std::thread> m_thread;
std::atomic<bool> m_stop;
asio::io_service m_ios;
};
int main() {
unsigned short port_num = 3333;
try {
Server srv;
srv.Start(port_num);
std::this_thread::sleep_for(std::chrono::seconds(60));
srv.Stop();
} catch (std::system_error& e) {
std::ofstream log("logfile1.txt", std::ios_base::app | std::ios_base::out);
log << "Error occurred! Error code = " << e.code().value() << ". Message: " << e.what() << "\n" << std::flush;
}
return 0;
}
答案 0 :(得分:1)
我没有看到很多错误,我无法用显示的代码重现问题。
我看到的事情:
delete this
是代码气味)boost::thread_group::join_all
会更好).store()
上的拼写.load()
和atomic<bool>
是非惯用的*sock.get()
不可原谅 非惯用code().value()
- 吞下类别 - 是一件坏事,e.what()
不是获取消息的方式(使用e.code().message()
)。flush
,也可以使用std::endl
在c ++ 14中没有理由使用shared_ptr:
asio::ip::tcp::socket sock(m_ios);
m_acceptor.accept(sock);
std::thread([sock=std::move(sock)]() mutable { HandleClient(sock); }).detach();
在C ++ 11中坚持:
auto sock = std::make_shared<asio::ip::tcp::socket>(m_ios);
m_acceptor.accept(*sock);
std::thread([sock] { HandleClient(*sock); }).detach();
这意味着HandleClient
可以只使用ip::tcp::socket&
代替智能指针。
Server.cpp
#include <atomic>
#include <boost/asio.hpp>
#include <fstream>
#include <iostream>
#include <memory>
#include <thread>
using namespace boost;
static void HandleClient(asio::ip::tcp::socket& sock) {
try {
asio::streambuf buf;
asio::read_until(sock, buf, '\n');
std::string request;
getline(std::istream(&buf), request);
std::ofstream log("server.log", std::ios_base::app | std::ios_base::out);
log << "Request: " << request << std::endl;
// Emulate request processing.
int i = 0;
while (i != 1000000)
i++;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// Sending response.
std::string response = "Response\n";
asio::write(sock, asio::buffer(response));
} catch (std::system_error &e) {
std::ofstream log("server.log", std::ios_base::app | std::ios_base::out);
log << e.what() << " " << e.code() << ": " << e.code().message() << std::endl;
}
}
class Acceptor {
public:
Acceptor(asio::io_service &ios, unsigned short port_num)
: m_ios{ ios }, m_acceptor{ m_ios, asio::ip::tcp::endpoint{ asio::ip::address_v4::any(), port_num } } {
m_acceptor.listen();
}
void Accept() {
auto sock = std::make_shared<asio::ip::tcp::socket>(m_ios);
m_acceptor.accept(*sock);
std::thread([sock] { HandleClient(*sock); }).detach();
}
private:
asio::io_service &m_ios;
asio::ip::tcp::acceptor m_acceptor;
};
class Server {
public:
Server() : m_stop{ false } {}
void Start(unsigned short port_num) {
m_thread.reset(new std::thread([this, port_num]() { Run(port_num); }));
}
void Stop() {
m_stop = true;
m_thread->join();
}
private:
void Run(unsigned short port_num) {
Acceptor acc{ m_ios, port_num };
while (!m_stop) {
acc.Accept();
}
}
private:
std::unique_ptr<std::thread> m_thread;
std::atomic<bool> m_stop;
asio::io_service m_ios;
};
int main() {
unsigned short port_num = 3333;
try {
Server srv;
srv.Start(port_num);
std::this_thread::sleep_for(std::chrono::seconds(60));
srv.Stop();
} catch (std::system_error &e) {
std::ofstream log("server.log", std::ios_base::app | std::ios_base::out);
log << e.what() << " " << e.code() << ": " << e.code().message() << std::endl;
}
}
Client.cpp
#include <boost/asio.hpp>
#include <fstream>
#include <iostream>
using namespace boost;
class SyncTCPClient {
public:
SyncTCPClient(const std::string &raw_ip_address, unsigned short port_num)
: m_ep(asio::ip::address::from_string(raw_ip_address), port_num), m_sock(m_ios) {
m_sock.open(m_ep.protocol());
}
~SyncTCPClient() { close(); }
void connect() { m_sock.connect(m_ep); }
std::string emulateLongComputationOp(unsigned int duration_sec) {
std::string request = "EMULATE_LONG_COMP_OP " + std::to_string(duration_sec) + "\n";
sendRequest(request);
return receiveResponse();
}
private:
void close() {
if (m_sock.is_open()) {
std::ofstream log("client.log", std::ios_base::app | std::ios_base::out);
log << "shutting down" << std::endl;
m_sock.shutdown(asio::ip::tcp::socket::shutdown_both);
log << "closing the socket" << std::endl;
m_sock.close();
log << "socket closed" << std::endl;
}
}
void sendRequest(const std::string &request) { asio::write(m_sock, asio::buffer(request)); }
std::string receiveResponse() {
asio::streambuf buf;
asio::read_until(m_sock, buf, '\n');
std::string response;
getline(std::istream(&buf), response);
return response;
}
private:
asio::io_service m_ios;
asio::ip::tcp::endpoint m_ep;
asio::ip::tcp::socket m_sock;
};
int main() {
const std::string raw_ip_address = "127.0.0.1";
const unsigned short port_num = 3333;
try {
SyncTCPClient client{ raw_ip_address, port_num };
// Sync connect.
client.connect();
std::cout << "Sending request to the server...\n";
std::string response = client.emulateLongComputationOp(10);
std::cout << "Response received: " << response << std::endl;
} catch (std::system_error &e) {
std::ofstream log("client.log", std::ios_base::app | std::ios_base::out);
log << e.what() << " " << e.code() << ": " << e.code().message() << std::endl;
return e.code().value();
}
}
答案 1 :(得分:0)
@sehe
通过运行此客户端代码,我有时在OSX中只有相同的例外。
Linux,Windows不会触发异常。
注意:服务器端不会关闭连接
for (size_t idx = 0; idx < nbr_rep; idx++)
{
try
{
tcp::socket socket(io_service);
asio::connect(socket, resolver.resolve(host, port_num));
asio::write(socket, asio::buffer(request, request.size()));
//receive response
http_msg_t http;
if (http::parse(socket, http) < 0)
{
}
//close after receiving acknowledgement
socket.shutdown(asio::ip::tcp::socket::shutdown_both);
socket.close();
}
catch (std::exception& e)
{
events::log("exception: " + std::string(e.what()));
}
std::this_thread::sleep_for(std::chrono::seconds(nbr_wai));
}
这会循环
sample ::仅在迭代编号306中发生异常
input_sender:19:11:43 304 sent 2113 bytes
input_sender:19:11:43 received: HTTP/1.1 200 OK
input_sender:19:11:45 305 sent 2113 bytes
input_sender:19:11:45 received: HTTP/1.1 200 OK
input_sender:19:11:45 exception: shutdown: Socket is not connected
input_sender:19:11:47 306 sent 2113 bytes
input_sender:19:11:48 received: HTTP/1.1 200 OK
http :: parse只是从套接字读取
int http::parse(asio::ip::tcp::socket& sock, http_msg_t& http)
{
std::string line;
asio::streambuf buf;
std::stringstream ss;
asio::error_code error;
size_t content_size = 0;
size_t read_left = 0;
try
{
//read until end of HTTP header
//Note:: after a successful read_until operation, the streambuf may contain additional data
//beyond the delimiter. An application will typically leave that data in the streambuf for a subsequent
//read_until operation to examine.
asio::read_until(sock, buf, "\r\n\r\n");
std::istream stream(&buf);
while (std::getline(stream, line) && line != "\r")
{
http.header.push_back(line);
}
//store method and url
line = http.header.at(0);
http.method = http::http_get_method(line);
http.url = http::http_get_url(line);
//find 'Content-Length'
for (int idx = 0; idx < http.header.size(); idx++)
{
line = http.header.at(idx);
if (line.find("Content-Length: ") != std::string::npos)
{
size_t start = line.find(":");
start += 2; //space
size_t end = line.find("\r");
std::string s = line.substr(start, end - 1);
try
{
content_size = std::atoi(s.c_str());
}
catch (std::invalid_argument&)
{
events::log("invalid Content-Length");
return -1;
}
http.content_size = content_size;
}
}
if (http.content_size == 0)
{
//nothing to read; not a POST; must be an acknowledgement response
return 0;
}
//read end of message left
//dump whatever content we already have
if (buf.size() > 0)
{
ss << &buf;
std::string s = ss.str();
read_left = content_size - s.size();
}
else
{
read_left = content_size;
}
//asio::read reads exact number of bytes
size_t recv = asio::read(sock, buf, asio::transfer_exactly(read_left));
ss << &buf;
}
catch (std::exception& e)
{
events::log(e.what());
return -1;
}
http.msg = ss.str();
return 0;
}