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 
ClientSocket()28 ClientSocket::ClientSocket()
29     : mSocket(-1),
30       mTimeoutEnabled(false)
31 {
32 
33 }
34 
~ClientSocket()35 ClientSocket::~ClientSocket()
36 {
37     release();
38 }
39 
init(const char * hostIp,int port,bool enableTimeout)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
readData(char * data,int len,int timeoutInMs)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 
sendData(const char * data,int len)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 
release()158 void ClientSocket::release()
159 {
160     if (mSocket != -1) {
161         close(mSocket);
162         mSocket = -1;
163     }
164 }
165