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

mavlink / MAVSDK / 21930006145

12 Feb 2026 01:29AM UTC coverage: 49.036% (+0.009%) from 49.027%
21930006145

Pull #2765

github

web-flow
Merge 8cb4b3661 into ef29d2ec8
Pull Request #2765: Docs: Update broken links and .gitignore for new docs location

18363 of 37448 relevant lines covered (49.04%)

675.12 hits per line

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

73.6
/cpp/src/mavsdk/core/tcp_client_connection.cpp
1
#include "tcp_client_connection.h"
2
#include "log.h"
3

4
#include <cassert>
5
#include <sstream>
6
#include <utility>
7
#include <thread>
8
#include <sstream>
9

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

22
namespace mavsdk {
23

24
/* change to remote_ip and remote_port */
25
TcpClientConnection::TcpClientConnection(
5✔
26
    Connection::ReceiverCallback receiver_callback,
27
    Connection::LibmavReceiverCallback libmav_receiver_callback,
28
    MavsdkImpl& mavsdk_impl,
29
    std::string remote_ip,
30
    int remote_port,
31
    ForwardingOption forwarding_option) :
5✔
32
    Connection(
33
        std::move(receiver_callback),
5✔
34
        std::move(libmav_receiver_callback),
5✔
35
        mavsdk_impl,
36
        forwarding_option),
37
    _remote_ip(std::move(remote_ip)),
5✔
38
    _remote_port_number(remote_port),
5✔
39
    _should_exit(false)
15✔
40
{}
5✔
41

42
TcpClientConnection::~TcpClientConnection()
10✔
43
{
44
    // If no one explicitly called stop before, we should at least do it.
45
    stop();
5✔
46
}
10✔
47

48
ConnectionResult TcpClientConnection::start()
5✔
49
{
50
    if (!start_mavlink_receiver()) {
5✔
51
        return ConnectionResult::ConnectionsExhausted;
×
52
    }
53

54
    if (!start_libmav_receiver()) {
5✔
55
        return ConnectionResult::ConnectionsExhausted;
×
56
    }
57

58
    ConnectionResult ret = setup_port();
5✔
59
    if (ret != ConnectionResult::Success) {
5✔
60
        return ret;
×
61
    }
62

63
    start_recv_thread();
5✔
64

65
    return ConnectionResult::Success;
5✔
66
}
67

68
ConnectionResult TcpClientConnection::setup_port()
9✔
69
{
70
#ifdef WINDOWS
71
    WSADATA wsa;
72
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
73
        LogErr() << "Error: Winsock failed, error: " << get_socket_error_string(WSAGetLastError());
74
        return ConnectionResult::SocketError;
75
    }
76
#endif
77

78
    std::lock_guard<std::mutex> lock(_mutex);
9✔
79

80
    int new_fd = socket(AF_INET, SOCK_STREAM, 0);
9✔
81

82
    if (new_fd < 0) {
9✔
83
        LogErr() << "socket error" << strerror(errno);
×
84
        return ConnectionResult::SocketError;
×
85
    }
86

87
    struct sockaddr_in remote_addr{};
9✔
88
    remote_addr.sin_family = AF_INET;
9✔
89
    remote_addr.sin_port = htons(_remote_port_number);
9✔
90

91
    struct hostent* hp;
92
    hp = gethostbyname(_remote_ip.c_str());
9✔
93
    if (hp == nullptr) {
9✔
94
        LogErr() << "Could not get host by name";
×
95
        return ConnectionResult::SocketConnectionError;
×
96
    }
97

98
    memcpy(&remote_addr.sin_addr, hp->h_addr, hp->h_length);
9✔
99

100
    if (connect(new_fd, reinterpret_cast<sockaddr*>(&remote_addr), sizeof(struct sockaddr_in)) <
9✔
101
        0) {
102
        LogErr() << "Connect error: " << strerror(errno);
3✔
103
        return ConnectionResult::SocketConnectionError;
3✔
104
    }
105

106
    // Set receive timeout cross-platform
107
    const unsigned timeout_ms = 500;
6✔
108

109
#if defined(WINDOWS)
110
    setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
111
#else
112
    struct timeval tv;
113
    tv.tv_sec = 0;
6✔
114
    tv.tv_usec = timeout_ms * 1000;
6✔
115
    setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const void*)&tv, sizeof(tv));
6✔
116
#endif
117

118
    _socket_fd.reset(new_fd);
6✔
119

120
    return ConnectionResult::Success;
6✔
121
}
9✔
122

123
void TcpClientConnection::start_recv_thread()
5✔
124
{
125
    _recv_thread = std::make_unique<std::thread>(&TcpClientConnection::receive, this);
5✔
126
}
5✔
127

128
ConnectionResult TcpClientConnection::stop()
5✔
129
{
130
    _should_exit = true;
5✔
131

132
    if (_recv_thread) {
5✔
133
        _recv_thread->join();
5✔
134
        _recv_thread.reset();
5✔
135
    }
136

137
    {
138
        std::lock_guard<std::mutex> lock(_mutex);
5✔
139
        _socket_fd.close();
5✔
140
    }
5✔
141

142
    // We need to stop this after stopping the receive thread, otherwise
143
    // it can happen that we interfere with the parsing of a message.
144
    stop_mavlink_receiver();
5✔
145

146
    return ConnectionResult::Success;
5✔
147
}
148

149
std::pair<bool, std::string> TcpClientConnection::send_message(const mavlink_message_t& message)
45✔
150
{
151
    // Convert message to raw bytes and use common send path
152
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
153
    uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
45✔
154

155
    return send_raw_bytes(reinterpret_cast<const char*>(buffer), buffer_len);
45✔
156
}
157

158
std::pair<bool, std::string> TcpClientConnection::send_raw_bytes(const char* bytes, size_t length)
45✔
159
{
160
    std::pair<bool, std::string> result;
45✔
161

162
    if (_remote_ip.empty()) {
45✔
163
        result.first = false;
×
164
        result.second = "Remote IP unknown";
×
165
        LogErr() << result.second;
×
166
        return result;
×
167
    }
168

169
    if (_remote_port_number == 0) {
45✔
170
        result.first = false;
×
171
        result.second = "Remote port unknown";
×
172
        LogErr() << result.second;
×
173
        return result;
×
174
    }
175

176
    // Hold mutex during send to prevent socket from being closed mid-send
177
    std::lock_guard<std::mutex> lock(_mutex);
45✔
178

179
    if (_socket_fd.empty()) {
45✔
180
        result.first = false;
×
181
        result.second = "Not connected";
×
182
        return result;
×
183
    }
184

185
#if !defined(MSG_NOSIGNAL)
186
    auto flags = 0;
187
#else
188
    auto flags = MSG_NOSIGNAL;
45✔
189
#endif
190

191
    const auto send_len = send(_socket_fd.get(), bytes, length, flags);
45✔
192

193
    if (send_len != static_cast<std::remove_cv_t<decltype(send_len)>>(length)) {
45✔
194
        std::stringstream ss;
×
195
        ss << "Send failure: " << strerror(errno);
×
196
        LogErr() << ss.str();
×
197
        result.first = false;
×
198
        result.second = ss.str();
×
199
        return result;
×
200
    }
×
201

202
    result.first = true;
45✔
203
    return result;
45✔
204
}
45✔
205

206
void TcpClientConnection::receive()
5✔
207
{
208
    // Enough for MTU 1500 bytes.
209
    char buffer[2048];
210

211
    while (!_should_exit) {
74✔
212
        // Get socket fd with mutex protection, then release mutex before blocking recv
213
        SocketHolder::DescriptorType socket_fd;
214
        {
215
            std::lock_guard<std::mutex> lock(_mutex);
69✔
216
            if (_socket_fd.empty()) {
69✔
217
                // Socket not connected, need to reconnect
218
                socket_fd = SocketHolder::invalid_socket_fd;
×
219
            } else {
220
                socket_fd = _socket_fd.get();
69✔
221
            }
222
        }
69✔
223

224
        if (socket_fd == SocketHolder::invalid_socket_fd) {
69✔
225
            std::this_thread::sleep_for(std::chrono::seconds(1));
×
226
            setup_port();
×
227
            continue;
×
228
        }
229

230
        const auto recv_len = recv(socket_fd, buffer, sizeof(buffer), 0);
69✔
231

232
        if (recv_len == 0) {
69✔
233
            // Connection closed, just try again.
234
            LogInfo() << "TCP connection closed, trying to reconnect...";
4✔
235
            std::this_thread::sleep_for(std::chrono::seconds(1));
4✔
236
            setup_port();
4✔
237
            continue;
4✔
238
        }
239

240
        if (recv_len < 0) {
65✔
241
#ifdef WINDOWS
242
            int err = WSAGetLastError();
243
            if (err == WSAEWOULDBLOCK || err == WSAETIMEDOUT) {
244
                // Timeout, just try again.
245
                continue;
246
            }
247
            LogErr() << "TCP receive error: " << get_socket_error_string(err)
248
                     << ", trying to reconnect...";
249
#else
250
            if (errno == EAGAIN || errno == ETIMEDOUT) {
20✔
251
                // Timeout, just try again.
252
                continue;
20✔
253
            }
254
            LogErr() << "TCP receive error: " << strerror(errno) << ", trying to reconnect...";
×
255
#endif
256
            std::this_thread::sleep_for(std::chrono::seconds(1));
×
257
            setup_port();
×
258
            continue;
×
259
        }
260

261
        _mavlink_receiver->set_new_datagram(buffer, static_cast<int>(recv_len));
45✔
262

263
        auto parse_result = _mavlink_receiver->parse_message();
45✔
264
        while (parse_result != MavlinkReceiver::ParseResult::NoneAvailable) {
92✔
265
            receive_message(parse_result, _mavlink_receiver->get_last_message(), this);
47✔
266
            parse_result = _mavlink_receiver->parse_message();
47✔
267
        }
268

269
        // Also parse with libmav if available
270
        if (_libmav_receiver) {
45✔
271
            _libmav_receiver->set_new_datagram(buffer, static_cast<int>(recv_len));
45✔
272

273
            while (_libmav_receiver->parse_message()) {
92✔
274
                receive_libmav_message(_libmav_receiver->get_last_message(), this);
47✔
275
            }
276
        }
277
    }
278
}
5✔
279

280
} // 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