static gssize socket_send_message (NiceSocket *sock, const NiceOutputMessage *message, gboolean reliable) { TcpPriv *priv = sock->priv; gssize ret; GError *gerr = NULL; gsize message_len; /* Socket has been closed: */ if (sock->priv == NULL) return -1; /* Don't try to access the socket if it had an error, otherwise we risk a * crash with SIGPIPE (Broken pipe) */ if (priv->error) return -1; message_len = output_message_get_size (message); /* 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 */ if (g_queue_is_empty (&priv->send_queue)) { 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)) { /* Queue the message and send it later. */ nice_socket_queue_send_with_callback (&priv->send_queue, message, 0, message_len, FALSE, sock->fileno, &priv->io_source, priv->context, (GSourceFunc) socket_send_more, sock); ret = message_len; } g_error_free (gerr); } else if ((gsize) ret < message_len) { /* Partial send. */ nice_socket_queue_send_with_callback (&priv->send_queue, message, ret, message_len, TRUE, sock->fileno, &priv->io_source, priv->context, (GSourceFunc) socket_send_more, sock); ret = message_len; } } else { /* Only queue if we're sending reliably */ if (reliable) { /* Queue the message and send it later. */ nice_socket_queue_send_with_callback (&priv->send_queue, message, 0, message_len, FALSE, sock->fileno, &priv->io_source, priv->context, (GSourceFunc) socket_send_more, sock); ret = message_len; } else { /* non reliable send, so we shouldn't queue the message */ ret = 0; } } return ret; }
static gboolean socket_send_more ( GSocket *gsocket, GIOCondition condition, gpointer data) { NiceSocket *sock = (NiceSocket *) data; TcpPriv *priv = sock->priv; struct to_be_sent *tbs = NULL; GError *gerr = NULL; agent_lock (); if (g_source_is_destroyed (g_main_current_source ())) { nice_debug ("Source was destroyed. " "Avoided race condition in tcp-bsd.c:socket_send_more"); agent_unlock (); return FALSE; } while ((tbs = g_queue_pop_head (&priv->send_queue)) != NULL) { int ret; if(condition & G_IO_HUP) { /* connection hangs up */ ret = -1; } else { GOutputVector local_bufs = { tbs->buf, tbs->length }; ret = g_socket_send_message (sock->fileno, NULL, &local_bufs, 1, NULL, 0, G_SOCKET_MSG_NONE, NULL, &gerr); } if (ret < 0) { if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { GOutputVector local_buf = { tbs->buf, tbs->length }; NiceOutputMessage local_message = {&local_buf, 1}; add_to_be_sent (sock, &local_message, 0, local_buf.size, TRUE); free_to_be_sent (tbs); g_error_free (gerr); break; } g_clear_error (&gerr); } else if (ret < (int) tbs->length) { GOutputVector local_buf = { tbs->buf + ret, tbs->length - ret }; NiceOutputMessage local_message = {&local_buf, 1}; add_to_be_sent (sock, &local_message, 0, local_buf.size, TRUE); free_to_be_sent (tbs); break; } free_to_be_sent (tbs); } if (g_queue_is_empty (&priv->send_queue)) { g_source_destroy (priv->io_source); g_source_unref (priv->io_source); priv->io_source = NULL; agent_unlock (); return FALSE; } agent_unlock (); return TRUE; }
static gssize socket_send_message (NiceSocket *sock, const NiceOutputMessage *message) { TcpPriv *priv = sock->priv; gssize ret; GError *gerr = NULL; gsize message_len; /* Don't try to access the socket if it had an error, otherwise we risk a * crash with SIGPIPE (Broken pipe) */ if (priv->error) return -1; message_len = output_message_get_size (message); /* 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 */ if (g_queue_is_empty (&priv->send_queue)) { 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)) { /* Queue the message and send it later. */ add_to_be_sent (sock, message, 0, message_len, FALSE); ret = message_len; } g_error_free (gerr); } else if ((gsize) ret < message_len) { /* Partial send. */ add_to_be_sent (sock, message, ret, message_len, TRUE); ret = message_len; } } else { /* FIXME: This dropping will break http/socks5/etc * We probably need a way to the upper layer to control reliability */ /* 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); free_to_be_sent (tbs); break; } else { peek_idx++; } } } /* Queue the message and send it later. */ add_to_be_sent (sock, message, 0, message_len, FALSE); ret = message_len; } return ret; }
static GstFlowReturn gst_multiudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) { GstMultiUDPSink *sink; GList *clients; GOutputVector *vec; GstMapInfo *map; guint n_mem, i; gsize size; GstMemory *mem; gint num, no_clients; GError *err = NULL; sink = GST_MULTIUDPSINK (bsink); n_mem = gst_buffer_n_memory (buffer); if (n_mem == 0) goto no_data; /* allocated on the stack, the max number of memory blocks is limited so this * should not cause stack overflows */ vec = sink->vec; map = sink->map; size = 0; for (i = 0; i < n_mem; i++) { mem = gst_buffer_get_memory (buffer, i); gst_memory_map (mem, &map[i], GST_MAP_READ); vec[i].buffer = map[i].data; vec[i].size = map[i].size; size += map[i].size; } sink->bytes_to_serve += size; /* grab lock while iterating and sending to clients, this should be * fast as UDP never blocks */ g_mutex_lock (&sink->client_lock); GST_LOG_OBJECT (bsink, "about to send %" G_GSIZE_FORMAT " bytes in %u blocks", size, n_mem); no_clients = 0; num = 0; for (clients = sink->clients; clients; clients = g_list_next (clients)) { GstUDPClient *client; GSocket *socket; GSocketFamily family; gint count; client = (GstUDPClient *) clients->data; no_clients++; GST_LOG_OBJECT (sink, "sending %" G_GSIZE_FORMAT " bytes to client %p", size, client); family = g_socket_address_get_family (G_SOCKET_ADDRESS (client->addr)); /* Select socket to send from for this address */ if (family == G_SOCKET_FAMILY_IPV6 || !sink->used_socket) socket = sink->used_socket_v6; else socket = sink->used_socket; count = sink->send_duplicates ? client->refcount : 1; while (count--) { gssize ret; ret = g_socket_send_message (socket, client->addr, vec, n_mem, NULL, 0, 0, sink->cancellable, &err); if (G_UNLIKELY (ret < 0)) { if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) goto flushing; /* we continue after posting a warning, next packets might be ok * again */ if (size > UDP_MAX_SIZE) { GST_ELEMENT_WARNING (sink, RESOURCE, WRITE, ("Attempting to send a UDP packet larger than maximum size " "(%" G_GSIZE_FORMAT " > %d)", size, UDP_MAX_SIZE), ("Reason: %s", err ? err->message : "unknown reason")); } else { GST_ELEMENT_WARNING (sink, RESOURCE, WRITE, ("Error sending UDP packet"), ("Reason: %s", err ? err->message : "unknown reason")); } g_clear_error (&err); } else { num++; client->bytes_sent += ret; client->packets_sent++; sink->bytes_served += ret; } } } g_mutex_unlock (&sink->client_lock); /* unmap all memory again */ for (i = 0; i < n_mem; i++) { gst_memory_unmap (map[i].memory, &map[i]); gst_memory_unref (map[i].memory); } GST_LOG_OBJECT (sink, "sent %" G_GSIZE_FORMAT " bytes to %d (of %d) clients", size, num, no_clients); return GST_FLOW_OK; no_data: { return GST_FLOW_OK; } flushing: { GST_DEBUG ("we are flushing"); g_mutex_unlock (&sink->client_lock); g_clear_error (&err); /* unmap all memory */ for (i = 0; i < n_mem; i++) { gst_memory_unmap (map[i].memory, &map[i]); gst_memory_unref (map[i].memory); } return GST_FLOW_FLUSHING; } }
static GstFlowReturn gst_multiudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) { GstMultiUDPSink *sink; GList *clients; GOutputVector *vec; GstMapInfo *map; guint n_mem, i; gsize size; GstMemory *mem; gint num, no_clients; GError *err = NULL; sink = GST_MULTIUDPSINK (bsink); n_mem = gst_buffer_n_memory (buffer); if (n_mem == 0) goto no_data; vec = g_new (GOutputVector, n_mem); map = g_new (GstMapInfo, n_mem); size = 0; for (i = 0; i < n_mem; i++) { mem = gst_buffer_get_memory (buffer, i); gst_memory_map (mem, &map[i], GST_MAP_READ); if (map[i].size > UDP_MAX_SIZE) { GST_WARNING ("Attempting to send a UDP packet larger than maximum " "size (%" G_GSIZE_FORMAT " > %d)", map[i].size, UDP_MAX_SIZE); } vec[i].buffer = map[i].data; vec[i].size = map[i].size; size += map[i].size; } sink->bytes_to_serve += size; /* grab lock while iterating and sending to clients, this should be * fast as UDP never blocks */ g_mutex_lock (&sink->client_lock); GST_LOG_OBJECT (bsink, "about to send %" G_GSIZE_FORMAT " bytes", size); no_clients = 0; num = 0; for (clients = sink->clients; clients; clients = g_list_next (clients)) { GstUDPClient *client; gint count; client = (GstUDPClient *) clients->data; no_clients++; GST_LOG_OBJECT (sink, "sending %" G_GSIZE_FORMAT " bytes to client %p", size, client); count = sink->send_duplicates ? client->refcount : 1; while (count--) { gssize ret; ret = g_socket_send_message (sink->used_socket, client->addr, vec, n_mem, NULL, 0, 0, sink->cancellable, &err); if (ret < 0) goto send_error; num++; client->bytes_sent += ret; client->packets_sent++; sink->bytes_served += ret; } } g_mutex_unlock (&sink->client_lock); /* unmap all memory again */ for (i = 0; i < n_mem; i++) { gst_memory_unmap (map[i].memory, &map[i]); gst_memory_unref (map[i].memory); } g_free (vec); g_free (map); GST_LOG_OBJECT (sink, "sent %" G_GSIZE_FORMAT " bytes to %d (of %d) clients", size, num, no_clients); return GST_FLOW_OK; no_data: { return GST_FLOW_OK; } send_error: { g_mutex_unlock (&sink->client_lock); GST_DEBUG ("got send error %s", err->message); g_clear_error (&err); return GST_FLOW_ERROR; } }