Boost asio TCP server splits incoming message into two parts

Hi.

I have written a plugin using boost::asio that acts as a server. It receives messages from the client that are sent every 5 seconds, which look like this:

MESSAGEID:NA|NA|NA|NA\n

Upon receiving these messages from the client, the server checks for the message ID (MESSAGEID), performs further actions based on this ID, and sends a relevant response back. This is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
class tcp_connection : public boost::enable_shared_from_this<tcp_connection>
{
public:
    
    typedef boost::shared_ptr<tcp_connection> pointer;
    
    
    static pointer create(boost::asio::io_service& io_service)
    {
        return pointer(new tcp_connection(io_service));
    }
    
    
    tcp::socket& socket()
    {
        return socket_;
    }
    
    
    void startConnection()
    {
        start_read();
    }

    
    void stopConnection()
    {
        socket_.close();
    }
    
    
private:
    
    tcp_connection(boost::asio::io_service& io_service) : socket_(io_service)
    {
    }
    

    void start_read()
    {
        boost::asio::async_read(socket_, input_buffer_,
                                boost::asio::transfer_at_least(1),
                                boost::bind(&tcp_connection::handle_read, shared_from_this(),
                                            boost::asio::placeholders::error));
    }
    

    void handle_read(const boost::system::error_code& error)
    {
        if (!error)
        {
            boost::asio::streambuf::const_buffers_type bufs = input_buffer_.data();
            std::string msgstr(boost::asio::buffers_begin(bufs),
                               boost::asio::buffers_begin(bufs) +
                               input_buffer_.size());
            
            // Remove the first part of the buffer to prevent from accumulating
            input_buffer_.consume(input_buffer_.size());
            
            // The received messaged is stored in string msgstr.
            // This string must be divided into two substrings using delimitor ":"
            // The first part of string is the message that determines the response from the server
            
            std::vector<std::string> msgVector;
            boost::split(msgVector, msgstr, boost::is_any_of(":"));
            
            
            if(msgVector.size() >= 2)
            {
                messageFromClient_ = msgVector[0];
                valueFromClient_ = msgVector[1];
                
                // SAAB-340A
                if (messageFromClient_ == "MESSAGEID1")
                {
                   // Do something
                }
                    
                else if (messageFromClient_ == "MESSAGEID2")
                {
                   // Do something else
                }
                
                boost::asio::async_write(socket_, boost::asio::buffer(messageToClient_),
                                         boost::bind(&tcp_connection::handle_write, shared_from_this(),
                                                     boost::asio::placeholders::error,
                                                     boost::asio::placeholders::bytes_transferred));
                
                start_read();
            }
        }
        else
        {
            stopConnection();
        }
    }
    
    
    void handle_write(const boost::system::error_code& /*error*/,
                      size_t /*bytes_transferred*/)
    {
    }
    
    tcp::socket socket_;
    boost::asio::streambuf input_buffer_;
};



class tcp_server
{
public:
    
    tcp_server(boost::asio::io_service& io_service)
    : acceptor_(io_service, tcp::endpoint(tcp::v4(), tcpPort))
    

    {
        start_accept();
    }
    
    
    void start_accept()
    {
        
        tcp_connection::pointer new_connection = tcp_connection::create(acceptor_.get_io_service());
        
        acceptor_.async_accept(new_connection->socket(),
                               boost::bind(&tcp_server::handle_accept, this, new_connection,
                                           boost::asio::placeholders::error));
    }
    
    
    void handle_user_read(const boost::system::error_code& err,
                          std::size_t bytes_transferred)
    {
    }
    
    
    void handle_accept(tcp_connection::pointer new_connection,
                       const boost::system::error_code& error)
    {
        if (!error)
        {
            new_connection->startConnection();
            
            start_accept();
        }
    }
    
    tcp::acceptor acceptor_;
};


The server works fine with my iOS client written in Swift. However, with the client written in Java, I get the following problem. My first message from the client is received fine and sends a response back. However, my second message always splits into two parts, like this:

M
ESSAGEID:NA|NA|NA|NA

And the connection interrupts right after reading the first part of the split message. Interesting enough, the server works fine on Mac but has this issue in Windows.

I have been trying to find the culprit for many days but I can't...

I would greatly appreciate if someone could help me!
Last edited on
TCP is a stream oriented protocol. There are no records. What you are seeing is not a violation of that principle. The bytes arrive in the right order, that's all that guaranteed.
Thanks. Any idea how I should deal with this situation?
Collect all the data until you've received a token.

Everyone has to do this. That's why all TCP protocols have some means on identifying sections. For example, HTTP uses CR LF to seperate fields and CR LF CR LF to end the request. The webserver has to wait until it receives all the relevant parts before it can do anything.
https://tools.ietf.org/html/rfc2616#section-2.2
kbw. But I thought this is exactly what I am trying to do by including a newline in the message from the client. And I also thought that the code above would wait until the newline has been received.

Thanks for the link, I will read it.

Thank you.
Last edited on
Need more testing but looks like replacing

1
2
3
4
boost::asio::async_read(socket_, input_buffer_,
                                boost::asio::transfer_at_least(1),
                                boost::bind(&tcp_connection::handle_read, shared_from_this(),
                                            boost::asio::placeholders::error));


with

1
2
3
4
boost::asio::async_read_until(socket_,
                                      input_buffer_, '\n',
                                      boost::bind(&tcp_connection::handle_read, shared_from_this(),
                                                  boost::asio::placeholders::error));


has solved the problem.
Last edited on
Topic archived. No new replies allowed.