Merge "libcutils: add multi-buffer socket send." am: d95ecfc432
am: d56499b21d
* commit 'd56499b21d1411a7c799cdad316fb2138db26a7c':
libcutils: add multi-buffer socket send.
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
index 6cffe12..cb9b3ff 100644
--- a/include/cutils/sockets.h
+++ b/include/cutils/sockets.h
@@ -30,12 +30,15 @@
typedef int socklen_t;
typedef SOCKET cutils_socket_t;
+typedef WSABUF cutils_socket_buffer_t;
#else
#include <sys/socket.h>
+#include <sys/uio.h>
typedef int cutils_socket_t;
+typedef struct iovec cutils_socket_buffer_t;
#define INVALID_SOCKET (-1)
#endif
@@ -137,6 +140,28 @@
int socket_get_local_port(cutils_socket_t sock);
/*
+ * Sends to a socket from multiple buffers; wraps writev() on Unix or WSASend()
+ * on Windows. This can give significant speedup compared to calling send()
+ * multiple times.
+ *
+ * Because Unix and Windows use different structs to hold buffers, we also
+ * need a generic function to set up the buffers.
+ *
+ * Example usage:
+ * cutils_socket_buffer_t buffers[2] = {
+ * make_cutils_socket_buffer(data0, len0),
+ * make_cutils_socket_buffer(data1, len1)
+ * };
+ * socket_send_buffers(sock, buffers, 2);
+ *
+ * Returns the number of bytes written or -1 on error.
+ */
+cutils_socket_buffer_t make_cutils_socket_buffer(void* data, size_t length);
+ssize_t socket_send_buffers(cutils_socket_t sock,
+ cutils_socket_buffer_t* buffers,
+ size_t num_buffers);
+
+/*
* socket_peer_is_trusted - Takes a socket which is presumed to be a
* connected local socket (e.g. AF_LOCAL) and returns whether the peer
* (the userid that owns the process on the other end of that socket)
diff --git a/libcutils/sockets_unix.c b/libcutils/sockets_unix.c
index 5eddc4b..3e7cea0 100644
--- a/libcutils/sockets_unix.c
+++ b/libcutils/sockets_unix.c
@@ -47,12 +47,25 @@
}
int socket_close(int sock) {
- return close(sock);
+ return close(sock);
}
int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
- struct timeval tv;
- tv.tv_sec = timeout_ms / 1000;
- tv.tv_usec = (timeout_ms % 1000) * 1000;
- return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ struct timeval tv;
+ tv.tv_sec = timeout_ms / 1000;
+ tv.tv_usec = (timeout_ms % 1000) * 1000;
+ return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+}
+
+cutils_socket_buffer_t make_cutils_socket_buffer(void* data, size_t length) {
+ cutils_socket_buffer_t buffer;
+ buffer.iov_base = data;
+ buffer.iov_len = length;
+ return buffer;
+}
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+ cutils_socket_buffer_t* buffers,
+ size_t num_buffers) {
+ return writev(sock, buffers, num_buffers);
}
diff --git a/libcutils/sockets_windows.c b/libcutils/sockets_windows.c
index 1bf2933..8153688 100644
--- a/libcutils/sockets_windows.c
+++ b/libcutils/sockets_windows.c
@@ -58,3 +58,22 @@
return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_ms,
sizeof(timeout_ms));
}
+
+cutils_socket_buffer_t make_cutils_socket_buffer(void* data, size_t length) {
+ cutils_socket_buffer_t buffer;
+ buffer.buf = data;
+ buffer.len = length;
+ return buffer;
+}
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+ cutils_socket_buffer_t* buffers,
+ size_t num_buffers) {
+ DWORD bytes_sent = 0;
+
+ if (WSASend(sock, buffers, num_buffers, &bytes_sent, 0, NULL, NULL) !=
+ SOCKET_ERROR) {
+ return bytes_sent;
+ }
+ return -1;
+}
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp
index 6c74b9a..40fa9b1 100644
--- a/libcutils/tests/sockets_test.cpp
+++ b/libcutils/tests/sockets_test.cpp
@@ -34,17 +34,17 @@
ASSERT_NE(INVALID_SOCKET, server);
ASSERT_NE(INVALID_SOCKET, client);
- char buffer[3];
+ char buffer[128];
sockaddr_storage addr;
socklen_t addr_size = sizeof(addr);
// Send client -> server first to get the UDP client's address.
ASSERT_EQ(3, send(client, "foo", 3, 0));
if (type == SOCK_DGRAM) {
- EXPECT_EQ(3, recvfrom(server, buffer, 3, 0,
+ EXPECT_EQ(3, recvfrom(server, buffer, sizeof(buffer), 0,
reinterpret_cast<sockaddr*>(&addr), &addr_size));
} else {
- EXPECT_EQ(3, recv(server, buffer, 3, 0));
+ EXPECT_EQ(3, recv(server, buffer, sizeof(buffer), 0));
}
EXPECT_EQ(0, memcmp(buffer, "foo", 3));
@@ -55,9 +55,20 @@
} else {
ASSERT_EQ(3, send(server, "bar", 3, 0));
}
- EXPECT_EQ(3, recv(client, buffer, 3, 0));
+ EXPECT_EQ(3, recv(client, buffer, sizeof(buffer), 0));
EXPECT_EQ(0, memcmp(buffer, "bar", 3));
+ // Send multiple buffers using socket_send_buffers().
+ std::string data[] = {"foo", "bar", "12345"};
+ cutils_socket_buffer_t socket_buffers[3];
+ for (int i = 0; i < 3; ++i) {
+ socket_buffers[i] = make_cutils_socket_buffer(&data[i][0],
+ data[i].length());
+ }
+ EXPECT_EQ(11, socket_send_buffers(client, socket_buffers, 3));
+ EXPECT_EQ(11, recv(server, buffer, sizeof(buffer), 0));
+ EXPECT_EQ(0, memcmp(buffer, "foobar12345", 11));
+
EXPECT_EQ(0, socket_close(server));
EXPECT_EQ(0, socket_close(client));
}
@@ -171,3 +182,8 @@
EXPECT_EQ(0, socket_close(client));
EXPECT_EQ(0, socket_close(handler));
}
+
+// Tests socket_send_buffers() failure.
+TEST(SocketsTest, TestSocketSendBuffersFailure) {
+ EXPECT_EQ(-1, socket_send_buffers(INVALID_SOCKET, nullptr, 0));
+}