blob: 0d6fae5eb88cbf3d3690e4737c3b12f191d8c031 [file] [log] [blame]
jat@google.com134be542009-08-03 15:30:11 +00001/*
2 * Copyright 2008 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17#include "Platform.h"
18
19#include <cstdio>
20#include <cstdlib>
21#include <cstring>
22#include <cerrno>
23
24#include "Socket.h"
25
26void Socket::init() {
27#ifdef _WINDOWS
28 // version 2.2 supported on Win95OSR2/WinNT4 and up
29 WORD winsockVers = MAKEWORD(2, 2);
30 WSADATA wsaData;
31 int err = WSAStartup(winsockVers, &wsaData);
32 if (err) {
33 // TODO(jat): report error
34 Debug::log(Debug::Error) << "WSAStartup(vers=2.2): err=" << err << Debug::flush;
35 }
36#endif
37}
38
39bool Socket::connect(const char* host, int port) {
40 Debug::log(Debug::Debugging) << "Socket::connect(host=" << host << ",port=" << port << ")"
41 << Debug::flush;
42 if (isConnected()) {
43 Debug::log(Debug::Error) << "Socket::connect - already connected" << Debug::flush;
44 return false;
45 }
46
47 SOCKETTYPE fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
48 if (fd < 0) {
49 Debug::log(Debug::Error) << "Socket::connect - can't get socket" << Debug::flush;
50 return false;
51 }
52#ifdef SO_NOSIGPIPE
53 // On BSD, we need to suppress the SIGPIPE if the remote end disconnects.
54 int option_value = 1;
55 if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &option_value, sizeof(int))) {
56 Debug::log(Debug::Error) << "Socket::connect - can't set NOSIGPIPE option" << Debug::flush;
57 return false;
58 }
59#endif
60
61 struct sockaddr_in sockAddr;
62 memset(&sockAddr, 0, static_cast<int>(sizeof(sockAddr)));
63 // check for numeric IP4 addresses first
64 // TODO(jat): handle IPv6 addresses
65 unsigned long numericAddr = inet_addr(host);
66 if (numericAddr != 0xFFFFFFFF) {
67 sockAddr.sin_addr.s_addr = numericAddr;
68 sockAddr.sin_family = AF_INET;
69 } else {
70 struct hostent* hent = gethostbyname(host);
71 if (!hent || !hent->h_addr_list[0]) {
72 Debug::log(Debug::Error) << "Unable to get address for " << host << Debug::flush;
73 return false;
74 }
75 memcpy(&(sockAddr.sin_addr), hent->h_addr_list[0], hent->h_length);
76 sockAddr.sin_family = hent->h_addrtype;
77 }
78 sockAddr.sin_port = htons(port);
79
80 if (::connect(fd, (struct sockaddr*) &sockAddr, sizeof(sockAddr)) < 0) {
81#ifdef _WINDOWS
82 char buf[256];
83 strerror_s(buf, sizeof(buf), errno);
84 Debug::log(Debug::Error) << "Can't connect to " << host << ":" << port << " -- "
85 << buf << Debug::flush;
86 closesocket(fd);
87#else
88 Debug::log(Debug::Error) << "Can't connect to " << host << ":" << port << " -- "
89 << strerror(errno) << Debug::flush;
90 close(fd);
91#endif
92 return false;
93 }
94 sock = fd;
95 connected = true;
96 readBufPtr = readValid = readBuf;
97 writeBufPtr = writeBuf;
98#ifdef _WINDOWS
99 Debug::log(Debug::Spam) << " connected" << Debug::flush;
100#else
101 Debug::log(Debug::Spam) << " connected, fd=" << fd << Debug::flush;
102#endif
103 return true;
104}
105
106bool Socket::disconnect(bool doFlush) {
107 if (connected) {
108 Debug::log(Debug::Debugging) << "Disconnecting socket" << Debug::flush;
109 if (doFlush) {
110 flush();
111 }
112 connected = false;
113#ifdef _WINDOWS
114 closesocket(sock);
115#else
116 shutdown(sock, SHUT_RDWR);
117 close(sock);
118#endif
119 }
120 return true;
121}
122
123bool Socket::emptyWriteBuf() {
124 size_t len = writeBufPtr - writeBuf;
125 Debug::log(Debug::Spam) << "Socket::emptyWriteBuf: len=" << len << Debug::flush;
126 ++numWrites;
127 totWriteBytes += len;
128 if (len > maxWriteBytes) {
129 maxWriteBytes = len;
130 }
131 for (char* ptr = writeBuf; len > 0; ) {
132 ssize_t n = send(sock, ptr, len, 0);
133 if (n <= 0) {
134 if (errno == EPIPE) {
135 Debug::log(Debug::Warning) << "Other end of socket disconnected" << Debug::flush;
136 disconnect(false);
137 return false;
138 }
139 Debug::log(Debug::Error) << "Error " << errno << " writing " << len << " bytes to socket"
140 << Debug::flush;
141 return false;
142 }
143 ptr += n;
144 len -= n;
145 }
146 writeBufPtr = writeBuf;
147 return true;
148}
149
150bool Socket::fillReadBuf() {
151 readBufPtr = readBuf;
152 errno = 0;
153 ssize_t n = recv(sock, readBuf, BUF_SIZE, 0);
154 if (n <= 0) {
155 // EOF results in no error
156 if (!errno || errno == EPIPE) {
157 Debug::log(Debug::Warning) << "Other end of socket disconnected" << Debug::flush;
158 disconnect(false);
159 return false;
160 }
161 Debug::log(Debug::Error) << "Error " << errno << " reading " << BUF_SIZE << " bytes from socket"
162 << Debug::flush;
163 return false;
164 }
165 ++numReads;
166 totReadBytes += n;
167 if (static_cast<size_t>(n) > maxReadBytes) {
168 maxReadBytes = n;
169 }
170 readValid = readBuf + n;
171 return true;
172}