| /* |
| * Copyright 2008 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| #include "Platform.h" |
| |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <cerrno> |
| |
| #include "Socket.h" |
| |
| void Socket::init() { |
| #ifdef _WINDOWS |
| // version 2.2 supported on Win95OSR2/WinNT4 and up |
| WORD winsockVers = MAKEWORD(2, 2); |
| WSADATA wsaData; |
| int err = WSAStartup(winsockVers, &wsaData); |
| if (err) { |
| // TODO(jat): report error |
| Debug::log(Debug::Error) << "WSAStartup(vers=2.2): err=" << err << Debug::flush; |
| } |
| #endif |
| } |
| |
| bool Socket::connect(const char* host, int port) { |
| Debug::log(Debug::Debugging) << "Socket::connect(host=" << host << ",port=" << port << ")" |
| << Debug::flush; |
| if (isConnected()) { |
| Debug::log(Debug::Error) << "Socket::connect - already connected" << Debug::flush; |
| return false; |
| } |
| |
| SOCKETTYPE fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
| if (fd < 0) { |
| Debug::log(Debug::Error) << "Socket::connect - can't get socket" << Debug::flush; |
| return false; |
| } |
| #ifdef SO_NOSIGPIPE |
| // On BSD, we need to suppress the SIGPIPE if the remote end disconnects. |
| int option_value = 1; |
| if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &option_value, sizeof(int))) { |
| Debug::log(Debug::Error) << "Socket::connect - can't set NOSIGPIPE option" << Debug::flush; |
| return false; |
| } |
| #endif |
| |
| struct sockaddr_in sockAddr; |
| memset(&sockAddr, 0, static_cast<int>(sizeof(sockAddr))); |
| // check for numeric IP4 addresses first |
| // TODO(jat): handle IPv6 addresses |
| unsigned long numericAddr = inet_addr(host); |
| if (numericAddr != 0xFFFFFFFF) { |
| sockAddr.sin_addr.s_addr = numericAddr; |
| sockAddr.sin_family = AF_INET; |
| } else { |
| struct hostent* hent = gethostbyname(host); |
| if (!hent || !hent->h_addr_list[0]) { |
| Debug::log(Debug::Error) << "Unable to get address for " << host << Debug::flush; |
| return false; |
| } |
| memcpy(&(sockAddr.sin_addr), hent->h_addr_list[0], hent->h_length); |
| sockAddr.sin_family = hent->h_addrtype; |
| } |
| sockAddr.sin_port = htons(port); |
| |
| if (::connect(fd, (struct sockaddr*) &sockAddr, sizeof(sockAddr)) < 0) { |
| #ifdef _WINDOWS |
| char buf[256]; |
| strerror_s(buf, sizeof(buf), errno); |
| Debug::log(Debug::Error) << "Can't connect to " << host << ":" << port << " -- " |
| << buf << Debug::flush; |
| closesocket(fd); |
| #else |
| Debug::log(Debug::Error) << "Can't connect to " << host << ":" << port << " -- " |
| << strerror(errno) << Debug::flush; |
| close(fd); |
| #endif |
| return false; |
| } |
| sock = fd; |
| connected = true; |
| readBufPtr = readValid = readBuf; |
| writeBufPtr = writeBuf; |
| #ifdef _WINDOWS |
| Debug::log(Debug::Spam) << " connected" << Debug::flush; |
| #else |
| Debug::log(Debug::Spam) << " connected, fd=" << fd << Debug::flush; |
| #endif |
| return true; |
| } |
| |
| bool Socket::disconnect(bool doFlush) { |
| if (connected) { |
| Debug::log(Debug::Debugging) << "Disconnecting socket" << Debug::flush; |
| if (doFlush) { |
| flush(); |
| } |
| connected = false; |
| #ifdef _WINDOWS |
| closesocket(sock); |
| #else |
| shutdown(sock, SHUT_RDWR); |
| close(sock); |
| #endif |
| } |
| return true; |
| } |
| |
| bool Socket::emptyWriteBuf() { |
| size_t len = writeBufPtr - writeBuf; |
| Debug::log(Debug::Spam) << "Socket::emptyWriteBuf: len=" << len << Debug::flush; |
| ++numWrites; |
| totWriteBytes += len; |
| if (len > maxWriteBytes) { |
| maxWriteBytes = len; |
| } |
| for (char* ptr = writeBuf; len > 0; ) { |
| ssize_t n = send(sock, ptr, len, 0); |
| if (n <= 0) { |
| if (errno == EPIPE) { |
| Debug::log(Debug::Warning) << "Other end of socket disconnected" << Debug::flush; |
| disconnect(false); |
| return false; |
| } |
| Debug::log(Debug::Error) << "Error " << errno << " writing " << len << " bytes to socket" |
| << Debug::flush; |
| return false; |
| } |
| ptr += n; |
| len -= n; |
| } |
| writeBufPtr = writeBuf; |
| return true; |
| } |
| |
| bool Socket::fillReadBuf() { |
| readBufPtr = readBuf; |
| errno = 0; |
| ssize_t n = recv(sock, readBuf, BUF_SIZE, 0); |
| if (n <= 0) { |
| // EOF results in no error |
| if (!errno || errno == EPIPE) { |
| Debug::log(Debug::Warning) << "Other end of socket disconnected" << Debug::flush; |
| disconnect(false); |
| return false; |
| } |
| Debug::log(Debug::Error) << "Error " << errno << " reading " << BUF_SIZE << " bytes from socket" |
| << Debug::flush; |
| return false; |
| } |
| ++numReads; |
| totReadBytes += n; |
| if (static_cast<size_t>(n) > maxReadBytes) { |
| maxReadBytes = n; |
| } |
| readValid = readBuf + n; |
| return true; |
| } |