Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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;
}
Beispiel #4
0
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;
  }
}
Beispiel #5
0
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;
  }
}