diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h index 6cffe1265..cb9b3ff8e 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 +#include typedef int cutils_socket_t; +typedef struct iovec cutils_socket_buffer_t; #define INVALID_SOCKET (-1) #endif @@ -136,6 +139,28 @@ int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms); */ 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 diff --git a/libcutils/sockets_unix.c b/libcutils/sockets_unix.c index 5eddc4be1..3e7cea087 100644 --- a/libcutils/sockets_unix.c +++ b/libcutils/sockets_unix.c @@ -47,12 +47,25 @@ bool socket_peer_is_trusted(int fd __android_unused) } 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 1bf2933bd..815368822 100644 --- a/libcutils/sockets_windows.c +++ b/libcutils/sockets_windows.c @@ -58,3 +58,22 @@ int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) { 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 6c74b9a80..40fa9b110 100644 --- a/libcutils/tests/sockets_test.cpp +++ b/libcutils/tests/sockets_test.cpp @@ -34,17 +34,17 @@ static void TestConnectedSockets(cutils_socket_t server, cutils_socket_t client, 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(&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 @@ static void TestConnectedSockets(cutils_socket_t server, cutils_socket_t client, } 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 @@ TEST(SocketsTest, TestTcpReceiveTimeout) { 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)); +}