Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      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 #include <arpa/inet.h>
     17 #include <errno.h>
     18 #include <strings.h>
     19 #include <sys/types.h>
     20 #include <sys/socket.h>
     21 #include <unistd.h>
     22 #include <fcntl.h>
     23 
     24 #include "Log.h"
     25 
     26 #include "ClientSocket.h"
     27 
     28 ClientSocket::ClientSocket()
     29     : mSocket(-1),
     30       mTimeoutEnabled(false)
     31 {
     32 
     33 }
     34 
     35 ClientSocket::~ClientSocket()
     36 {
     37     release();
     38 }
     39 
     40 bool ClientSocket::init(const char* hostIp, int port, bool enableTimeout)
     41 {
     42     LOGD("ClientSocket::init");
     43     mSocket = socket(AF_INET, SOCK_STREAM, 0);
     44     if (mSocket < 0) {
     45         LOGE("cannot open socket %d", errno);
     46         return false;
     47     }
     48     int reuse = 1;
     49     if (setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
     50         LOGE("setsockopt error %d", errno);
     51         release();
     52         return false;
     53     }
     54 
     55     struct sockaddr_in serverAddr;
     56     bzero((char*)&serverAddr, sizeof(serverAddr));
     57     serverAddr.sin_family = AF_INET;
     58     serverAddr.sin_port = htons(port);
     59     if (inet_pton(AF_INET, hostIp, &serverAddr.sin_addr) != 1) {
     60         release();
     61         LOGE("inet_pton failed %d", errno);
     62         return false;
     63     }
     64     if (connect(mSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
     65         release();
     66         LOGE("cannot connect socket %d", errno);
     67         return false;
     68     }
     69     mTimeoutEnabled = enableTimeout;
     70     return true;
     71 }
     72 
     73 const int ZERO_RW_SLEEP_TIME_US = 10;
     74 
     75 // make non-blocking mode only during read. This allows supporting time-out for read
     76 bool ClientSocket::readData(char* data, int len, int timeoutInMs)
     77 {
     78     bool useTimeout = (mTimeoutEnabled && (timeoutInMs > 0));
     79     int flOriginal = 0;
     80     int timeInSec = 0;
     81     int timeInUs = 0;
     82     if (useTimeout) {
     83         flOriginal = fcntl(mSocket, F_GETFL,0);
     84         if (flOriginal == -1) {
     85             LOGE("fcntl error %d", errno);
     86             return false;
     87         }
     88         if (fcntl(mSocket, F_SETFL, flOriginal | O_NONBLOCK) == -1) {
     89             LOGE("fcntl error %d", errno);
     90             return false;
     91         }
     92         timeInSec = timeoutInMs / 1000;
     93         timeInUs = (timeoutInMs % 1000) * 1000;
     94     }
     95     bool result = true;
     96     int read;
     97     int toRead = len;
     98     while (toRead > 0) {
     99         if (useTimeout) {
    100             fd_set rfds;
    101             struct timeval tv;
    102             tv.tv_sec = timeInSec;
    103             tv.tv_usec = timeInUs;
    104             FD_ZERO(&rfds);
    105             FD_SET(mSocket, &rfds);
    106             if (select(mSocket + 1, &rfds, NULL, NULL, &tv) == -1) {
    107                 LOGE("select failed");
    108                 result = false;
    109                 break;
    110             }
    111             if (!FD_ISSET(mSocket, &rfds)) {
    112                 LOGE("socket read timeout");
    113                 result = false;
    114                 break;
    115             }
    116         }
    117         read = recv(mSocket, (void*)data, toRead, 0);
    118         if (read > 0) {
    119             toRead -= read;
    120             data += read;
    121         } else if (read == 0) {
    122             // in blocking mode, zero read mean's peer closed.
    123             // in non-blocking mode, select said that there is data. so it should not happen
    124             LOGE("zero read, peer closed or what?, nonblocking: %d", useTimeout);
    125             result = false;
    126             break;
    127         } else {
    128             LOGE("recv returned %d", read);
    129             result = false;
    130             break;
    131         }
    132     }
    133     if (useTimeout) {
    134         fcntl(mSocket, F_SETFL, flOriginal); // now blocking again
    135     }
    136     return result;
    137 }
    138 
    139 bool ClientSocket::sendData(const char* data, int len)
    140 {
    141     int sent;
    142     int toSend = len;
    143     while (toSend > 0) {
    144         sent = send(mSocket, (void*)data, (size_t)toSend, 0);
    145         if (sent > 0) {
    146             toSend -= sent;
    147             data += sent;
    148         } else if (sent == 0) { // no more buffer?
    149             usleep(ZERO_RW_SLEEP_TIME_US); // just wait
    150         } else {
    151             LOGE("send returned %d, error %d", sent, errno);
    152             return false;
    153         }
    154     }
    155     return true;
    156 }
    157 
    158 void ClientSocket::release()
    159 {
    160     if (mSocket != -1) {
    161         close(mSocket);
    162         mSocket = -1;
    163     }
    164 }
    165