summaryrefslogtreecommitdiffstats
path: root/libs/utils/Socket.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit7c1b96a165f970a09ed239bb4fb3f1b0d8f2a407 (patch)
treedf5a6539447324de36e95b057d6b9f0361b7a250 /libs/utils/Socket.cpp
downloadframeworks_native-7c1b96a165f970a09ed239bb4fb3f1b0d8f2a407.zip
frameworks_native-7c1b96a165f970a09ed239bb4fb3f1b0d8f2a407.tar.gz
frameworks_native-7c1b96a165f970a09ed239bb4fb3f1b0d8f2a407.tar.bz2
Initial Contribution
Diffstat (limited to 'libs/utils/Socket.cpp')
-rw-r--r--libs/utils/Socket.cpp388
1 files changed, 388 insertions, 0 deletions
diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp
new file mode 100644
index 0000000..51509a3
--- /dev/null
+++ b/libs/utils/Socket.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Internet address class.
+//
+
+#ifdef HAVE_WINSOCK
+// This needs to come first, or Cygwin gets concerned about a potential
+// clash between WinSock and <sys/types.h>.
+# include <winsock2.h>
+#endif
+
+#include <utils/Socket.h>
+#include <utils/inet_address.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+
+#ifndef HAVE_WINSOCK
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+
+/*
+ * ===========================================================================
+ * Socket
+ * ===========================================================================
+ */
+
+#ifndef INVALID_SOCKET
+# define INVALID_SOCKET (-1)
+#endif
+#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET)
+
+/*static*/ bool Socket::mBootInitialized = false;
+
+/*
+ * Extract system-dependent error code.
+ */
+static inline int getSocketError(void) {
+#ifdef HAVE_WINSOCK
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+/*
+ * One-time initialization for socket code.
+ */
+/*static*/ bool Socket::bootInit(void)
+{
+#ifdef HAVE_WINSOCK
+ WSADATA wsaData;
+ int err;
+
+ err = WSAStartup(MAKEWORD(2, 0), &wsaData);
+ if (err != 0) {
+ LOG(LOG_ERROR, "socket", "Unable to start WinSock\n");
+ return false;
+ }
+
+ LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n",
+ LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
+#endif
+
+ mBootInitialized = true;
+ return true;
+}
+
+/*
+ * One-time shutdown for socket code.
+ */
+/*static*/ void Socket::finalShutdown(void)
+{
+#ifdef HAVE_WINSOCK
+ WSACleanup();
+#endif
+ mBootInitialized = false;
+}
+
+
+/*
+ * Simple constructor. Allow the application to create us and then make
+ * bind/connect calls.
+ */
+Socket::Socket(void)
+ : mSock(UNDEF_SOCKET)
+{
+ if (!mBootInitialized)
+ LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n");
+}
+
+/*
+ * Destructor. Closes the socket and resets our storage.
+ */
+Socket::~Socket(void)
+{
+ close();
+}
+
+
+/*
+ * Create a socket and connect to the specified host and port.
+ */
+int Socket::connect(const char* host, int port)
+{
+ if (mSock != UNDEF_SOCKET) {
+ LOG(LOG_WARN, "socket", "Socket already connected\n");
+ return -1;
+ }
+
+ InetSocketAddress sockAddr;
+ if (!sockAddr.create(host, port))
+ return -1;
+
+ //return doConnect(sockAddr);
+ int foo;
+ foo = doConnect(sockAddr);
+ return foo;
+}
+
+/*
+ * Create a socket and connect to the specified host and port.
+ */
+int Socket::connect(const InetAddress* addr, int port)
+{
+ if (mSock != UNDEF_SOCKET) {
+ LOG(LOG_WARN, "socket", "Socket already connected\n");
+ return -1;
+ }
+
+ InetSocketAddress sockAddr;
+ if (!sockAddr.create(addr, port))
+ return -1;
+
+ return doConnect(sockAddr);
+}
+
+/*
+ * Finish creating a socket by connecting to the remote host.
+ *
+ * Returns 0 on success.
+ */
+int Socket::doConnect(const InetSocketAddress& sockAddr)
+{
+#ifdef HAVE_WINSOCK
+ SOCKET sock;
+#else
+ int sock;
+#endif
+ const InetAddress* addr = sockAddr.getAddress();
+ int port = sockAddr.getPort();
+ struct sockaddr_in inaddr;
+ DurationTimer connectTimer;
+
+ assert(sizeof(struct sockaddr_in) == addr->getAddressLength());
+ memcpy(&inaddr, addr->getAddress(), addr->getAddressLength());
+ inaddr.sin_port = htons(port);
+
+ //fprintf(stderr, "--- connecting to %s:%d\n",
+ // sockAddr.getHostName(), port);
+
+ sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == INVALID_SOCKET) {
+ int err = getSocketError();
+ LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err);
+ return (err != 0) ? err : -1;
+ }
+
+ connectTimer.start();
+
+ if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) {
+ int err = getSocketError();
+ LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n",
+ sockAddr.getHostName(), port, err);
+ return (err != 0) ? err : -1;
+ }
+
+ connectTimer.stop();
+ if ((long) connectTimer.durationUsecs() > 100000) {
+ LOG(LOG_INFO, "socket",
+ "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(),
+ port, ((long) connectTimer.durationUsecs()) / 1000000.0);
+ }
+
+ mSock = (unsigned long) sock;
+ LOG(LOG_VERBOSE, "socket",
+ "--- connected to %s:%d\n", sockAddr.getHostName(), port);
+ return 0;
+}
+
+
+/*
+ * Close the socket if it needs closing.
+ */
+bool Socket::close(void)
+{
+ if (mSock != UNDEF_SOCKET) {
+ //fprintf(stderr, "--- closing socket %lu\n", mSock);
+#ifdef HAVE_WINSOCK
+ if (::closesocket((SOCKET) mSock) != 0)
+ return false;
+#else
+ if (::close((int) mSock) != 0)
+ return false;
+#endif
+ }
+
+ mSock = UNDEF_SOCKET;
+
+ return true;
+}
+
+/*
+ * Read data from socket.
+ *
+ * Standard semantics: read up to "len" bytes into "buf". Returns the
+ * number of bytes read, or less than zero on error.
+ */
+int Socket::read(void* buf, ssize_t len) const
+{
+ if (mSock == UNDEF_SOCKET) {
+ LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n");
+ return -500;
+ }
+
+#ifdef HAVE_WINSOCK
+ SOCKET sock = (SOCKET) mSock;
+#else
+ int sock = (int) mSock;
+#endif
+ int cc;
+
+ cc = recv(sock, (char*)buf, len, 0);
+ if (cc < 0) {
+ int err = getSocketError();
+ return (err > 0) ? -err : -1;
+ }
+
+ return cc;
+}
+
+/*
+ * Write data to a socket.
+ *
+ * Standard semantics: write up to "len" bytes into "buf". Returns the
+ * number of bytes written, or less than zero on error.
+ */
+int Socket::write(const void* buf, ssize_t len) const
+{
+ if (mSock == UNDEF_SOCKET) {
+ LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n");
+ return -500;
+ }
+
+#ifdef HAVE_WINSOCK
+ SOCKET sock = (SOCKET) mSock;
+#else
+ int sock = (int) mSock;
+#endif
+ int cc;
+
+ cc = send(sock, (const char*)buf, len, 0);
+ if (cc < 0) {
+ int err = getSocketError();
+ return (err > 0) ? -err : -1;
+ }
+
+ return cc;
+}
+
+
+/*
+ * ===========================================================================
+ * Socket tests
+ * ===========================================================================
+ */
+
+/*
+ * Read all data from the socket. The data is read into a buffer that
+ * expands as needed.
+ *
+ * On exit, the buffer is returned, and the length of the data is stored
+ * in "*sz". A null byte is added to the end, but is not included in
+ * the length.
+ */
+static char* socketReadAll(const Socket& s, int *sz)
+{
+ int max, r;
+ char *data, *ptr, *tmp;
+
+ data = (char*) malloc(max = 32768);
+ if (data == NULL)
+ return NULL;
+
+ ptr = data;
+
+ for (;;) {
+ if ((ptr - data) == max) {
+ tmp = (char*) realloc(data, max *= 2);
+ if(tmp == 0) {
+ free(data);
+ return 0;
+ }
+ }
+ r = s.read(ptr, max - (ptr - data));
+ if (r == 0)
+ break;
+ if (r < 0) {
+ LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r);
+ break;
+ }
+ ptr += r;
+ }
+
+ if ((ptr - data) == max) {
+ tmp = (char*) realloc(data, max + 1);
+ if (tmp == NULL) {
+ free(data);
+ return NULL;
+ }
+ }
+ *ptr = '\0';
+ *sz = (ptr - data);
+ return data;
+}
+
+/*
+ * Exercise the Socket class.
+ */
+void android::TestSockets(void)
+{
+ printf("----- SOCKET TEST ------\n");
+ Socket::bootInit();
+
+ char* buf = NULL;
+ int len, cc;
+ const char* kTestStr =
+ "GET / HTTP/1.0\n"
+ "Connection: close\n"
+ "\n";
+
+ Socket sock;
+ if (sock.connect("www.google.com", 80) != 0) {
+ fprintf(stderr, "socket connected failed\n");
+ goto bail;
+ }
+
+ cc = sock.write(kTestStr, strlen(kTestStr));
+ if (cc != (int) strlen(kTestStr)) {
+ fprintf(stderr, "write failed, res=%d\n", cc);
+ goto bail;
+ }
+ buf = socketReadAll(sock, &len);
+
+ printf("GOT '%s'\n", buf);
+
+bail:
+ sock.close();
+ free(buf);
+}
+