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