blob: b4c18d085a3d00e82f644f9ef86d0d916db529f6 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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 "tsocket.hpp"
#include <sstream>
#ifndef _WIN32
#include <arpa/inet.h>
#include <cstring>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#endif
Socket::Socket(long timeout /*= SOCKET_TIMEOUT_IN_SECONDS*/)
: timeout_(timeout) {
#ifdef _WIN32
initialize_sockets();
#endif
}
Socket::~Socket() {
#ifdef _WIN32
closesocket(handle_);
WSACleanup();
#else
close(handle_);
#endif
}
SOCKET_HANDLE Socket::get_handle() { return handle_; }
#ifdef _WIN32
void Socket::initialize_sockets() {
WSADATA wsa_data;
int error_code = WSAStartup(MAKEWORD(2, 0), &wsa_data);
if (error_code != 0) {
std::string message = get_error_message(error_code);
throw SocketException("Unable to initialize Windows sockets: " + message);
}
}
#endif
std::string Socket::get_error_message(int error_code) const {
// Format the error code message using the default system language
std::stringstream message;
#ifdef _WIN32
LPVOID error_string;
int size =
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&error_string, 0, NULL);
// Ensure the message could be retrieved and update the return message
if (size) {
message << std::string((LPTSTR)error_string, size);
LocalFree(error_string);
}
#else
message << std::string(strerror(error_code));
#endif
message << " [" << error_code << "]";
return message.str();
}
void Socket::synchronize(bool is_read, bool is_write) {
fd_set socket;
FD_ZERO(&socket);
FD_SET(handle_, &socket);
// Determine current read/write direction of the session
fd_set* read = NULL;
fd_set* write = NULL;
if (is_read) {
read = &socket;
}
if (is_write) {
write = &socket;
}
// Get the status of the socket (synchronize/wait on socket)
struct timeval timeout;
timeout.tv_sec = timeout_;
timeout.tv_usec = 0;
if (select(static_cast<int>(handle_) + 1, read, write, NULL, &timeout) == SOCKET_ERROR) {
std::string message = "Failed to Synchronize Socket: ";
#ifdef _WIN32
message += get_error_message(GetLastError());
#else
message += get_error_message(errno);
#endif
throw SocketException(message);
}
}
void Socket::establish_connection(const std::string& ip_address, unsigned short port) {
handle_ = socket(AF_INET, SOCK_STREAM, 0);
if (handle_ == INVALID_SOCKET) {
std::string message = "Failed to Create Socket: ";
#ifdef _WIN32
message += get_error_message(GetLastError());
#else
message += get_error_message(errno);
#endif
throw SocketException(message);
}
struct sockaddr_in ipv4_socket_address;
ipv4_socket_address.sin_family = AF_INET;
ipv4_socket_address.sin_port = htons(port);
ipv4_socket_address.sin_addr.s_addr = inet_addr(ip_address.c_str());
if (connect(handle_, (struct sockaddr*)(&ipv4_socket_address), sizeof(struct sockaddr_in)) != 0) {
std::string message = "Failed to Establish Connection: ";
#ifdef _WIN32
message += get_error_message(GetLastError());
#else
message += get_error_message(errno);
#endif
throw SocketException(message);
}
}