blob: 1dfe431f5bdb71f075ccdb78842476a3868896ad [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"
10
11using namespace std;
12
13// Mutex to protect syscalls to netdb
14pthread_mutex_t g_netdb_mutex = PTHREAD_MUTEX_INITIALIZER;
15
16// TODO(mcslee): Make this an option to the socket class
17#define MAX_RECV_RETRIES 20
18
19TSocket::TSocket(string host, int port) :
20 host_(host), port_(port), socket_(0) {}
21
22TSocket::TSocket(int socket) {
23 socket_ = socket;
24}
25
26TSocket::~TSocket() {
27 close();
28}
29
30bool TSocket::open() {
31 // Create socket
32 socket_ = socket(AF_INET, SOCK_STREAM, 0);
33 if (socket_ == -1) {
34 socket_ = 0;
35 return false;
36 }
37
38 // Lookup the host
39 struct sockaddr_in addr;
40 addr.sin_family = AF_INET;
41 addr.sin_port = htons(port_);
42
43 /*
44 if (inet_pton(AF_INET, host_.c_str(), &addr.sin_addr) < 0) {
45 perror("TSocket::open() inet_pton");
46 }
47 */
48
49 {
50 // TODO(mcslee): Fix scope-locking here to protect hostname lookups
51 // scopelock sl(&netdb_mutex);
52 struct hostent *host_entry = gethostbyname(host_.c_str());
53
54 if (host_entry == NULL) {
55 // perror("dns error: failed call to gethostbyname.\n");
56 close();
57 return false;
58 }
59
60 addr.sin_port = htons(port_);
61 memcpy(&addr.sin_addr.s_addr,
62 host_entry->h_addr_list[0],
63 host_entry->h_length);
64 }
65
66 // Connect the socket
67 int ret = connect(socket_, (struct sockaddr *)&addr, sizeof(addr));
68
69 // Connect failed
70 if (ret < 0) {
71 perror("TSocket::open() connect");
72 close();
73 return false;
74 }
75
76 return true;
77}
78
79void TSocket::close() {
80 if (socket_ > 0) {
81 shutdown(socket_, SHUT_RDWR);
82 ::close(socket_);
83 }
84 socket_ = 0;
85}
86
87int TSocket::read(string& s, uint32_t len) {
88 char buff[len];
89 s = "";
90
91 uint32_t have = 0;
92 uint32_t retries = 0;
93
94 while (have < len) {
95 try_again:
96 // Read from the socket
97 int got = recv(socket_, buff+have, len-have, 0);
98
99 // Check for error on read
100 if (got < 0) {
101 perror("TSocket::read()");
102
103 // If temporarily out of resources, sleep a bit and try again
104 if (errno == EAGAIN && retries++ < MAX_RECV_RETRIES) {
105 usleep(50);
106 goto try_again;
107 }
108
109 // If interrupted, try again
110 if (errno == EINTR && retries++ < MAX_RECV_RETRIES) {
111 goto try_again;
112 }
113
114 // If we disconnect with no linger time
115 if (errno == ECONNRESET) {
116 return 0;
117 }
118
119 return 0;
120 }
121
122 // Check for empty read
123 if (got == 0) {
124 return 0;
125 }
126
127 // Update the count
128 have += (uint32_t) got;
129 }
130
131 // Pack data into string
132 s = string(buff, have);
133 return have;
134}
135
136void TSocket::write(const string& s) {
137 uint32_t sent = 0;
138
139 while (sent < s.size()) {
140 int b = send(socket_, s.data() + sent, s.size() - sent, 0);
141
142 // Fail on a send error
143 if (b < 0) {
144 // TODO(mcslee): Make the function return how many bytes it wrote or
145 // throw an exception
146 // throw_perror("send");
147 return;
148 }
149
150 // Fail on blocked send
151 if (b == 0) {
152 // TODO(mcslee): Make the function return how many bytes it wrote or
153 // throw string("couldn't send data.\n");
154 return;
155 }
156
157 sent += b;
158 }
159}
160
161bool TSocket::setLinger(bool on, int linger) {
162 struct linger ling = {(on ? 1 : 0), linger};
163 if (-1 == setsockopt(socket_, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling))) {
164 close();
165 perror("TSocket::setLinger()");
166 return false;
167 }
168 return true;
169}
170
171bool TSocket::setNoDelay(bool noDelay) {
172 // Set socket to NODELAY
173 int val = (noDelay ? 1 : 0);
174 if (-1 == setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) {
175 close();
176 perror("TSocket::setNoDelay()");
177 return false;
178 }
179 return true;
180}