ASIO示例代码关闭套接字之前应该

时间:2017-12-07 09:33:36

标签: c++ sockets boost-asio

我需要使用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;
}

2 个答案:

答案 0 :(得分:1)

我没有看到很多错误,我无法用显示的代码重现问题。

看到的事情

  1. 线程程序可能是静态的,因为它是无状态的(delete this是代码气味)
  2. 线程无需分离(使用boost::thread_group::join_all会更好)
  3. 您正在从服务器和客户端写入相同的日志文件;结果未定义
  4. .store()上的拼写.load()atomic<bool>是非惯用的
  5. 在任何类型的智能指针上拼出*sock.get() 不可原谅 非惯用
  6. 撰写code().value() - 吞下类别 - 是一件坏事,e.what()不是获取消息的方式(使用e.code().message())。
  7. 如果您需要flush,也可以使用std::endl
  8. 在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&代替智能指针。

  9. INTEGRATING

    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;
}