Bind a socket but don't listen to it yet

This commit is contained in:
Perttu Ahola 2014-09-17 04:04:50 +03:00
parent 72766206cb
commit 0287e82006
10 changed files with 387 additions and 2 deletions

View File

@ -68,6 +68,7 @@ set(SERVER_SRCS
src/server/rccpp.cpp src/server/rccpp.cpp
src/impl/fs.cpp src/impl/fs.cpp
src/impl/event.cpp src/impl/event.cpp
src/impl/tcpsocket.cpp
) )
add_executable(${SERVER_EXE_NAME} ${SERVER_SRCS}) add_executable(${SERVER_EXE_NAME} ${SERVER_SRCS})
TARGET_LINK_LIBRARIES(${SERVER_EXE_NAME} TARGET_LINK_LIBRARIES(${SERVER_EXE_NAME}

View File

@ -49,3 +49,6 @@ name->type registry is used for determining numeric packet types.
Data is freeform. Types 0...100 are reserved for initialization. Data is freeform. Types 0...100 are reserved for initialization.
Core uses cereal's portable binary serialization.

View File

@ -0,0 +1,48 @@
#include "interface/module.h"
#include "interface/server.h"
#include "interface/event.h"
#include "interface/tcpsocket.h"
//#include "network/include/api.h"
#include <iostream>
using interface::Event;
namespace network {
struct Module: public interface::Module
{
interface::Server *m_server;
sp_<interface::TCPSocket> m_socket;
Module(interface::Server *server):
m_server(server),
m_socket(interface::createTCPSocket())
{
std::cout<<"network construct"<<std::endl;
ss_ address = "any";
ss_ port = "20000";
if(!m_socket->bind_fd(address, port)){
std::cerr<<"Failed to bind to "<<address<<":"<<port<<std::endl;
} else {
std::cerr<<"Listening at "<<address<<":"<<port<<std::endl;
}
}
~Module()
{
std::cout<<"network destruct"<<std::endl;
}
void event(const interface::Event &event)
{
}
};
extern "C" {
EXPORT void* createModule_network(interface::Server *server){
return (void*)(new Module(server));
}
}
}

295
src/impl/tcpsocket.cpp Normal file
View File

@ -0,0 +1,295 @@
#include "interface/tcpsocket.h"
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h> // strerror()
#include <netinet/in.h>
#include <iomanip>
#include <netdb.h>
namespace interface {
const unsigned char prefix[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF};
bool sockaddr_to_bytes(const sockaddr_storage* ptr, sv_<uchar>& to)
{
if(ptr->ss_family == AF_INET)
{
uchar* u = (uchar*)&((struct sockaddr_in*)ptr)->sin_addr.s_addr;
to.assign(u, u + 4);
return true;
}
else if(ptr->ss_family == AF_INET6)
{
uchar* u = (uchar*)&((struct sockaddr_in6*)ptr)->sin6_addr.s6_addr;
if(memcmp(prefix, u, sizeof(prefix)) == 0){
to.assign(u + 12, u + 16);
return true;
}
to.assign(u, u + 16);
return true;
}
return false;
};
std::string address_bytes_to_string(const sv_<uchar> &ip)
{
std::ostringstream os;
for(size_t i=0; i<ip.size(); i++){
if(ip.size() == 4){
os<<std::dec<<std::setfill('0')<<std::setw(0)
<<((uint32_t)ip[i]&0xff);
if(i < ip.size()-1)
os<<".";
} else {
os<<std::hex<<std::setfill('0')<<std::setw(2)
<<((uint32_t)ip[i]&0xff);
i++;
if(i < ip.size())
os<<std::hex<<std::setfill('0')<<std::setw(2)
<<((uint32_t)ip[i]&0xff);
if(i < ip.size()-1)
os<<":";
}
}
return os.str();
}
struct CTCPSocket: public TCPSocket
{
int m_fd = 0;
int fd() const
{
return m_fd;
}
bool good() const
{
return (m_fd != -1);
}
void close_fd()
{
if(m_fd != -1)
close(m_fd);
m_fd = -1;
}
bool listen_fd()
{
if(m_fd == -1)
return false;
if(listen(m_fd, 5) == -1){
std::cerr<<"TCPSocket::listen_fd(): "<<strerror(errno)<<std::endl;
return false;
}
return true;
}
bool connect_fd(const ss_ &address, const ss_ &port)
{
close_fd();
struct addrinfo hints;
struct addrinfo *res0 = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if(address == "any")
hints.ai_flags = AI_PASSIVE; // Wildcard address
const char *address_c = (address == "any" ? NULL : address.c_str());
const char *port_c = (port == "any" ? NULL : port.c_str());
int err = getaddrinfo(address_c, port_c, &hints, &res0);
if(err){
std::cerr<<"getaddrinfo: "<<gai_strerror(err)<<std::endl;
return false;
}
if(res0 == NULL){
std::cerr<<"getaddrinfo: No results"<<std::endl;
return false;
}
// Try to use one of the results
int fd = -1;
int i=0;
for(struct addrinfo *res = res0; res != NULL; res = res->ai_next, i++)
{
std::cerr<<"Trying addrinfo #"<<i<<std::endl;
int try_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if(try_fd == -1){
std::cerr<<"socket: "<<strerror(errno)<<std::endl;
continue;
}
if(connect(try_fd, res->ai_addr, res->ai_addrlen) == -1){
std::cerr<<"connect: "<<strerror(errno)<<std::endl;
close(try_fd);
continue;
}
fd = try_fd;
break;
}
freeaddrinfo(res0);
if(fd == -1){
std::cerr<<"Failed to create and connect socket"<<std::endl;
return false;
}
int val = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
// Set this so that forked child processes don't prevent re-opening the
// same port after crash
if(fcntl(fd, F_SETFD, FD_CLOEXEC) != 0){
std::cerr<<"Failed to set socket FD_CLOEXEC"<<std::endl;
return false;
}
m_fd = fd;
return true;
}
bool bind_fd(const ss_ &address, const ss_ &port)
{
close_fd();
struct addrinfo hints;
struct addrinfo *res0 = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
std::string address1 = address;
if(address1 == "any4"){
address1 = "any";
hints.ai_family = AF_INET;
}
if(address1 == "any6"){
address1 = "any";
hints.ai_family = AF_INET6;
}
if(address1 == "any"){
hints.ai_flags = AI_PASSIVE; // Wildcard address
}
const char *address_c = (address1 == "any" ? NULL : address1.c_str());
const char *port_c = (port == "any" ? NULL : port.c_str());
int err = getaddrinfo(address_c, port_c, &hints, &res0);
if(err){
std::cerr<<"getaddrinfo: "<<gai_strerror(err)<<std::endl;
return false;
}
if(res0 == NULL){
std::cerr<<"getaddrinfo: No results"<<std::endl;
return false;
}
// Try to use one of the results
int fd = -1;
int i=0;
for(struct addrinfo *res = res0; res != NULL; res = res->ai_next, i++)
{
//std::cerr<<"Trying addrinfo #"<<i<<std::endl;
int try_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if(try_fd == -1){
//std::cerr<<"socket: "<<strerror(errno)<<std::endl;
continue;
}
int val = 1;
setsockopt(try_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
if(res->ai_family == AF_INET6){
int val = 1;
setsockopt(try_fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
}
if(bind(try_fd, res->ai_addr, res->ai_addrlen) == -1){
//std::cerr<<"bind: "<<strerror(errno)<<std::endl;
close(try_fd);
continue;
}
fd = try_fd;
break;
}
freeaddrinfo(res0);
if(fd == -1){
std::cerr<<"Failed to create and bind socket"<<std::endl;
return false;
}
// Set this so that forked child processes don't prevent re-opening the
// same port after crash
if(fcntl(fd, F_SETFD, FD_CLOEXEC) != 0){
std::cerr<<"Failed to set socket FD_CLOEXEC"<<std::endl;
return false;
}
m_fd = fd;
return true;
}
bool accept_fd(const TCPSocket &listener)
{
close_fd();
if(!listener.good())
return false;
struct sockaddr_storage pin;
socklen_t pin_len = sizeof(pin);
int fd_client = accept(listener.fd(), (struct sockaddr *) &pin, &pin_len);
if(fd_client == -1){
std::cerr<<"accept: "<<strerror(errno)<<std::endl;
return false;
}
int val = 1;
setsockopt(fd_client, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
m_fd = fd_client;
return true;
}
bool send_fd(const ss_ &data)
{
if(m_fd == -1)
return false;
if(send(m_fd, &data[0], data.size(), 0) == -1){
std::cerr<<"send: "<<strerror(errno)<<std::endl;
return false;
}
return true;
}
ss_ get_local_address() const
{
if(m_fd == -1)
return "";
struct sockaddr_storage sa;
socklen_t sa_len = sizeof(sa);
if(getpeername(m_fd, (sockaddr*)&sa, &sa_len) == -1)
return "";
sv_<uchar> a;
if(!sockaddr_to_bytes(&sa, a))
return "";
return address_bytes_to_string(a);
}
ss_ get_remote_address() const
{
if(m_fd == -1)
return "";
struct sockaddr_storage sa;
socklen_t sa_len = sizeof(sa);
if(getsockname(m_fd, (sockaddr*)&sa, &sa_len) == -1)
return "";
sv_<uchar> a;
if(!sockaddr_to_bytes(&sa, a))
return "";
return address_bytes_to_string(a);
}
};
TCPSocket* createTCPSocket()
{
return new CTCPSocket();
}
}

View File

@ -10,6 +10,7 @@ namespace interface
virtual ~Server(){} virtual ~Server(){}
virtual void load_module(const ss_ &module_name, const ss_ &path) = 0; virtual void load_module(const ss_ &module_name, const ss_ &path) = 0;
virtual ss_ get_modules_path() = 0; virtual ss_ get_modules_path() = 0;
virtual ss_ get_builtin_modules_path() = 0;
virtual Module* get_module(const ss_ &module_name) = 0; virtual Module* get_module(const ss_ &module_name) = 0;
}; };
} }

24
src/interface/tcpsocket.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "core/types.h"
namespace interface
{
struct TCPSocket
{
virtual ~TCPSocket(){}
virtual int fd() const = 0;
virtual bool good() const = 0;
virtual void close_fd() = 0;
virtual bool listen_fd() = 0;
virtual bool connect_fd(const ss_ &address, const ss_ &port) = 0;
// Special values "any4", "any6" and "any"
virtual bool bind_fd(const ss_ &address, const ss_ &port) = 0;
virtual bool accept_fd(const TCPSocket &listener) = 0;
virtual bool send_fd(const ss_ &data) = 0;
virtual ss_ get_local_address() const = 0;
virtual ss_ get_remote_address() const = 0;
};
TCPSocket* createTCPSocket();
}

View File

@ -7,6 +7,7 @@ namespace server
{ {
ss_ rccpp_build_path = "../cache/rccpp_build"; ss_ rccpp_build_path = "../cache/rccpp_build";
ss_ interface_path = "../src/interface"; ss_ interface_path = "../src/interface";
ss_ share_path = "../share";
}; };
} }

View File

@ -12,13 +12,14 @@ int main(int argc, char *argv[])
std::string module_path; std::string module_path;
const char opts[100] = "hm:r:i:"; const char opts[100] = "hm:r:i:S:";
const char usagefmt[1000] = const char usagefmt[1000] =
"Usage: %s [OPTION]...\n" "Usage: %s [OPTION]...\n"
" -h Show this help\n" " -h Show this help\n"
" -m [module_path] Specify module path\n" " -m [module_path] Specify module path\n"
" -r [rccpp_build_path]Specify runtime compiled C++ build path\n" " -r [rccpp_build_path]Specify runtime compiled C++ build path\n"
" -i [interface_path] Specify path to interface headers\n" " -i [interface_path] Specify path to interface headers\n"
" -S [share_path] Specify path to share/\n"
; ;
int c; int c;
@ -41,6 +42,10 @@ int main(int argc, char *argv[])
fprintf(stderr, "INFO: config.interface_path: %s\n", c55_optarg); fprintf(stderr, "INFO: config.interface_path: %s\n", c55_optarg);
config.interface_path = c55_optarg; config.interface_path = c55_optarg;
break; break;
case 'S':
fprintf(stderr, "INFO: config.share_path: %s\n", c55_optarg);
config.share_path = c55_optarg;
break;
default: default:
fprintf(stderr, "ERROR: Invalid command-line argument\n"); fprintf(stderr, "ERROR: Invalid command-line argument\n");
fprintf(stderr, usagefmt, argv[0]); fprintf(stderr, usagefmt, argv[0]);

View File

@ -57,6 +57,11 @@ struct CState: public State, public interface::Server
return m_modules_path; return m_modules_path;
} }
ss_ get_builtin_modules_path()
{
return g_server_config.share_path+"/builtin";
}
interface::Module* get_module(const ss_ &module_name) interface::Module* get_module(const ss_ &module_name)
{ {
auto it = m_modules.find(module_name); auto it = m_modules.find(module_name);

View File

@ -2,7 +2,6 @@
#include "interface/server.h" #include "interface/server.h"
#include "interface/fs.h" #include "interface/fs.h"
#include "interface/event.h" #include "interface/event.h"
//#include <cereal/archives/binary.hpp>
#include <iostream> #include <iostream>
using interface::Event; using interface::Event;
@ -35,10 +34,13 @@ struct Module: public interface::Module
void load_modules() void load_modules()
{ {
m_server->load_module("network", m_server->get_builtin_modules_path()+"/network");
sv_<ss_> load_list = {"test1", "test2"}; sv_<ss_> load_list = {"test1", "test2"};
for(const ss_ &name : load_list){ for(const ss_ &name : load_list){
m_server->load_module(name, m_server->get_modules_path()+"/"+name); m_server->load_module(name, m_server->get_modules_path()+"/"+name);
} }
/*// TODO: Dependencies /*// TODO: Dependencies
auto list = interface::getGlobalFilesystem()->list_directory(m_server->get_modules_path()); auto list = interface::getGlobalFilesystem()->list_directory(m_server->get_modules_path());
for(const interface::Filesystem::Node &n : list){ for(const interface::Filesystem::Node &n : list){