1 | /************************************************************************* |
2 | * libjson-rpc-cpp |
3 | ************************************************************************* |
4 | * @file linuxtcpsocketserver.cpp |
5 | * @date 17.07.2015 |
6 | * @author Alexandre Poirot <alexandre.poirot@legrand.fr> |
7 | * @license See attached LICENSE.txt |
8 | ************************************************************************/ |
9 | |
10 | #include "linuxtcpsocketserver.h" |
11 | #include "../../common/sharedconstants.h" |
12 | #include "../../common/streamreader.h" |
13 | #include "../../common/streamwriter.h" |
14 | #include <cstdio> |
15 | #include <cstdlib> |
16 | #include <cstring> |
17 | #include <fcntl.h> |
18 | #include <sys/types.h> |
19 | #include <unistd.h> |
20 | |
21 | #include <errno.h> |
22 | #include <iostream> |
23 | #include <sstream> |
24 | #include <string> |
25 | |
26 | using namespace jsonrpc; |
27 | using namespace std; |
28 | |
29 | LinuxTcpSocketServer::LinuxTcpSocketServer(const std::string &ipToBind, const unsigned int &port, size_t threads) |
30 | : AbstractThreadedServer(threads), ipToBind(ipToBind), port(port) {} |
31 | |
32 | LinuxTcpSocketServer::~LinuxTcpSocketServer() { |
33 | shutdown(this->socket_fd, 2); |
34 | close(this->socket_fd); |
35 | } |
36 | |
37 | bool LinuxTcpSocketServer::InitializeListener() { |
38 | this->socket_fd = socket(AF_INET, SOCK_STREAM, 0); |
39 | if (this->socket_fd < 0) { |
40 | return false; |
41 | } |
42 | |
43 | fcntl(this->socket_fd, F_SETFL, FNDELAY); |
44 | int reuseaddr = 1; |
45 | setsockopt(this->socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)); |
46 | |
47 | /* start with a clean address structure */ |
48 | memset(&(this->address), 0, sizeof(struct sockaddr_in)); |
49 | |
50 | this->address.sin_family = AF_INET; |
51 | inet_aton(this->ipToBind.c_str(), &(this->address.sin_addr)); |
52 | this->address.sin_port = htons(this->port); |
53 | |
54 | if (::bind(this->socket_fd, reinterpret_cast<struct sockaddr *>(&(this->address)), sizeof(struct sockaddr_in)) != 0) { |
55 | return false; |
56 | } |
57 | |
58 | if (listen(this->socket_fd, 5) != 0) { |
59 | return false; |
60 | } |
61 | return true; |
62 | } |
63 | |
64 | int LinuxTcpSocketServer::CheckForConnection() { |
65 | struct sockaddr_in connection_address; |
66 | memset(&connection_address, 0, sizeof(struct sockaddr_in)); |
67 | socklen_t address_length = sizeof(connection_address); |
68 | return accept(this->socket_fd, reinterpret_cast<struct sockaddr *>(&(connection_address)), &address_length); |
69 | } |
70 | |
71 | void LinuxTcpSocketServer::HandleConnection(int connection) { |
72 | StreamReader reader(DEFAULT_BUFFER_SIZE); |
73 | string request, response; |
74 | |
75 | reader.Read(request, connection, DEFAULT_DELIMITER_CHAR); |
76 | |
77 | this->ProcessRequest(request, response); |
78 | |
79 | response.append(1, DEFAULT_DELIMITER_CHAR); |
80 | StreamWriter writer; |
81 | writer.Write(response, connection); |
82 | CleanClose(connection); |
83 | } |
84 | |
85 | bool LinuxTcpSocketServer::WaitClientClose(const int &fd, const int &timeout) { |
86 | bool ret = false; |
87 | int i = 0; |
88 | while ((recv(fd, NULL, 0, 0) != 0) && i < timeout) { |
89 | usleep(1); |
90 | ++i; |
91 | ret = true; |
92 | } |
93 | |
94 | return ret; |
95 | } |
96 | |
97 | int LinuxTcpSocketServer::CloseByReset(const int &fd) { |
98 | struct linger so_linger; |
99 | so_linger.l_onoff = 1; |
100 | so_linger.l_linger = 0; |
101 | |
102 | int ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)); |
103 | if (ret != 0) |
104 | return ret; |
105 | |
106 | return close(fd); |
107 | } |
108 | |
109 | int LinuxTcpSocketServer::CleanClose(const int &fd) { |
110 | if (WaitClientClose(fd)) { |
111 | return close(fd); |
112 | } else { |
113 | return CloseByReset(fd); |
114 | } |
115 | } |
116 | |