• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mavlink / MAVSDK / 18324161155

07 Oct 2025 07:39PM UTC coverage: 47.638% (+0.03%) from 47.607%
18324161155

push

github

web-flow
Merge pull request #2678 from mavlink/pr-connection-error-cleanup

core: improve connection error strings, fix threading issue

4 of 24 new or added lines in 3 files covered. (16.67%)

7 existing lines in 3 files now uncovered.

17051 of 35793 relevant lines covered (47.64%)

448.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.45
/src/mavsdk/core/tcp_server_connection.cpp
1
#include "tcp_server_connection.h"
2
#include "log.h"
3

4
#include <cassert>
5
#include <fcntl.h>
6
#include <sstream>
7

8
#ifdef WINDOWS
9
#ifndef MINGW
10
#pragma comment(lib, "Ws2_32.lib") // Without this, Ws2_32.lib is not included in static library.
11
#endif
12
#include <Windows.h>
13
#include <Winsock2.h>
14
#include <ws2tcpip.h>
15
#else
16
#include <netinet/in.h>
17
#include <sys/select.h>
18
#include <sys/socket.h>
19
#include <arpa/inet.h>
20
#include <errno.h>
21
#include <netdb.h>
22
#endif
23

24
namespace mavsdk {
25
TcpServerConnection::TcpServerConnection(
1✔
26
    Connection::ReceiverCallback receiver_callback,
27
    Connection::LibmavReceiverCallback libmav_receiver_callback,
28
    MavsdkImpl& mavsdk_impl,
29
    std::string local_ip,
30
    int local_port,
31
    ForwardingOption forwarding_option) :
1✔
32
    Connection(
33
        std::move(receiver_callback),
1✔
34
        std::move(libmav_receiver_callback),
1✔
35
        mavsdk_impl,
36
        forwarding_option),
37
    _local_ip(std::move(local_ip)),
1✔
38
    _local_port(local_port)
3✔
39
{}
1✔
40

41
TcpServerConnection::~TcpServerConnection()
2✔
42
{
43
    stop();
1✔
44
}
2✔
45

46
ConnectionResult TcpServerConnection::start()
1✔
47
{
48
    if (!start_mavlink_receiver()) {
1✔
49
        return ConnectionResult::ConnectionsExhausted;
×
50
    }
51

52
    if (!start_libmav_receiver()) {
1✔
53
        return ConnectionResult::ConnectionsExhausted;
×
54
    }
55

56
#ifdef WINDOWS
57
    WSADATA wsa;
58
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
59
        LogErr() << "Error: Winsock failed, error: " << get_socket_error_string(WSAGetLastError());
60
        return ConnectionResult::SocketError;
61
    }
62
#endif
63

64
    _server_socket_fd.reset(socket(AF_INET, SOCK_STREAM, 0));
1✔
65
    if (_server_socket_fd.empty()) {
1✔
NEW
66
        LogErr() << "socket error: " << strerror(errno);
×
67
        return ConnectionResult::SocketError;
×
68
    }
69

70
    sockaddr_in server_addr{};
1✔
71
    server_addr.sin_family = AF_INET;
1✔
72
    server_addr.sin_addr.s_addr = INADDR_ANY;
1✔
73
    server_addr.sin_port = htons(_local_port);
1✔
74

75
    if (bind(
1✔
76
            _server_socket_fd.get(),
77
            reinterpret_cast<sockaddr*>(&server_addr),
78
            sizeof(server_addr)) < 0) {
1✔
NEW
79
        LogErr() << "bind error: " << strerror(errno);
×
80
        return ConnectionResult::SocketError;
×
81
    }
82

83
    if (listen(_server_socket_fd.get(), 3) < 0) {
1✔
NEW
84
        LogErr() << "listen error: " << strerror(errno);
×
85
        return ConnectionResult::SocketError;
×
86
    }
87

88
    // Set receive timeout cross-platform
89
    const unsigned timeout_ms = 500;
1✔
90

91
#if defined(WINDOWS)
92
    setsockopt(
93
        _server_socket_fd.get(),
94
        SOL_SOCKET,
95
        SO_RCVTIMEO,
96
        (const char*)&timeout_ms,
97
        sizeof(timeout_ms));
98
    setsockopt(
99
        _client_socket_fd.get(),
100
        SOL_SOCKET,
101
        SO_RCVTIMEO,
102
        (const char*)&timeout_ms,
103
        sizeof(timeout_ms));
104
#else
105
    struct timeval tv;
106
    tv.tv_sec = 0;
1✔
107
    tv.tv_usec = timeout_ms * 1000;
1✔
108
    setsockopt(_server_socket_fd.get(), SOL_SOCKET, SO_RCVTIMEO, (const void*)&tv, sizeof(tv));
1✔
109
    setsockopt(_client_socket_fd.get(), SOL_SOCKET, SO_RCVTIMEO, (const void*)&tv, sizeof(tv));
1✔
110
#endif
111

112
    _accept_receive_thread =
113
        std::make_unique<std::thread>(&TcpServerConnection::accept_client, this);
1✔
114

115
    return ConnectionResult::Success;
1✔
116
}
117

118
ConnectionResult TcpServerConnection::stop()
1✔
119
{
120
    _should_exit = true;
1✔
121

122
    if (_accept_receive_thread && _accept_receive_thread->joinable()) {
1✔
123
        _accept_receive_thread->join();
1✔
124
        _accept_receive_thread.reset();
1✔
125
    }
126

127
    _client_socket_fd.close();
1✔
128
    _server_socket_fd.close();
1✔
129

130
    // We need to stop this after stopping the receive thread, otherwise
131
    // it can happen that we interfere with the parsing of a message.
132
    stop_mavlink_receiver();
1✔
133

134
    return ConnectionResult::Success;
1✔
135
}
136

137
std::pair<bool, std::string> TcpServerConnection::send_message(const mavlink_message_t& message)
7✔
138
{
139
    // Convert message to raw bytes and use common send path
140
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
141
    uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
7✔
142

143
    assert(buffer_len <= MAVLINK_MAX_PACKET_LEN);
7✔
144

145
    return send_raw_bytes(reinterpret_cast<const char*>(buffer), buffer_len);
7✔
146
}
147

148
void TcpServerConnection::accept_client()
1✔
149
{
150
#ifdef WINDOWS
151
    // Set server socket to non-blocking
152
    u_long iMode = 1;
153
    int iResult = ioctlsocket(_server_socket_fd.get(), FIONBIO, &iMode);
154
    if (iResult != 0) {
155
        LogErr() << "ioctlsocket failed with error: " << get_socket_error_string(WSAGetLastError());
156
    }
157
#else
158
    // Set server socket to non-blocking
159
    int flags = fcntl(_server_socket_fd.get(), F_GETFL, 0);
1✔
160
    fcntl(_server_socket_fd.get(), F_SETFL, flags | O_NONBLOCK);
1✔
161
#endif
162

163
    while (!_should_exit) {
2✔
164
        fd_set readfds;
165
        FD_ZERO(&readfds);
17✔
166
        FD_SET(_server_socket_fd.get(), &readfds);
1✔
167

168
        // Set timeout to 1 second
169
        timeval timeout;
170
        timeout.tv_sec = 1;
1✔
171
        timeout.tv_usec = 0;
1✔
172

173
        const int activity =
174
            select(_server_socket_fd.get() + 1, &readfds, nullptr, nullptr, &timeout);
1✔
175

176
        if (activity < 0 && errno != EINTR) {
1✔
NEW
177
            LogErr() << "select error: " << strerror(errno);
×
178
            continue;
×
179
        }
180

181
        if (activity == 0) {
1✔
182
            // Timeout, no incoming connection
183
            continue;
×
184
        }
185

186
        if (FD_ISSET(_server_socket_fd.get(), &readfds)) {
1✔
187
            sockaddr_in client_addr{};
1✔
188
            socklen_t client_addr_len = sizeof(client_addr);
1✔
189

190
            {
191
                _client_socket_fd.reset(accept(
1✔
192
                    _server_socket_fd.get(),
193
                    reinterpret_cast<sockaddr*>(&client_addr),
194
                    &client_addr_len));
195
            }
196
            if (_client_socket_fd.empty()) {
1✔
197
                if (_should_exit) {
×
198
                    return;
×
199
                }
NEW
200
                LogErr() << "accept error: " << strerror(errno);
×
201
                continue;
×
202
            }
203

204
            receive();
1✔
205
        }
206
    }
207
}
208

209
void TcpServerConnection::receive()
1✔
210
{
211
    std::array<char, 2048> buffer{};
1✔
212

213
    bool dataReceived = false;
1✔
214
    while (!dataReceived && !_should_exit) {
14✔
215
        const auto recv_len = recv(_client_socket_fd.get(), buffer.data(), buffer.size(), 0);
13✔
216

217
#ifdef WINDOWS
218
        if (recv_len == SOCKET_ERROR) {
219
            // On Windows, on the first try, select says there is something
220
            // but recv doesn't succeed yet, and we just need to try again.
221
            if (WSAGetLastError() == WSAEWOULDBLOCK) {
222
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
223
                continue;
224
            }
225
            // And at the end, we get an abort that we can silently ignore.
226
            if (WSAGetLastError() == WSAECONNABORTED) {
227
                return;
228
            }
229
        }
230
#else
231
        if (recv_len < 0) {
13✔
232
            // On macOS we presumably see the same thing, and have to try again.
233
            if (errno == EAGAIN) {
2✔
234
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
2✔
235
                continue;
2✔
236
            }
237

238
            // Connection reset - if shutting down, exit quietly; otherwise log and exit
NEW
239
            if (errno == ECONNRESET) {
×
NEW
240
                if (!_should_exit) {
×
NEW
241
                    LogErr() << "recv failed: " << strerror(errno);
×
242
                }
NEW
243
                return;
×
244
            }
245

NEW
246
            LogErr() << "recv failed: " << strerror(errno);
×
UNCOV
247
            return;
×
248
        }
249
#endif
250

251
        if (recv_len == 0) {
11✔
UNCOV
252
            continue;
×
253
        }
254

255
        _mavlink_receiver->set_new_datagram(buffer.data(), static_cast<int>(recv_len));
11✔
256

257
        // Parse all mavlink messages in one data packet. Once exhausted, we'll exit while.
258
        while (_mavlink_receiver->parse_message()) {
23✔
259
            receive_message(_mavlink_receiver->get_last_message(), this);
12✔
260
        }
261

262
        // Also parse with libmav if available
263
        if (_libmav_receiver) {
11✔
264
            _libmav_receiver->set_new_datagram(buffer.data(), static_cast<int>(recv_len));
11✔
265

266
            while (_libmav_receiver->parse_message()) {
22✔
267
                receive_libmav_message(_libmav_receiver->get_last_message(), this);
11✔
268
            }
269
        }
270
    }
271
}
272

273
std::pair<bool, std::string> TcpServerConnection::send_raw_bytes(const char* bytes, size_t length)
7✔
274
{
275
    // Basic implementation for TCP server connections
276
    std::pair<bool, std::string> result;
7✔
277

278
#if !defined(MSG_NOSIGNAL)
279
    auto flags = 0;
280
#else
281
    auto flags = MSG_NOSIGNAL;
7✔
282
#endif
283

284
    const auto send_len = send(_client_socket_fd.get(), bytes, length, flags);
7✔
285

286
    if (send_len != static_cast<std::remove_cv_t<decltype(send_len)>>(length)) {
7✔
287
        // Broken pipe is expected during shutdown, don't log it
288
        std::stringstream ss;
×
289
#ifdef WINDOWS
290
        int err = WSAGetLastError();
291
        ss << "Send failure: " << get_socket_error_string(err);
292
        if (err != WSAECONNRESET || !_should_exit) {
293
            LogErr() << ss.str();
294
        }
295
#else
NEW
296
        ss << "Send failure: " << strerror(errno);
×
NEW
297
        if (errno != EPIPE || !_should_exit) {
×
NEW
298
            LogErr() << ss.str();
×
299
        }
300
#endif
301
        result.first = false;
×
302
        result.second = ss.str();
×
303
        return result;
×
304
    }
×
305

306
    result.first = true;
7✔
307
    return result;
7✔
308
}
309

310
} // namespace mavsdk
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc