| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * 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 <arpa/inet.h> |
| #include <errno.h> |
| #include <strings.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| #include "Log.h" |
| |
| #include "ClientSocket.h" |
| |
| ClientSocket::ClientSocket() |
| : mSocket(-1), |
| mTimeoutEnabled(false) |
| { |
| |
| } |
| |
| ClientSocket::~ClientSocket() |
| { |
| release(); |
| } |
| |
| bool ClientSocket::init(const char* hostIp, int port, bool enableTimeout) |
| { |
| LOGD("ClientSocket::init"); |
| mSocket = socket(AF_INET, SOCK_STREAM, 0); |
| if (mSocket < 0) { |
| LOGE("cannot open socket %d", errno); |
| return false; |
| } |
| int reuse = 1; |
| if (setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) { |
| LOGE("setsockopt error %d", errno); |
| release(); |
| return false; |
| } |
| |
| struct sockaddr_in serverAddr; |
| bzero((char*)&serverAddr, sizeof(serverAddr)); |
| serverAddr.sin_family = AF_INET; |
| serverAddr.sin_port = htons(port); |
| if (inet_pton(AF_INET, hostIp, &serverAddr.sin_addr) != 1) { |
| release(); |
| LOGE("inet_pton failed %d", errno); |
| return false; |
| } |
| if (connect(mSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { |
| release(); |
| LOGE("cannot connect socket %d", errno); |
| return false; |
| } |
| mTimeoutEnabled = enableTimeout; |
| return true; |
| } |
| |
| const int ZERO_RW_SLEEP_TIME_US = 10; |
| |
| // make non-blocking mode only during read. This allows supporting time-out for read |
| bool ClientSocket::readData(char* data, int len, int timeoutInMs) |
| { |
| bool useTimeout = (mTimeoutEnabled && (timeoutInMs > 0)); |
| int flOriginal = 0; |
| int timeInSec = 0; |
| int timeInUs = 0; |
| if (useTimeout) { |
| flOriginal = fcntl(mSocket, F_GETFL,0); |
| if (flOriginal == -1) { |
| LOGE("fcntl error %d", errno); |
| return false; |
| } |
| if (fcntl(mSocket, F_SETFL, flOriginal | O_NONBLOCK) == -1) { |
| LOGE("fcntl error %d", errno); |
| return false; |
| } |
| timeInSec = timeoutInMs / 1000; |
| timeInUs = (timeoutInMs % 1000) * 1000; |
| } |
| bool result = true; |
| int read; |
| int toRead = len; |
| while (toRead > 0) { |
| if (useTimeout) { |
| fd_set rfds; |
| struct timeval tv; |
| tv.tv_sec = timeInSec; |
| tv.tv_usec = timeInUs; |
| FD_ZERO(&rfds); |
| FD_SET(mSocket, &rfds); |
| if (select(mSocket + 1, &rfds, NULL, NULL, &tv) == -1) { |
| LOGE("select failed"); |
| result = false; |
| break; |
| } |
| if (!FD_ISSET(mSocket, &rfds)) { |
| LOGE("socket read timeout"); |
| result = false; |
| break; |
| } |
| } |
| read = recv(mSocket, (void*)data, toRead, 0); |
| if (read > 0) { |
| toRead -= read; |
| data += read; |
| } else if (read == 0) { |
| // in blocking mode, zero read mean's peer closed. |
| // in non-blocking mode, select said that there is data. so it should not happen |
| LOGE("zero read, peer closed or what?, nonblocking: %d", useTimeout); |
| result = false; |
| break; |
| } else { |
| LOGE("recv returned %d", read); |
| result = false; |
| break; |
| } |
| } |
| if (useTimeout) { |
| fcntl(mSocket, F_SETFL, flOriginal); // now blocking again |
| } |
| return result; |
| } |
| |
| bool ClientSocket::sendData(const char* data, int len) |
| { |
| int sent; |
| int toSend = len; |
| while (toSend > 0) { |
| sent = send(mSocket, (void*)data, (size_t)toSend, 0); |
| if (sent > 0) { |
| toSend -= sent; |
| data += sent; |
| } else if (sent == 0) { // no more buffer? |
| usleep(ZERO_RW_SLEEP_TIME_US); // just wait |
| } else { |
| LOGE("send returned %d, error %d", sent, errno); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void ClientSocket::release() |
| { |
| if (mSocket != -1) { |
| close(mSocket); |
| mSocket = -1; |
| } |
| } |