Commit 515481e6 authored by Philip Withnall's avatar Philip Withnall Committed by Olivier Crête

socket: Add vectored I/O support for sending on sockets

Replace the send() API with a send_messages() API, which supports
sending multiple messages, each with multiple buffers rather than a
single monolithic buffer.

This doesn’t break API, as the socket API is not exposed outside
libnice. It does introduce a new struct: NiceOutputMessage, which is
analogous to struct mmsghdr and NiceInputMessage.

This includes updates to the test-bsd test to cover the changed API.

The existing nice_socket_send() API has been retained as a thin wrapper
around nice_socket_send_messages(), for convenience only. It’s hoped
that internal usage of this API will decline to the point where it can
be removed.
parent 55e53a9c
......@@ -217,4 +217,7 @@ memcpy_buffer_to_input_message (NiceInputMessage *message,
guint8 *
compact_input_message (NiceInputMessage *message, gsize *buffer_length);
guint8 *
compact_output_message (const NiceOutputMessage *message, gsize *buffer_length);
#endif /*_NICE_AGENT_PRIV_H */
......@@ -2781,6 +2781,20 @@ memcpy_buffer_to_input_message (NiceInputMessage *message,
return message->length;
}
/* Concatenate all the buffers in the given @message into a single, newly
* allocated, monolithic buffer which is returned. The length of the new buffer
* is returned in @buffer_length, and should be equal to the length field of
* @recv_message.
*
* The return value must be freed with g_free(). */
guint8 *
compact_output_message (const NiceOutputMessage *message, gsize *buffer_length)
{
/* This works as long as NiceInputMessage and NiceOutputMessage are layed out
* identically. */
return compact_input_message ((NiceInputMessage *) message, buffer_length);
}
/**
* nice_input_message_iter_reset:
* @iter: a #NiceInputMessageIter
......
......@@ -170,6 +170,39 @@ typedef struct {
gsize length; /* sum of the lengths of @buffers */
} NiceInputMessage;
/**
* NiceOutputMessage:
* @buffers: (array length=n_buffers): unowned array of #GOutputVector buffers
* which contain data to transmit for this message
* @n_buffers: number of #GOutputVectors in @buffers, or -1 to indicate @buffers
* is %NULL-terminated
* @to: (allow-none): address of the peer to transmit the message to, or %NULL
* to use the default address for the outbound socket
* @length: total number of valid bytes contiguously stored in @buffers
*
* Represents a single message to transmit on the network. For reliable
* connections, this is essentially just an array of buffers (specifically,
* @to can be ignored). for non-reliable connections, it represents a single
* packet to send to the OS.
*
* @n_buffers may be -1 to indicate that @buffers is terminated by a
* #GOutputVector with a %NULL buffer pointer.
*
* By providing arrays of #NiceOutputMessages to functions like
* nice_agent_send_messages_nonblocking(), multiple messages may be transmitted
* with a single call, which is more efficient than making multiple calls in a
* loop. In this manner, nice_agent_send_messages_nonblocking() is analogous to
* sendmmsg(); and #NiceOutputMessage to struct mmsghdr.
*
* Since: 0.1.5
*/
typedef struct {
GOutputVector *buffers;
gint n_buffers;
const NiceAddress *to;
gsize length;
} NiceOutputMessage;
#define NICE_TYPE_AGENT nice_agent_get_type()
......
......@@ -92,12 +92,12 @@ struct to_be_sent {
static void socket_close (NiceSocket *sock);
static gint socket_recv_messages (NiceSocket *sock,
NiceInputMessage *recv_messages, guint n_recv_messages);
static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf);
static gint socket_send_messages (NiceSocket *sock,
const NiceOutputMessage *messages, guint n_messages);
static gboolean socket_is_reliable (NiceSocket *sock);
static void add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
const gchar *buf, guint len);
static void add_to_be_sent (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages);
static void free_to_be_sent (struct to_be_sent *tbs);
......@@ -125,7 +125,7 @@ nice_http_socket_new (NiceSocket *base_socket,
sock->fileno = priv->base_socket->fileno;
sock->addr = priv->base_socket->addr;
sock->send = socket_send;
sock->send_messages = socket_send_messages;
sock->recv_messages = socket_recv_messages;
sock->is_reliable = socket_is_reliable;
sock->close = socket_close;
......@@ -136,6 +136,9 @@ nice_http_socket_new (NiceSocket *base_socket,
gchar *credential = NULL;
gchar host[INET6_ADDRSTRLEN];
gint port = nice_address_get_port (&priv->addr);
GOutputVector local_bufs;
NiceOutputMessage local_messages;
nice_address_to_string (&priv->addr, host);
if (username) {
......@@ -158,7 +161,14 @@ nice_http_socket_new (NiceSocket *base_socket,
credential? credential : "" );
g_free (credential);
nice_socket_send (priv->base_socket, NULL, strlen (msg), msg);
local_bufs.buffer = msg;
local_bufs.size = strlen (msg);
local_messages.buffers = &local_bufs;
local_messages.n_buffers = 1;
local_messages.to = NULL;
local_messages.length = local_bufs.size;
nice_socket_send_messages (priv->base_socket, &local_messages, 1);
priv->state = HTTP_STATE_INIT;
g_free (msg);
}
......@@ -565,23 +575,25 @@ retry:
return -1;
}
static gboolean
socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf)
static gint
socket_send_messages (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages)
{
HttpPriv *priv = sock->priv;
if (priv->state == HTTP_STATE_CONNECTED) {
if (priv->base_socket)
return nice_socket_send (priv->base_socket, to, len, buf);
else
return FALSE;
/* Fast path. */
if (!priv->base_socket)
return -1;
return nice_socket_send_messages (priv->base_socket, messages, n_messages);
} else if (priv->state == HTTP_STATE_ERROR) {
return FALSE;
return -1;
} else {
add_to_be_sent (sock, to, buf, len);
add_to_be_sent (sock, messages, n_messages);
}
return TRUE;
return n_messages;
}
......@@ -593,22 +605,46 @@ socket_is_reliable (NiceSocket *sock)
static void
add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
const gchar *buf, guint len)
add_to_be_sent (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages)
{
HttpPriv *priv = sock->priv;
struct to_be_sent *tbs = NULL;
guint i;
if (len <= 0)
if (n_messages == 0)
return;
tbs = g_slice_new0 (struct to_be_sent);
tbs->buf = g_memdup (buf, len);
tbs->length = len;
if (to)
tbs->to = *to;
g_queue_push_tail (&priv->send_queue, tbs);
/* Compact the message’s buffers before queueing. */
for (i = 0; i < n_messages; i++) {
const NiceOutputMessage *message = &messages[i];
struct to_be_sent *tbs = NULL;
guint j;
gsize message_len_remaining = message->length;
gsize offset = 0;
if (message->length == 0)
continue;
tbs = g_slice_new0 (struct to_be_sent);
tbs->buf = g_malloc (message->length);
tbs->length = message->length;
if (message->to)
tbs->to = *message->to;
g_queue_push_tail (&priv->send_queue, tbs);
for (j = 0;
(message->n_buffers >= 0 && j < (guint) message->n_buffers) ||
(message->n_buffers < 0 && message->buffers[j].buffer != NULL);
j++) {
const GOutputVector *buffer = &message->buffers[j];
gsize len;
len = MIN (buffer->size, message_len_remaining);
memcpy (tbs->buf + offset, buffer->buffer, len);
message_len_remaining -= len;
offset += len;
}
}
}
......
......@@ -58,8 +58,8 @@ typedef struct {
struct to_be_sent {
guint length;
gchar *buf;
guint8 *buf; /* owned */
gsize length;
NiceAddress to;
};
......@@ -91,12 +91,12 @@ static const gchar SSL_CLIENT_HANDSHAKE[] = {
static void socket_close (NiceSocket *sock);
static gint socket_recv_messages (NiceSocket *sock,
NiceInputMessage *recv_messages, guint n_recv_messages);
static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf);
static gint socket_send_messages (NiceSocket *sock,
const NiceOutputMessage *messages, guint n_messages);
static gboolean socket_is_reliable (NiceSocket *sock);
static void add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
const gchar *buf, guint len);
static void add_to_be_sent (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages);
static void free_to_be_sent (struct to_be_sent *tbs);
......@@ -112,7 +112,7 @@ nice_pseudossl_socket_new (NiceSocket *base_socket)
sock->fileno = priv->base_socket->fileno;
sock->addr = priv->base_socket->addr;
sock->send = socket_send;
sock->send_messages = socket_send_messages;
sock->recv_messages = socket_recv_messages;
sock->is_reliable = socket_is_reliable;
sock->close = socket_close;
......@@ -173,7 +173,8 @@ socket_recv_messages (NiceSocket *sock,
struct to_be_sent *tbs = NULL;
priv->handshaken = TRUE;
while ((tbs = g_queue_pop_head (&priv->send_queue))) {
nice_socket_send (priv->base_socket, &tbs->to, tbs->length, tbs->buf);
nice_socket_send (priv->base_socket, &tbs->to, tbs->length,
(const gchar *) tbs->buf);
g_free (tbs->buf);
g_slice_free (struct to_be_sent, tbs);
}
......@@ -188,19 +189,21 @@ socket_recv_messages (NiceSocket *sock,
return 0;
}
static gboolean
socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf)
static gint
socket_send_messages (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages)
{
PseudoSSLPriv *priv = sock->priv;
if (priv->handshaken) {
if (priv->base_socket)
return nice_socket_send (priv->base_socket, to, len, buf);
else
/* Fast path: pass directly through to the base socket once the handshake is
* complete. */
if (priv->base_socket == NULL)
return FALSE;
return nice_socket_send_messages (priv->base_socket, messages, n_messages);
} else {
add_to_be_sent (sock, to, buf, len);
add_to_be_sent (sock, messages, n_messages);
}
return TRUE;
}
......@@ -214,22 +217,41 @@ socket_is_reliable (NiceSocket *sock)
static void
add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
const gchar *buf, guint len)
add_to_be_sent (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages)
{
PseudoSSLPriv *priv = sock->priv;
struct to_be_sent *tbs = NULL;
if (len <= 0)
return;
tbs = g_slice_new0 (struct to_be_sent);
tbs->buf = g_memdup (buf, len);
tbs->length = len;
if (to)
tbs->to = *to;
g_queue_push_tail (&priv->send_queue, tbs);
guint i;
for (i = 0; i < n_messages; i++) {
struct to_be_sent *tbs;
const NiceOutputMessage *message = &messages[i];
guint j;
gsize offset = 0;
tbs = g_slice_new0 (struct to_be_sent);
/* Compact the buffer. */
tbs->buf = g_malloc (message->length);
tbs->length = message->length;
if (message->to != NULL)
tbs->to = *message->to;
g_queue_push_tail (&priv->send_queue, tbs);
for (j = 0;
(message->n_buffers >= 0 && j < (guint) message->n_buffers) ||
(message->n_buffers < 0 && message->buffers[j].buffer != NULL);
j++) {
const GOutputVector *buffer = &message->buffers[j];
gsize len;
len = MIN (message->length - offset, buffer->size);
memcpy (tbs->buf + offset, buffer->buffer, len);
offset += len;
}
g_assert_cmpuint (offset, ==, message->length);
}
}
static void
......
......@@ -93,11 +93,66 @@ nice_socket_recv_messages (NiceSocket *sock,
return sock->recv_messages (sock, recv_messages, n_recv_messages);
}
gboolean
nice_socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf)
/**
* nice_socket_send_messages:
* @sock: a #NiceSocket
* @messages: (array length=n_messages) (in caller-allocates):
* array of #NiceOutputMessages containing the messages to send
* @n_messages: number of elements in the @messages array
*
* Send up to @n_messages on the socket, in a non-reliable, non-blocking
* fashion. The total size of the buffers in each #NiceOutputMessage
* must be at most the maximum UDP payload size (65535 bytes), or excess
* bytes will be silently dropped.
*
* On success, the number of messages transmitted from @messages is returned,
* which may be less than @n_messages if the call would have blocked
* part-way through. If the socket would have blocked to begin with, or if
* @n_messages is zero, zero is returned. On failure, a negative value is
* returned, but no further error information is available. Calling this
* function on a socket which has closed is an error, and a negative value is
* returned.
*
* If a positive N is returned, the first N messages in @messages have been
* sent in full, and the remaining messages have not been sent at all.
*
* If #NiceOutputMessage::to is specified for a message, that will be used as
* the destination address for the message. Otherwise, if %NULL, the default
* destination for @sock will be used.
*
* Every field of every #NiceOutputMessage is guaranteed to be unmodified when
* this function returns.
*
* Returns: number of messages successfully sent from @messages, or a negative
* value on error
*
* Since: 0.1.5
*/
gint
nice_socket_send_messages (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages)
{
return sock->send (sock, to, len, buf);
g_return_val_if_fail (sock != NULL, -1);
g_return_val_if_fail (n_messages == 0 || messages != NULL, -1);
return sock->send_messages (sock, messages, n_messages);
}
/* Convenience wrapper around nice_socket_send_messages(). Returns the number of
* bytes sent on success (which will be @len), zero if sending would block, or
* -1 on error. */
gssize
nice_socket_send (NiceSocket *sock, const NiceAddress *to, gsize len,
const gchar *buf)
{
GOutputVector local_buf = { buf, len };
NiceOutputMessage local_message = { &local_buf, 1, to, len };
gint ret;
ret = nice_socket_send_messages (sock, &local_message, 1);
if (ret == 1)
return len;
return ret;
}
gboolean
......
......@@ -63,8 +63,9 @@ struct _NiceSocket
* n_recv_messages is 0, recv_messages may be NULL. */
gint (*recv_messages) (NiceSocket *sock,
NiceInputMessage *recv_messages, guint n_recv_messages);
gboolean (*send) (NiceSocket *sock, const NiceAddress *to, guint len,
const gchar *buf);
/* As above, @n_messages may be zero. Iff so, @messages may be %NULL. */
gint (*send_messages) (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages);
gboolean (*is_reliable) (NiceSocket *sock);
void (*close) (NiceSocket *sock);
void *priv;
......@@ -75,9 +76,12 @@ gint
nice_socket_recv_messages (NiceSocket *sock,
NiceInputMessage *recv_messages, guint n_recv_messages);
gboolean
nice_socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf);
gint
nice_socket_send_messages (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages);
gssize
nice_socket_send (NiceSocket *sock, const NiceAddress *addr, gsize len,
const gchar *buf);
gboolean
nice_socket_is_reliable (NiceSocket *sock);
......
......@@ -69,8 +69,8 @@ typedef struct {
struct to_be_sent {
guint length;
gchar *buf;
guint8 *buf; /* owned */
gsize length;
NiceAddress to;
};
......@@ -78,12 +78,12 @@ struct to_be_sent {
static void socket_close (NiceSocket *sock);
static gint socket_recv_messages (NiceSocket *sock,
NiceInputMessage *recv_messages, guint n_recv_messages);
static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf);
static gint socket_send_messages (NiceSocket *sock,
const NiceOutputMessage *messages, guint n_messages);
static gboolean socket_is_reliable (NiceSocket *sock);
static void add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
const gchar *buf, guint len);
static void add_to_be_sent (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages);
static void free_to_be_sent (struct to_be_sent *tbs);
......@@ -105,7 +105,7 @@ nice_socks5_socket_new (NiceSocket *base_socket,
sock->fileno = priv->base_socket->fileno;
sock->addr = priv->base_socket->addr;
sock->send = socket_send;
sock->send_messages = socket_send_messages;
sock->recv_messages = socket_recv_messages;
sock->is_reliable = socket_is_reliable;
sock->close = socket_close;
......@@ -330,7 +330,7 @@ socket_recv_messages (NiceSocket *sock,
}
while ((tbs = g_queue_pop_head (&priv->send_queue))) {
nice_socket_send (priv->base_socket, &tbs->to,
tbs->length, tbs->buf);
tbs->length, (const gchar *) tbs->buf);
g_free (tbs->buf);
g_slice_free (struct to_be_sent, tbs);
}
......@@ -418,21 +418,22 @@ socket_recv_messages (NiceSocket *sock,
return -1;
}
static gboolean
socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf)
static gint
socket_send_messages (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages)
{
Socks5Priv *priv = sock->priv;
if (priv->state == SOCKS_STATE_CONNECTED) {
if (priv->base_socket)
return nice_socket_send (priv->base_socket, to, len, buf);
else
/* Fast path: pass through to the base socket once connected. */
if (priv->base_socket == NULL)
return FALSE;
return nice_socket_send_messages (priv->base_socket, messages, n_messages);
} else if (priv->state == SOCKS_STATE_ERROR) {
return FALSE;
} else {
add_to_be_sent (sock, to, buf, len);
add_to_be_sent (sock, messages, n_messages);
}
return TRUE;
}
......@@ -446,22 +447,41 @@ socket_is_reliable (NiceSocket *sock)
static void
add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
const gchar *buf, guint len)
add_to_be_sent (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages)
{
Socks5Priv *priv = sock->priv;
struct to_be_sent *tbs = NULL;
if (len <= 0)
return;
guint i;
tbs = g_slice_new0 (struct to_be_sent);
tbs->buf = g_memdup (buf, len);
tbs->length = len;
if (to)
tbs->to = *to;
g_queue_push_tail (&priv->send_queue, tbs);
for (i = 0; i < n_messages; i++) {
struct to_be_sent *tbs;
const NiceOutputMessage *message = &messages[i];
guint j;
gsize offset = 0;
tbs = g_slice_new0 (struct to_be_sent);
/* Compact the buffer. */
tbs->buf = g_malloc (message->length);
tbs->length = message->length;
if (message->to != NULL)
tbs->to = *message->to;
g_queue_push_tail (&priv->send_queue, tbs);
for (j = 0;
(message->n_buffers >= 0 && j < (guint) message->n_buffers) ||
(message->n_buffers < 0 && message->buffers[j].buffer != NULL);
j++) {
const GOutputVector *buffer = &message->buffers[j];
gsize len;
len = MIN (message->length - offset, buffer->size);
memcpy (tbs->buf + offset, buffer->buffer, len);
offset += len;
}
g_assert_cmpuint (offset, ==, message->length);
}
}
......
......@@ -62,8 +62,8 @@ typedef struct {
} TcpPriv;
struct to_be_sent {
guint length;
gchar *buf;
guint8 *buf;
gsize length;
gboolean can_drop;
};
......@@ -72,13 +72,13 @@ struct to_be_sent {
static void socket_close (NiceSocket *sock);
static gint socket_recv_messages (NiceSocket *sock,
NiceInputMessage *recv_messages, guint n_recv_messages);
static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf);
static gint socket_send_messages (NiceSocket *sock,
const NiceOutputMessage *messages, guint n_messages);
static gboolean socket_is_reliable (NiceSocket *sock);
static void add_to_be_sent (NiceSocket *sock, const gchar *buf, guint len,
gboolean head);
static void add_to_be_sent (NiceSocket *sock, const NiceOutputMessage *message,
gsize message_offset, gboolean head);
static void free_to_be_sent (struct to_be_sent *tbs);
static gboolean socket_send_more (GSocket *gsocket, GIOCondition condition,
gpointer data);
......@@ -169,7 +169,7 @@ nice_tcp_bsd_socket_new (GMainContext *ctx, NiceAddress *addr)
priv->error = FALSE;
sock->fileno = gsock;
sock->send = socket_send;
sock->send_messages = socket_send_messages;
sock->recv_messages = socket_recv_messages;
sock->is_reliable = socket_is_reliable;
sock->close = socket_close;
......@@ -252,59 +252,88 @@ socket_recv_messages (NiceSocket *sock,
return i;
}
/* Data sent to this function must be a single entity because buffers can be
* dropped if the bandwidth isn't fast enough. So do not send a message in
* multiple chunks. */
static gboolean
socket_send (NiceSocket *sock, const NiceAddress *to,
guint len, const gchar *buf)
static gssize
socket_send_message (NiceSocket *sock, const NiceOutputMessage *message)
{
TcpPriv *priv = sock->priv;
int ret;
gssize ret;
GError *gerr = NULL;
/* Don't try to access the socket if it had an error, otherwise we risk a
crash with SIGPIPE (Broken pipe) */
* crash with SIGPIPE (Broken pipe) */
if (priv->error)
return -1;
/* First try to send the data, don't send it later if it can be sent now
this way we avoid allocating memory on every send */
* this way we avoid allocating memory on every send */
if (g_queue_is_empty (&priv->send_queue)) {
ret = g_socket_send (sock->fileno, buf, len, NULL, &gerr);
ret = g_socket_send_message (sock->fileno, NULL, message->buffers,
message->n_buffers, NULL, 0, G_SOCKET_MSG_NONE, NULL, &gerr);
if (ret < 0) {
if(g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)
|| g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_FAILED)) {
add_to_be_sent (sock, buf, len, FALSE);
g_error_free (gerr);
return TRUE;
} else {
g_error_free (gerr);
return FALSE;
if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_FAILED)) {
/* Queue the message and send it later. */
add_to_be_sent (sock, message, 0, FALSE);
ret = message->length;
}
} else if ((guint)ret < len) {
add_to_be_sent (sock, buf + ret, len - ret, TRUE);
return TRUE;
g_error_free (gerr);
} else if ((gsize) ret < message->length) {
/* Partial send. */
add_to_be_sent (sock, message, ret, TRUE);
ret = message->length;
}
} else {
if (g_queue_get_length(&priv->send_queue) >= MAX_QUEUE_LENGTH) {
int peek_idx = 0;
/* If the queue is too long, drop whatever packets we can. */
if (g_queue_get_length (&priv->send_queue) >= MAX_QUEUE_LENGTH) {
guint peek_idx = 0;
struct to_be_sent *tbs = NULL;
while ((tbs = g_queue_peek_nth (&priv->send_queue, peek_idx)) != NULL) {
if (tbs->can_drop) {
tbs = g_queue_pop_nth (&priv->send_queue, peek_idx);
g_free (tbs->buf);
g_slice_free (struct to_be_sent, tbs);
free_to_be_sent (tbs);
break;
} else {
peek_idx++;
}
}
}
add_to_be_sent (sock, buf, len, FALSE);
/* Queue the message and send it later. */
add_to_be_sent (sock, message, 0, FALSE);
ret = message->length;
}
return TRUE;
return ret;
}
/* Data sent to this function must be a single entity because buffers can be
* dropped if the bandwidth isn't fast enough. So do not send a message in
* multiple chunks. */
static gint
socket_send_messages (NiceSocket *sock, const NiceOutputMessage *messages,
guint n_messages)
{
guint i;
for (i = 0; i < n_messages; i++) {
const NiceOutputMessage *message = &messages[i];
gssize len;
len = socket_send_message (sock, message);
if (len < 0) {
/* Error. */
return len;
} else if (len == 0) {
/* EWOULDBLOCK. */
break;
}
}
return i;
}
static gboolean
......@@ -348,28 +377,37 @@ socket_send_more (
/* connection hangs up */
ret = -1;
} else {
ret = g_socket_send (sock->fileno, tbs->buf, tbs->length, NULL, &gerr);
GOutputVector local_bufs = { tbs->buf, tbs->length };
ret = g_socket_send_message (sock->fileno, NULL, &local_bufs, 1, NULL, 0,