blob: 34717551dee849ec81a4a1c45fc13f13b83df976 [file] [log] [blame]
Mark Sleee8540632006-05-30 09:24:40 +00001#include <sys/socket.h>
2#include <arpa/inet.h>
3#include <netinet/in.h>
4#include <netinet/tcp.h>
5#include <netdb.h>
6#include <unistd.h>
7#include <errno.h>
8
9#include "transport/TSocket.h"
Mark Slee8d7e1f62006-06-07 06:48:56 +000010#include "transport/TTransportException.h"
Mark Sleee8540632006-05-30 09:24:40 +000011
12using namespace std;
13
Mark Slee8d7e1f62006-06-07 06:48:56 +000014uint32_t g_socket_syscalls = 0;
15
16/**
17 * TSocket implementation.
18 *
19 * @author Mark Slee <mcslee@facebook.com>
20 */
21
Mark Sleee8540632006-05-30 09:24:40 +000022// Mutex to protect syscalls to netdb
23pthread_mutex_t g_netdb_mutex = PTHREAD_MUTEX_INITIALIZER;
24
25// TODO(mcslee): Make this an option to the socket class
26#define MAX_RECV_RETRIES 20
27
28TSocket::TSocket(string host, int port) :
29 host_(host), port_(port), socket_(0) {}
30
31TSocket::TSocket(int socket) {
32 socket_ = socket;
33}
34
35TSocket::~TSocket() {
36 close();
37}
38
Mark Slee8d7e1f62006-06-07 06:48:56 +000039bool TSocket::isOpen() {
40 return (socket_ > 0);
41}
42
43void TSocket::open() {
Mark Sleee8540632006-05-30 09:24:40 +000044 // Create socket
45 socket_ = socket(AF_INET, SOCK_STREAM, 0);
46 if (socket_ == -1) {
Mark Slee8d7e1f62006-06-07 06:48:56 +000047 perror("TSocket::open() socket");
48 close();
49 throw TTransportException(TTX_NOT_OPEN, "socket() ERROR:" + errno);
Mark Sleee8540632006-05-30 09:24:40 +000050 }
51
Mark Slee8d7e1f62006-06-07 06:48:56 +000052 // Lookup the hostname
Mark Sleee8540632006-05-30 09:24:40 +000053 struct sockaddr_in addr;
54 addr.sin_family = AF_INET;
55 addr.sin_port = htons(port_);
56
57 /*
58 if (inet_pton(AF_INET, host_.c_str(), &addr.sin_addr) < 0) {
59 perror("TSocket::open() inet_pton");
60 }
61 */
62
63 {
64 // TODO(mcslee): Fix scope-locking here to protect hostname lookups
65 // scopelock sl(&netdb_mutex);
66 struct hostent *host_entry = gethostbyname(host_.c_str());
67
68 if (host_entry == NULL) {
69 // perror("dns error: failed call to gethostbyname.\n");
70 close();
Mark Slee8d7e1f62006-06-07 06:48:56 +000071 throw TTransportException(TTX_NOT_OPEN, "gethostbyname() failed");
Mark Sleee8540632006-05-30 09:24:40 +000072 }
73
74 addr.sin_port = htons(port_);
75 memcpy(&addr.sin_addr.s_addr,
76 host_entry->h_addr_list[0],
77 host_entry->h_length);
78 }
79
80 // Connect the socket
81 int ret = connect(socket_, (struct sockaddr *)&addr, sizeof(addr));
82
83 // Connect failed
84 if (ret < 0) {
85 perror("TSocket::open() connect");
86 close();
Mark Slee8d7e1f62006-06-07 06:48:56 +000087 throw TTransportException(TTX_NOT_OPEN, "open() ERROR: " + errno);
Mark Sleee8540632006-05-30 09:24:40 +000088 }
89
Mark Slee8d7e1f62006-06-07 06:48:56 +000090 // Connection was successful
Mark Sleee8540632006-05-30 09:24:40 +000091}
92
93void TSocket::close() {
94 if (socket_ > 0) {
95 shutdown(socket_, SHUT_RDWR);
96 ::close(socket_);
97 }
98 socket_ = 0;
99}
100
Mark Slee8d7e1f62006-06-07 06:48:56 +0000101uint32_t TSocket::read(uint8_t* buf, uint32_t len) {
102 if (socket_ <= 0) {
103 throw TTransportException(TTX_NOT_OPEN, "Called read on non-open socket");
104 }
Mark Sleee8540632006-05-30 09:24:40 +0000105
Mark Sleee8540632006-05-30 09:24:40 +0000106 uint32_t retries = 0;
Mark Slee8d7e1f62006-06-07 06:48:56 +0000107
108 try_again:
109 // Read from the socket
110 int got = recv(socket_, buf, len, 0);
111 ++g_socket_syscalls;
112
113 // Check for error on read
114 if (got < 0) {
115 perror("TSocket::read()");
116
117 // If temporarily out of resources, sleep a bit and try again
118 if (errno == EAGAIN && retries++ < MAX_RECV_RETRIES) {
119 usleep(50);
120 goto try_again;
Mark Sleee8540632006-05-30 09:24:40 +0000121 }
122
Mark Slee8d7e1f62006-06-07 06:48:56 +0000123 // If interrupted, try again
124 if (errno == EINTR && retries++ < MAX_RECV_RETRIES) {
125 goto try_again;
Mark Sleee8540632006-05-30 09:24:40 +0000126 }
127
Mark Slee8d7e1f62006-06-07 06:48:56 +0000128 // If we disconnect with no linger time
129 if (errno == ECONNRESET) {
130 throw TTransportException(TTX_NOT_OPEN, "ECONNRESET");
131 }
132
133 // This ish isn't open
134 if (errno == ENOTCONN) {
135 throw TTransportException(TTX_NOT_OPEN, "ENOTCONN");
136 }
137
138 // Timed out!
139 if (errno == ETIMEDOUT) {
140 throw TTransportException(TTX_TIMED_OUT, "ETIMEDOUT");
141 }
142
143 // Some other error, whatevz
144 throw TTransportException(TTX_UNKNOWN, "ERROR:" + errno);
145 }
146
147 // The remote host has closed the socket
148 if (got == 0) {
149 close();
150 return 0;
Mark Sleee8540632006-05-30 09:24:40 +0000151 }
152
153 // Pack data into string
Mark Slee8d7e1f62006-06-07 06:48:56 +0000154 return got;
Mark Sleee8540632006-05-30 09:24:40 +0000155}
156
Mark Slee8d7e1f62006-06-07 06:48:56 +0000157void TSocket::write(const uint8_t* buf, uint32_t len) {
158 if (socket_ <= 0) {
159 throw TTransportException(TTX_NOT_OPEN, "Called write on non-open socket");
160 }
161
Mark Sleee8540632006-05-30 09:24:40 +0000162 uint32_t sent = 0;
163
Mark Slee8d7e1f62006-06-07 06:48:56 +0000164 while (sent < len) {
165 // Note the use of MSG_NOSIGNAL to suppress SIGPIPE errors, instead we
166 // check for the EPIPE return condition and close the socket in that case
167 int b = send(socket_, buf + sent, len - sent, MSG_NOSIGNAL);
168 ++g_socket_syscalls;
169
Mark Sleee8540632006-05-30 09:24:40 +0000170 // Fail on a send error
171 if (b < 0) {
Mark Slee8d7e1f62006-06-07 06:48:56 +0000172 if (errno == EPIPE) {
173 close();
174 throw TTransportException(TTX_NOT_OPEN, "EPIPE");
175 }
176
177 if (errno == ECONNRESET) {
178 close();
179 throw TTransportException(TTX_NOT_OPEN, "ECONNRESET");
180 }
181
182 if (errno == ENOTCONN) {
183 close();
184 throw TTransportException(TTX_NOT_OPEN, "ENOTCONN");
185 }
186
187 perror("TSocket::write() send < 0");
188 throw TTransportException(TTX_UNKNOWN, "ERROR:" + errno);
Mark Sleee8540632006-05-30 09:24:40 +0000189 }
190
191 // Fail on blocked send
192 if (b == 0) {
Mark Slee8d7e1f62006-06-07 06:48:56 +0000193 throw TTransportException(TTX_NOT_OPEN, "Socket send returned 0.");
Mark Sleee8540632006-05-30 09:24:40 +0000194 }
Mark Sleee8540632006-05-30 09:24:40 +0000195 sent += b;
196 }
197}
198
Mark Slee8d7e1f62006-06-07 06:48:56 +0000199void TSocket::setLinger(bool on, int linger) {
200 // TODO(mcslee): Store these options so they can be set pre-connect
201 if (socket_ <= 0) {
202 return;
203 }
204
Mark Sleee8540632006-05-30 09:24:40 +0000205 struct linger ling = {(on ? 1 : 0), linger};
206 if (-1 == setsockopt(socket_, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling))) {
207 close();
208 perror("TSocket::setLinger()");
Mark Sleee8540632006-05-30 09:24:40 +0000209 }
Mark Sleee8540632006-05-30 09:24:40 +0000210}
211
Mark Slee8d7e1f62006-06-07 06:48:56 +0000212void TSocket::setNoDelay(bool noDelay) {
213 // TODO(mcslee): Store these options so they can be set pre-connect
214 if (socket_ <= 0) {
215 return;
216 }
217
Mark Sleee8540632006-05-30 09:24:40 +0000218 // Set socket to NODELAY
219 int val = (noDelay ? 1 : 0);
220 if (-1 == setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) {
221 close();
222 perror("TSocket::setNoDelay()");
Mark Sleee8540632006-05-30 09:24:40 +0000223 }
Mark Sleee8540632006-05-30 09:24:40 +0000224}