/* * Copyright 2017, 2018 Jarek Potiuk (low bandwidth trajectory follower) * * Copyright 2017, 2018 Simon Rasmussen (refactor) * * Copyright 2015, 2016 Thomas Timm Andersen (original version) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "ur_rtde_driver/log.h" #include "ur_rtde_driver/tcp_socket.h" namespace ur_rtde_driver { TCPSocket::TCPSocket() : socket_fd_(-1), state_(SocketState::Invalid) { } TCPSocket::~TCPSocket() { close(); } void TCPSocket::setOptions(int socket_fd) { int flag = 1; setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)); setsockopt(socket_fd, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(int)); } bool TCPSocket::setup(std::string& host, int port) { if (state_ == SocketState::Connected) return false; LOG_INFO("Setting up connection: %s:%d", host.c_str(), port); // gethostbyname() is deprecated so use getadderinfo() as described in: // http://www.beej.us/guide/bgnet/output/html/multipage/syscalls.html#getaddrinfo const char* host_name = host.empty() ? nullptr : host.c_str(); std::string service = std::to_string(port); struct addrinfo hints, *result; std::memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if (getaddrinfo(host_name, service.c_str(), &hints, &result) != 0) { LOG_ERROR("Failed to get address for %s:%d", host.c_str(), port); return false; } bool connected = false; // loop through the list of addresses untill we find one that's connectable for (struct addrinfo* p = result; p != nullptr; p = p->ai_next) { socket_fd_ = ::socket(p->ai_family, p->ai_socktype, p->ai_protocol); if (socket_fd_ != -1 && open(socket_fd_, p->ai_addr, p->ai_addrlen)) { connected = true; break; } } freeaddrinfo(result); if (!connected) { state_ = SocketState::Invalid; LOG_ERROR("Connection setup failed for %s:%d", host.c_str(), port); } else { setOptions(socket_fd_); state_ = SocketState::Connected; LOG_INFO("Connection established for %s:%d", host.c_str(), port); } return connected; } bool TCPSocket::setSocketFD(int socket_fd) { if (state_ == SocketState::Connected) return false; socket_fd_ = socket_fd; state_ = SocketState::Connected; return true; } void TCPSocket::close() { if (state_ != SocketState::Connected) return; state_ = SocketState::Closed; ::close(socket_fd_); socket_fd_ = -1; } std::string TCPSocket::getIP() { sockaddr_in name; socklen_t len = sizeof(name); int res = ::getsockname(socket_fd_, (sockaddr*)&name, &len); if (res < 0) { LOG_ERROR("Could not get local IP"); return std::string(); } char buf[128]; inet_ntop(AF_INET, &name.sin_addr, buf, sizeof(buf)); return std::string(buf); } bool TCPSocket::read(char* character) { size_t read_chars; // It's inefficient, but in our case we read very small messages // and the overhead connected with reading character by character is // negligible - adding buffering would complicate the code needlessly. return read((uint8_t*)character, 1, read_chars); } bool TCPSocket::read(uint8_t* buf, size_t buf_len, size_t& read) { read = 0; if (state_ != SocketState::Connected) return false; ssize_t res = ::recv(socket_fd_, buf, buf_len, 0); if (res == 0) { state_ = SocketState::Disconnected; return false; } else if (res < 0) return false; read = static_cast(res); return true; } bool TCPSocket::write(const uint8_t* buf, size_t buf_len, size_t& written) { written = 0; if (state_ != SocketState::Connected) return false; size_t remaining = buf_len; // handle partial sends while (written < buf_len) { ssize_t sent = ::send(socket_fd_, buf + written, remaining, 0); if (sent <= 0) return false; written += sent; remaining -= sent; } return true; } } // namespace ur_rtde_driver