| 1 |
#include <cstring> |
| 2 |
|
| 3 |
#include <boost/lexical_cast.hpp> |
| 4 |
|
| 5 |
#include <fcntl.h> |
| 6 |
#include <netdb.h> |
| 7 |
#include <netinet/in.h> |
| 8 |
#include <sys/socket.h> |
| 9 |
#include <unistd.h> |
| 10 |
|
| 11 |
#include "adt/logs.h" |
| 12 |
|
| 13 |
#include "deliantra/communicator.h" |
| 14 |
#include "deliantra/exception.h" |
| 15 |
|
| 16 |
static std::string |
| 17 |
strerror () |
| 18 |
{ |
| 19 |
return strerror (errno); |
| 20 |
} |
| 21 |
|
| 22 |
communicator::communicator (std::string const &server, std::string const &port) |
| 23 |
: response_len (0) |
| 24 |
, response { 0 } |
| 25 |
{ |
| 26 |
LOG ("net") << "creating socket\n"; |
| 27 |
int sockfd = socket (AF_INET, SOCK_STREAM, 0); |
| 28 |
if (sockfd == -1) |
| 29 |
throw "socket: " + strerror (); |
| 30 |
|
| 31 |
int portno = boost::lexical_cast<int> (port); |
| 32 |
|
| 33 |
LOG ("net") << "resolving hostname: " << server << "\n"; |
| 34 |
hostent *serverent = gethostbyname (server.c_str ()); |
| 35 |
if (!serverent) |
| 36 |
throw "gethostbyname: " + strerror (); |
| 37 |
|
| 38 |
LOG ("net") << "connecting to: " << server << ":" << portno << "\n"; |
| 39 |
sockaddr_in addr = { 0 }; |
| 40 |
addr.sin_family = AF_INET; |
| 41 |
memcpy (&addr.sin_addr.s_addr, serverent->h_addr, serverent->h_length); |
| 42 |
addr.sin_port = htons (portno); |
| 43 |
if (connect (sockfd, reinterpret_cast<sockaddr const *> (&addr), sizeof addr) == -1) |
| 44 |
throw "connect: " + strerror (); |
| 45 |
|
| 46 |
LOG ("net") << "setting socket to non-blocking\n"; |
| 47 |
int flags = fcntl (sockfd, F_GETFL, 0); |
| 48 |
if (flags == -1) |
| 49 |
flags = 0; |
| 50 |
if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) |
| 51 |
throw "fcntl: " + strerror (); |
| 52 |
|
| 53 |
LOG ("net") << "setting up read callback\n"; |
| 54 |
sock.set (sockfd, ev::READ); |
| 55 |
sock.set<communicator, &communicator::read_cb> (this); |
| 56 |
sock.start (); |
| 57 |
} |
| 58 |
|
| 59 |
void |
| 60 |
communicator::set_packet_handler (interface::packet_handler &handler) |
| 61 |
{ |
| 62 |
on = &handler; |
| 63 |
} |
| 64 |
|
| 65 |
void |
| 66 |
communicator::read_cb (ev::io &w, int revents) |
| 67 |
{ |
| 68 |
//LOG ("net") << "data is ready\n"; |
| 69 |
|
| 70 |
ssize_t readlen = read (w.fd, response + response_len, sizeof response - response_len); |
| 71 |
if (readlen < 0) |
| 72 |
throw "read: " + strerror (); |
| 73 |
if (readlen == 0) |
| 74 |
throw exit_success ("EOF from server"); |
| 75 |
response_len += readlen; |
| 76 |
|
| 77 |
for (;;) |
| 78 |
{ |
| 79 |
if (response_len < 2) |
| 80 |
break; |
| 81 |
|
| 82 |
unsigned int cmd_len = response[0] << 8 | response[1]; |
| 83 |
|
| 84 |
//LOG ("net") << "command length is ready: " << cmd_len << "\n"; |
| 85 |
if (response_len < cmd_len + 2) |
| 86 |
break; |
| 87 |
|
| 88 |
//LOG ("net") << "command is ready; processing\n"; |
| 89 |
on->receive (std::string (response + 2, response + 2 + cmd_len)); |
| 90 |
memmove (response, response + 2 + cmd_len, response_len - 2 - cmd_len); |
| 91 |
response_len -= 2 + cmd_len; |
| 92 |
//LOG ("net") << response_len << " bytes left\n"; |
| 93 |
} |
| 94 |
} |
| 95 |
|
| 96 |
void |
| 97 |
communicator::send (std::string const &command) |
| 98 |
{ |
| 99 |
LOG ("net") << "sending command: " << command << "\n"; |
| 100 |
write (sock.fd, &static_cast<uint16_t const &> (htons (command.length ())), 2); |
| 101 |
if (write (sock.fd, command.data (), command.length ()) == -1) |
| 102 |
throw "write: " + strerror (); |
| 103 |
} |