提升套接字通信无法通过一次交换

Posted

技术标签:

【中文标题】提升套接字通信无法通过一次交换【英文标题】:boost socket comms are not working past one exchange 【发布时间】:2015-12-01 22:04:21 【问题描述】:

我正在转换一个应用程序,该应用程序在两个服务之间具有非常简单的心跳/状态监控连接。由于现在除了 windows 之外还需要在 linux 上运行,我想我会使用 boost(v1.51,我无法升级 - linux 编译器太旧,windows 编译器是 Visual Studio 2005)来完成任务使其与平台无关(考虑到,我真的不希望有两个代码文件,一个用于每个操作系统,或者在整个代码中乱扔#defines,因为 boost 提供了令人愉快的阅读的可能性(6mos 后我'签到忘记了这段代码!)

我现在的问题是连接超时。实际上,它根本不起作用。

第一次通过时,“状态”消息被发送,它被服务器端接收,并发回适当的响应。服务器端然后返回到在套接字上等待另一条消息。客户端(此代码)再次发送“状态”消息......但这一次,服务器从未收到它,并且 read_some() 调用阻塞,直到套接字超时。我觉得很奇怪

服务器端没有变化。唯一改变的是我将客户端代码从基本的 winsock2 套接字更改为此代码。以前,它连接并循环发送/接收调用,直到程序中止或收到“锁定”消息。

为什么后续调用(发送)会默默地无法在套接字上发送任何内容,我需要调整什么才能恢复简单的发送/接收流程?

#include <boost/signals2/signal.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

using boost::asio::ip::tcp;
using namespace std;

boost::system::error_code ServiceMonitorThread::ConnectToPeer(
    tcp::socket &socket,
    tcp::resolver::iterator endpoint_iterator)

    boost::system::error_code error;
    int tries = 0;

    for (; tries < maxTriesBeforeAbort; tries++)
    
        boost::asio::connect(socket, endpoint_iterator, error);

        if (!error)
        
            break;
        
        else if (error != make_error_code(boost::system::errc::success))
        
            // Error connecting to service... may not be running?
            cerr << error.message() << endl;
            boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
        
    

    if (tries == maxTriesBeforeAbort)
    
        error = make_error_code(boost::system::errc::host_unreachable);
    

    return error;


// Main thread-loop routine.
void ServiceMonitorThread::run() 

    boost::system::error_code error;

    tcp::resolver resolver(io_service);
    tcp::resolver::query query(hostnameOrAddress, to_string(port));
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

    tcp::socket socket(io_service);

    error = ConnectToPeer(socket, endpoint_iterator);
    if (error && error == boost::system::errc::host_unreachable)
    
        TerminateProgram();
    

    boost::asio::streambuf command;
    std::ostream command_stream(&command);
    command_stream << "status\n";

    boost::array<char, 10> response;
    int retry = 0;

    while (retry < maxTriesBeforeAbort)
    
        // A 1s request interval is more than sufficient for status checking.
        boost::this_thread::sleep_for(boost::chrono::seconds(1));

        // Send the command to the network monitor server service.
        boost::asio::write(socket, command, error);

        if (error)
        
            // Error sending to socket
            cerr << error.message() << endl;
            retry++;
            continue;
        

        // Clear the response buffer, then read the network monitor status.
        response.assign(0);
        /* size_t bytes_read = */ socket.read_some(boost::asio::buffer(response), error);

        if (error)
        
            if (error == make_error_code(boost::asio::error::eof))
            
                // Connection was dropped, re-connect to the service.
                error = ConnectToPeer(socket, endpoint_iterator);
                if (error && error == make_error_code(boost::system::errc::host_unreachable))
                
                    TerminateProgram();
                
                continue;
            
            else 
            
                cerr << error.message() << endl;
                retry++;
                continue;
            
        

        // Examine the response message.
        if (strncmp(response.data(), "normal", 6) != 0)
        
            retry++;

            // If we received the lockdown response, then terminate.
            if (strncmp(response.data(), "lockdown", 8) == 0)
            
                break;
            

            // Not an expected response, potential error, retry to see if it was merely an aberration.
            continue;
        

        // If we arrived here, the exchange was successful; reset the retry count.
        if (retry > 0)
        
            retry = 0;
        
    

    // If retry count was incremented, then we have likely encountered an issue; shut things down.
    if (retry != 0)
    
        TerminateProgram();
    

【问题讨论】:

【参考方案1】:

streambuf 直接提供给I/O 操作作为缓冲区时,I/O 操作将通过commiting 读取数据或consuming 写入数据来适当地管理输入序列。因此,在以下代码中,command 在第一次迭代后为空:

boost::asio::streambuf command;
std::ostream command_stream(&command);
command_stream << "status\n";
// `command`'s input sequence contains "status\n".

while (retry < maxTriesBeforeAbort)

  ...

  // write all of `command`'s input sequence to the socket.
  boost::asio::write(socket, command, error);
  // `command.size()` is 0, as the write operation will consume the data.
  // Subsequent write operations with `command` will be no-ops.

  ...

一种解决方案是使用std::string 作为缓冲区:

std::string command("status\n");

while (retry < maxTriesBeforeAbort)

  ...

  boost::asio::write(socket, boost::asio::buffer(command), error);

  ...

有关streambuf 用法的更多详细信息,请考虑阅读this 答案。

【讨论】:

嗯,这是一个微妙的(或者,也许,不是那么微妙,如果它应该是显而易见的......对我来说,它不是)让一切变得不同!经过上述更正,代码现在运行良好。

以上是关于提升套接字通信无法通过一次交换的主要内容,如果未能解决你的问题,请参考以下文章

[解决]通常每个套接字地址只允许使用一次

简述Socket(套接字)通信

C#Socket通信

C#Socket通信

套接字详解

套接字—Socket