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