Skip to content
Snippets Groups Projects
Commit 78758f35 authored by Stéphane Del Pino's avatar Stéphane Del Pino
Browse files

Begin socket handling: core functionalities are available

On provides a way to create
- server socket (listening to a given port)
- accept connections
- client socket that can connect to an host:port
these three functionalities are the only ones accessible from pugs'
language

On the other hand, reading and writing are provided by low level
calls. On can exchange values or arrays of values (pointers+size). It
remains unclear if we should allow transfer of higher level objects,
since socket exchanges should be generally used between different
codes?
parent 0fd84c10
No related branches found
No related tags found
1 merge request!126Begin socket handling: core functionalities are available
......@@ -10,6 +10,7 @@ add_library(PugsLanguageModules
MeshModule.cpp
ModuleRepository.cpp
SchemeModule.cpp
SocketModule.cpp
UnaryOperatorRegisterForVh.cpp
UtilsModule.cpp
WriterModule.cpp
......
......@@ -6,6 +6,7 @@
#include <language/modules/MathModule.hpp>
#include <language/modules/MeshModule.hpp>
#include <language/modules/SchemeModule.hpp>
#include <language/modules/SocketModule.hpp>
#include <language/modules/UtilsModule.hpp>
#include <language/modules/WriterModule.hpp>
#include <language/utils/BasicAffectationRegistrerFor.hpp>
......@@ -56,6 +57,7 @@ ModuleRepository::ModuleRepository()
this->_subscribe(std::make_unique<MathModule>());
this->_subscribe(std::make_unique<MeshModule>());
this->_subscribe(std::make_unique<SchemeModule>());
this->_subscribe(std::make_unique<SocketModule>());
this->_subscribe(std::make_unique<UtilsModule>());
this->_subscribe(std::make_unique<WriterModule>());
}
......
#include <language/modules/SocketModule.hpp>
#include <language/utils/BuiltinFunctionEmbedder.hpp>
#include <utils/Socket.hpp>
SocketModule::SocketModule()
{
this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const Socket>>);
this->_addBuiltinFunction("createSocketServer",
std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(const uint64_t&)>>(
[](const uint64_t& port_number) -> std::shared_ptr<const Socket> {
return std::make_shared<const Socket>(createSocketServer(port_number));
}
));
this->_addBuiltinFunction("acceptSocketClient",
std::make_shared<
BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(std::shared_ptr<const Socket>)>>(
[](std::shared_ptr<const Socket> server_socket) -> std::shared_ptr<const Socket> {
return std::make_shared<const Socket>(acceptSocketClient(*server_socket));
}
));
this->_addBuiltinFunction("connectSocketServer",
std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(const std::string&,
const uint64_t&)>>(
[](const std::string& hostname,
const uint64_t& port_number) -> std::shared_ptr<const Socket> {
return std::make_shared<const Socket>(connectSocketServer(hostname, port_number));
}
));
}
void
SocketModule::registerOperators() const
{}
#ifndef SOCKET_MODULE_HPP
#define SOCKET_MODULE_HPP
#include <language/modules/BuiltinModule.hpp>
#include <language/utils/ASTNodeDataTypeTraits.hpp>
class Socket;
template <>
inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const Socket>> =
ASTNodeDataType::build<ASTNodeDataType::type_id_t>("socket");
class SocketModule : public BuiltinModule
{
public:
std::string_view
name() const final
{
return "socket";
}
void registerOperators() const final;
SocketModule();
~SocketModule() = default;
};
#endif // SOCKET_MODULE_HPP
......@@ -15,7 +15,8 @@ add_library(
RandomEngine.cpp
RevisionInfo.cpp
SignalManager.cpp
SLEPcWrapper.cpp)
SLEPcWrapper.cpp
Socket.cpp)
target_link_libraries(
PugsUtils
......
#include <utils/Exceptions.hpp>
#include <utils/Socket.hpp>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <stdexcept>
#include <sys/socket.h>
#include <unistd.h>
class Socket::Internals
{
private:
int m_socket_fd;
sockaddr_in m_address;
public:
friend Socket createSocketServer(int port_number);
friend Socket acceptSocketClient(const Socket& server);
friend Socket connectSocketServer(const std::string& server_name, int port_number);
friend std::ostream&
operator<<(std::ostream& os, const Socket::Internals& internals)
{
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf,
sizeof(hbuf), sbuf, sizeof(sbuf), NI_NAMEREQD) == 0) {
os << hbuf << ':' << sbuf;
} else if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf,
sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0) {
if (std ::string{hbuf} == "0.0.0.0") {
if (::gethostname(hbuf, NI_MAXHOST) == 0) {
os << hbuf << ':' << sbuf;
} else {
os << "localhost:" << sbuf;
}
} else {
os << hbuf << ':' << sbuf;
}
} else {
os << "<unknown host>";
}
return os;
}
int
fileDescriptor() const
{
return m_socket_fd;
}
Internals(const Internals&) = delete;
Internals(Internals&&) = delete;
Internals() = default;
~Internals()
{
close(m_socket_fd);
}
};
std::ostream&
operator<<(std::ostream& os, const Socket& socket)
{
return os << *socket.m_internals;
}
Socket
createSocketServer(int port_number)
{
auto p_socket_internals = std::make_shared<Socket::Internals>();
Socket::Internals& socket_internals = *p_socket_internals;
socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
if (socket_internals.m_socket_fd < 0) {
throw NormalError(strerror(errno));
}
socket_internals.m_address.sin_family = AF_INET;
socket_internals.m_address.sin_addr.s_addr = INADDR_ANY;
socket_internals.m_address.sin_port = htons(port_number);
int on = 1;
::setsockopt(socket_internals.m_socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (::bind(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&socket_internals.m_address),
sizeof(socket_internals.m_address)) < 0) {
throw NormalError(strerror(errno));
}
::listen(socket_internals.m_socket_fd, 1);
return Socket{p_socket_internals};
}
Socket
acceptSocketClient(const Socket& server)
{
auto p_socket_internals = std::make_shared<Socket::Internals>();
Socket::Internals& socket_internals = *p_socket_internals;
std::cout << "Waiting for connection on " << server << '\n';
socklen_t address_lenght = sizeof(socket_internals.m_address);
socket_internals.m_socket_fd = ::accept(server.m_internals->m_socket_fd,
reinterpret_cast<sockaddr*>(&socket_internals.m_address), &address_lenght);
if (socket_internals.m_socket_fd < 0) {
throw NormalError(strerror(errno));
}
Socket client(p_socket_internals);
std::cout << "Connected from " << client << '\n';
return client;
}
Socket
connectSocketServer(const std::string& server_name, int port_number)
{
std::cout << "Trying to establish connection to " << server_name << ':' << port_number << '\n';
auto p_socket_internals = std::make_shared<Socket::Internals>();
Socket::Internals& socket_internals = *p_socket_internals;
socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
if (socket_internals.m_socket_fd < 0) {
throw NormalError(strerror(errno));
}
hostent* server = ::gethostbyname(server_name.c_str());
if (server == NULL) {
throw NormalError(strerror(errno));
}
sockaddr_in& serv_addr = socket_internals.m_address;
::memset(reinterpret_cast<char*>(&serv_addr), 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
::memcpy(reinterpret_cast<char*>(&serv_addr.sin_addr.s_addr), reinterpret_cast<char*>(server->h_addr),
server->h_length);
serv_addr.sin_port = ::htons(port_number);
if (::connect(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&serv_addr), sizeof(serv_addr))) {
throw NormalError(strerror(errno));
}
Socket server_socket{p_socket_internals};
std::cout << "Connected to " << server_socket << '\n';
return server_socket;
}
void
Socket::_write(const char* data, const size_t lenght) const
{
if (::write(this->m_internals->fileDescriptor(), data, lenght) < 0) {
throw NormalError(strerror(errno));
}
}
void
Socket::_read(char* data, const size_t length) const
{
size_t received = 0;
do {
int n = ::read(this->m_internals->fileDescriptor(), reinterpret_cast<char*>(data) + received, length - received);
if (n <= 0) {
throw NormalError(strerror(errno));
}
received += n;
} while (received < length);
}
#ifndef SOCKET_HPP
#define SOCKET_HPP
#include <memory>
#include <string>
#include <type_traits>
class Socket
{
private:
class Internals;
std::shared_ptr<Internals> m_internals;
Socket(std::shared_ptr<Internals> internals) : m_internals{internals} {}
void _write(const char* data, const size_t lenght) const;
void _read(char* data, const size_t lenght) const;
public:
friend std::ostream& operator<<(std::ostream& os, const Socket& s);
friend Socket createSocketServer(int port_number);
friend Socket acceptSocketClient(const Socket& server);
friend Socket connectSocketServer(const std::string& server_name, int port_number);
template <typename T>
friend void write(const Socket& socket, const T& value);
template <typename T>
friend void read(const Socket& socket, T& value);
template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
friend void write(const Socket& socket, const ArrayT<T, R...>& array);
template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
friend void read(const Socket& socket, ArrayT<T, R...>& array);
Socket(Socket&&) = default;
Socket(const Socket&) = default;
~Socket() = default;
};
Socket createSocketServer(int port_number);
Socket acceptSocketClient(const Socket& server);
Socket connectSocketServer(const std::string& server_name, int port_number);
template <typename T>
inline void
write(const Socket& socket, const T& value)
{
socket._write(reinterpret_cast<const char*>(&value), sizeof(T) / sizeof(char));
}
template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
void
write(const Socket& socket, const ArrayT<T, R...>& array)
{
socket._write(reinterpret_cast<const char*>(&array[0]), array.size() * sizeof(T) / sizeof(char));
}
template <typename T>
inline void
read(const Socket& socket, T& value)
{
static_assert(not std::is_const_v<T>, "cannot read values into const data");
socket._read(reinterpret_cast<char*>(&value), sizeof(T) / sizeof(char));
}
template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
void
read(const Socket& socket, ArrayT<T, R...>& array)
{
static_assert(not std::is_const_v<T>, "cannot read values into const data");
socket._read(reinterpret_cast<char*>(&array[0]), array.size() * sizeof(T) / sizeof(char));
}
#endif // SOCKET_HPP
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment