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

mavlink / MAVSDK / 12228184175

09 Dec 2024 03:09AM UTC coverage: 38.637% (-0.07%) from 38.708%
12228184175

push

github

web-flow
Merge pull request #2461 from mavlink/pr-connection-subscription

Add connection error subscription

13 of 93 new or added lines in 6 files covered. (13.98%)

12 existing lines in 5 files now uncovered.

12093 of 31299 relevant lines covered (38.64%)

242.86 hits per line

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

73.47
/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
#ifndef WINDOWS
25
#define GET_ERROR(_x) strerror(_x)
26
#else
27
#define GET_ERROR(_x) WSAGetLastError()
28
#endif
29

30
namespace mavsdk {
31
TcpServerConnection::TcpServerConnection(
1✔
32
    Connection::ReceiverCallback receiver_callback,
33
    std::string local_ip,
34
    int local_port,
35
    ForwardingOption forwarding_option) :
1✔
36
    Connection(std::move(receiver_callback), forwarding_option),
1✔
37
    _local_ip(std::move(local_ip)),
1✔
38
    _local_port(local_port)
3✔
39
{}
1✔
40

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

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

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

60
    _server_socket_fd.reset(socket(AF_INET, SOCK_STREAM, 0));
1✔
61
    if (_server_socket_fd.empty()) {
1✔
62
        LogErr() << "socket error: " << GET_ERROR(errno);
×
63
        return ConnectionResult::SocketError;
×
64
    }
65

66
    sockaddr_in server_addr{};
1✔
67
    server_addr.sin_family = AF_INET;
1✔
68
    server_addr.sin_addr.s_addr = INADDR_ANY;
1✔
69
    server_addr.sin_port = htons(_local_port);
1✔
70

71
    if (bind(
1✔
72
            _server_socket_fd.get(),
73
            reinterpret_cast<sockaddr*>(&server_addr),
74
            sizeof(server_addr)) < 0) {
1✔
75
        LogErr() << "bind error: " << GET_ERROR(errno);
×
76
        return ConnectionResult::SocketError;
×
77
    }
78

79
    if (listen(_server_socket_fd.get(), 3) < 0) {
1✔
80
        LogErr() << "listen error: " << GET_ERROR(errno);
×
81
        return ConnectionResult::SocketError;
×
82
    }
83

84
    _accept_receive_thread =
1✔
85
        std::make_unique<std::thread>(&TcpServerConnection::accept_client, this);
1✔
86

87
    return ConnectionResult::Success;
1✔
88
}
89

90
ConnectionResult TcpServerConnection::stop()
1✔
91
{
92
    _should_exit = true;
1✔
93

94
    _client_socket_fd.close();
1✔
95
    _server_socket_fd.close();
1✔
96

97
    if (_accept_receive_thread && _accept_receive_thread->joinable()) {
1✔
98
        _accept_receive_thread->join();
1✔
99
        _accept_receive_thread.reset();
1✔
100
    }
101

102
    // We need to stop this after stopping the receive thread, otherwise
103
    // it can happen that we interfere with the parsing of a message.
104
    stop_mavlink_receiver();
1✔
105

106
    return ConnectionResult::Success;
1✔
107
}
108

109
std::pair<bool, std::string> TcpServerConnection::send_message(const mavlink_message_t& message)
3✔
110
{
111
    std::pair<bool, std::string> result;
3✔
112

113
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
3✔
114
    uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
3✔
115

116
    assert(buffer_len <= MAVLINK_MAX_PACKET_LEN);
3✔
117

118
#if !defined(MSG_NOSIGNAL)
119
    auto flags = 0;
120
#else
121
    auto flags = MSG_NOSIGNAL;
3✔
122
#endif
123

124
    const auto send_len =
125
        send(_client_socket_fd.get(), reinterpret_cast<const char*>(buffer), buffer_len, flags);
3✔
126

127
    if (send_len != buffer_len) {
3✔
NEW
128
        std::stringstream ss;
×
NEW
129
        ss << "Send failure: " << GET_ERROR(errno);
×
NEW
130
        LogErr() << ss.str();
×
NEW
131
        result.first = false;
×
NEW
132
        result.second = ss.str();
×
NEW
133
        return result;
×
UNCOV
134
    }
×
135

136
    result.first = true;
3✔
137
    return result;
3✔
138
}
139

140
void TcpServerConnection::accept_client()
1✔
141
{
142
#ifdef WINDOWS
143
    // Set server socket to non-blocking
144
    u_long iMode = 1;
145
    int iResult = ioctlsocket(_server_socket_fd.get(), FIONBIO, &iMode);
146
    if (iResult != 0) {
147
        LogErr() << "ioctlsocket failed with error: " << WSAGetLastError();
148
    }
149
#else
150
    // Set server socket to non-blocking
151
    int flags = fcntl(_server_socket_fd.get(), F_GETFL, 0);
1✔
152
    fcntl(_server_socket_fd.get(), F_SETFL, flags | O_NONBLOCK);
1✔
153
#endif
154

155
    while (!_should_exit) {
2✔
156
        fd_set readfds;
1✔
157
        FD_ZERO(&readfds);
17✔
158
        FD_SET(_server_socket_fd.get(), &readfds);
1✔
159

160
        // Set timeout to 1 second
161
        timeval timeout;
1✔
162
        timeout.tv_sec = 1;
1✔
163
        timeout.tv_usec = 0;
1✔
164

165
        const int activity =
166
            select(_server_socket_fd.get() + 1, &readfds, nullptr, nullptr, &timeout);
1✔
167

168
        if (activity < 0 && errno != EINTR) {
1✔
169
            LogErr() << "select error: " << GET_ERROR(errno);
×
170
            continue;
×
171
        }
172

173
        if (activity == 0) {
1✔
174
            // Timeout, no incoming connection
175
            continue;
×
176
        }
177

178
        if (FD_ISSET(_server_socket_fd.get(), &readfds)) {
1✔
179
            sockaddr_in client_addr{};
1✔
180
            socklen_t client_addr_len = sizeof(client_addr);
1✔
181

182
            _client_socket_fd.reset(accept(
1✔
183
                _server_socket_fd.get(),
184
                reinterpret_cast<sockaddr*>(&client_addr),
185
                &client_addr_len));
186
            if (_client_socket_fd.empty()) {
1✔
187
                if (_should_exit) {
×
188
                    return;
×
189
                }
190
                LogErr() << "accept error: " << GET_ERROR(errno);
×
191
                continue;
×
192
            }
193

194
            receive();
1✔
195
        }
196
    }
197
}
198

199
void TcpServerConnection::receive()
1✔
200
{
201
    std::array<char, 2048> buffer{};
1✔
202

203
    bool dataReceived = false;
1✔
204
    while (!dataReceived && !_should_exit) {
19,494✔
205
        const auto recv_len = recv(_client_socket_fd.get(), buffer.data(), buffer.size(), 0);
19,493✔
206

207
#ifdef WINDOWS
208
        if (recv_len == SOCKET_ERROR) {
209
            // On Windows, on the first try, select says there is something
210
            // but recv doesn't succeed yet, and we just need to try again.
211
            if (WSAGetLastError() == WSAEWOULDBLOCK) {
212
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
213
                continue;
214
            }
215
            // And at the end, we get an abort that we can silently ignore.
216
            if (WSAGetLastError() == WSAECONNABORTED) {
217
                return;
218
            }
219
        }
220
#else
221
        if (recv_len < 0) {
19,493✔
222
            // On macOS we presumably see the same thing, and have to try again.
223
            if (errno == EAGAIN) {
×
224
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
×
225
                continue;
×
226
            }
227

228
            LogErr() << "recv failed: " << GET_ERROR(errno);
×
229
            return;
×
230
        }
231
#endif
232

233
        if (recv_len == 0) {
19,493✔
234
            continue;
19,486✔
235
        }
236

237
        _mavlink_receiver->set_new_datagram(buffer.data(), static_cast<int>(recv_len));
7✔
238

239
        // Parse all mavlink messages in one data packet. Once exhausted, we'll exit while.
240
        while (_mavlink_receiver->parse_message()) {
14✔
241
            receive_message(_mavlink_receiver->get_last_message(), this);
7✔
242
        }
243
    }
244
}
245

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