void zmq::pgm_socket_t::open_transport (void) { zmq_log (1, "Opening PGM: network %s, port %i, udp encaps. %s, %s(%i)\n", network, port_number, udp_encapsulation ? "yes" : "no", __FILE__, __LINE__); // Can not open transport before destroying old one. assert (g_transport == NULL); // Set actual_tsi and prev_tsi to zeros. memset (&tsi, '\0', sizeof (pgm_tsi_t)); memset (&retired_tsi, '\0', sizeof (pgm_tsi_t)); // Zero counter used in msgrecv. nbytes_rec = 0; nbytes_processed = 0; pgm_msgv_processed = 0; // Init PGM transport. // Note that if you want to use gettimeofday and sleep for openPGM timing, // set environment variables PGM_TIMER to "GTOD" // and PGM_SLEEP to "USLEEP". pgm_init (); pgm_gsi_t gsi; int rc = pgm_create_md5_gsi (&gsi); assert (rc == 0); struct group_source_req recv_gsr, send_gsr; gsize recv_len = 1; rc = pgm_if_parse_transport (network, AF_INET, &recv_gsr, &recv_len, &send_gsr); assert (rc == 0); assert (recv_len == 1); // If we are using UDP encapsulation update send_gsr & recv_gsr // structures. Note that send_gsr & recv_gsr has to be updated after // pgm_if_parse_transport call. if (udp_encapsulation) { // Use the same port for UDP encapsulation. ((struct sockaddr_in*)&send_gsr.gsr_group)->sin_port = g_htons (port_number); ((struct sockaddr_in*)&recv_gsr.gsr_group)->sin_port = g_htons (port_number); } rc = pgm_transport_create (&g_transport, &gsi, 0, port_number, &recv_gsr, 1, &send_gsr); errno_assert (rc == 0); // Common parameters for receiver and sender. // Set maximum transport protocol data unit size (TPDU). rc = pgm_transport_set_max_tpdu (g_transport, pgm_max_tpdu); assert (rc == 0); // Set maximum number of network hops to cross. rc = pgm_transport_set_hops (g_transport, 16); assert (rc == 0); // Receiver transport. if (receiver) { // When using UDP-encapsulation enables socket address sharing via // SO_REUSEADDR to allow multiple applications to bind to the same // UDP port. if (udp_encapsulation) { rc = pgm_transport_set_multicast_loop (g_transport, TRUE); assert (rc == 0); } // Set transport->may_close_on_failure to true, // after data los recvmsgv returns -1 errno set to ECONNRESET. rc = pgm_transport_set_close_on_failure (g_transport, TRUE); assert (rc == 0); // Set transport->can_send_data = FALSE. // Note that NAKs are still generated by the transport. rc = pgm_transport_set_recv_only (g_transport, false); assert (rc == 0); // Set NAK transmit back-off interval [us]. rc = pgm_transport_set_nak_bo_ivl (g_transport, 50*1000); assert (rc ==0); // Set timeout before repeating NAK [us]. rc = pgm_transport_set_nak_rpt_ivl (g_transport, 200*1000); assert (rc == 0); // Set timeout for receiving RDATA. rc = pgm_transport_set_nak_rdata_ivl (g_transport, 200*1000); assert (rc == 0); // Set retries for NAK without NCF/DATA (NAK_DATA_RETRIES). rc = pgm_transport_set_nak_data_retries (g_transport, 5); assert (rc == 0); // Set retries for NCF after NAK (NAK_NCF_RETRIES). rc = pgm_transport_set_nak_ncf_retries (g_transport, 2); assert (rc == 0); // Set timeout for removing a dead peer [us]. rc = pgm_transport_set_peer_expiry (g_transport, 5*8192*1000); assert (rc == 0); // Set expiration time of SPM Requests [us]. rc = pgm_transport_set_spmr_expiry (g_transport, 25*1000); assert (rc == 0); if (pgm_window_size > 0) { // Set receive window size in sequence numbers. rc = pgm_transport_set_rxw_sqns (g_transport, pgm_window_size); assert (rc == 0); } else { // Set the size of the receive window size by max // data rate in bytes per second. assert (pgm_max_rte > 0); rc = pgm_transport_set_rxw_max_rte (g_transport, pgm_max_rte); assert (rc ==0); // Set receive window size in seconds. assert (pgm_secs > 0); rc = pgm_transport_set_rxw_secs (g_transport, pgm_secs); assert (rc == 0); } // Sender transport. } else { // Set transport->can_recv = FALSE, waiting_pipe wont not be read. rc = pgm_transport_set_send_only (g_transport, TRUE); assert (rc == 0); int to_preallocate = 0; if (pgm_window_size > 0) { // Set send window size in sequence numbers. rc = pgm_transport_set_txw_sqns (g_transport, pgm_window_size); assert (rc == 0); // Preallocate full window. to_preallocate = pgm_window_size; } else { // Set the size of the send window size by // data rate in bytes per second. assert (pgm_max_rte > 0); rc = pgm_transport_set_txw_max_rte (g_transport, pgm_max_rte); assert (rc ==0); // Set send window size in seconds. assert (pgm_secs > 0); rc = pgm_transport_set_txw_secs (g_transport, pgm_secs); assert (rc == 0); // Preallocate full transmit window. For simplification always // worst case is used (40 bytes ipv6 header and 20 bytes UDP // encapsulation). to_preallocate = pgm_secs * pgm_max_rte / (pgm_max_tpdu - 40 - 20); } rc = pgm_transport_set_txw_preallocate (g_transport, to_preallocate); assert (rc == 0); zmq_log (1, "Preallocated %i slices in TX window. %s(%i)\n", to_preallocate, __FILE__, __LINE__); // Set interval of background SPM packets [us]. rc = pgm_transport_set_ambient_spm (g_transport, 8192 * 1000); assert (rc == 0); // Set intervals of data flushing SPM packets [us]. guint spm_heartbeat[] = {4 * 1000, 4 * 1000, 8 * 1000, 16 * 1000, 32 * 1000, 64 * 1000, 128 * 1000, 256 * 1000, 512 * 1000, 1024 * 1000, 2048 * 1000, 4096 * 1000, 8192 * 1000}; rc = pgm_transport_set_heartbeat_spm (g_transport, spm_heartbeat, G_N_ELEMENTS(spm_heartbeat)); assert (rc == 0); } // Bind a transport to the specified network devices. rc = pgm_transport_bind (g_transport); assert (rc == 0); }
int zmq::pgm_socket_t::init (bool udp_encapsulation_, const char *network_) { // Can not open transport before destroying old one. zmq_assert (transport == NULL); // Parse port number. const char *port_delim = strchr (network_, ':'); if (!port_delim) { errno = EINVAL; return -1; } uint16_t port_number = atoi (port_delim + 1); char network [256]; if (port_delim - network_ >= (int) sizeof (network) - 1) { errno = EINVAL; return -1; } memset (network, '\0', sizeof (network)); memcpy (network, network_, port_delim - network_); // Zero counter used in msgrecv. nbytes_rec = 0; nbytes_processed = 0; pgm_msgv_processed = 0; GError *pgm_error = NULL; // Init PGM transport. // Ensure threading enabled, ensure timer enabled and find PGM protocol id. // // Note that if you want to use gettimeofday and sleep for openPGM timing, // set environment variables PGM_TIMER to "GTOD" // and PGM_SLEEP to "USLEEP". int rc = pgm_init (); if (rc != 0) { errno = EINVAL; return -1; } // PGM transport GSI. pgm_gsi_t gsi; std::string gsi_base; if (options.identity.size () > 0) { // Create gsi from identity string. gsi_base = options.identity; } else { // Generate random gsi. gsi_base = uuid_t ().to_string (); } rc = pgm_gsi_create_from_string (&gsi, gsi_base.c_str (), -1); if (rc != TRUE) { errno = EINVAL; return -1; } struct pgm_transport_info_t *res = NULL; if (!pgm_if_get_transport_info (network, NULL, &res, &pgm_error)) { if (pgm_error->domain == PGM_IF_ERROR && ( pgm_error->code == PGM_IF_ERROR_INVAL || pgm_error->code == PGM_IF_ERROR_XDEV || pgm_error->code == PGM_IF_ERROR_NODEV || pgm_error->code == PGM_IF_ERROR_NOTUNIQ || pgm_error->code == PGM_IF_ERROR_ADDRFAMILY || pgm_error->code == PGM_IF_ERROR_FAMILY || pgm_error->code == PGM_IF_ERROR_NODATA || pgm_error->code == PGM_IF_ERROR_NONAME || pgm_error->code == PGM_IF_ERROR_SERVICE)) { errno = EINVAL; g_error_free (pgm_error); return -1; } zmq_assert (false); } res->ti_gsi = gsi; res->ti_dport = port_number; // If we are using UDP encapsulation update gsr or res. if (udp_encapsulation_) { res->ti_udp_encap_ucast_port = port_number; res->ti_udp_encap_mcast_port = port_number; } if (!pgm_transport_create (&transport, res, &pgm_error)) { if (pgm_error->domain == PGM_TRANSPORT_ERROR && ( pgm_error->code == PGM_TRANSPORT_ERROR_INVAL || pgm_error->code == PGM_TRANSPORT_ERROR_PERM || pgm_error->code == PGM_TRANSPORT_ERROR_NODEV)) { pgm_if_free_transport_info (res); g_error_free (pgm_error); errno = EINVAL; return -1; } zmq_assert (false); } pgm_if_free_transport_info (res); // Common parameters for receiver and sender. // Set maximum transport protocol data unit size (TPDU). rc = pgm_transport_set_max_tpdu (transport, pgm_max_tpdu); if (rc != TRUE) { errno = EINVAL; return -1; } // Set maximum number of network hops to cross. rc = pgm_transport_set_hops (transport, 16); if (rc != TRUE) { errno = EINVAL; return -1; } // Set nonblocking send/recv sockets. if (!pgm_transport_set_nonblocking (transport, true)) { errno = EINVAL; return -1; } if (receiver) { // Receiver transport. // Note that NAKs are still generated by the transport. rc = pgm_transport_set_recv_only (transport, true, false); zmq_assert (rc == TRUE); if (options.rcvbuf) { rc = pgm_transport_set_rcvbuf (transport, (int) options.rcvbuf); if (rc != TRUE) return -1; } // Set NAK transmit back-off interval [us]. rc = pgm_transport_set_nak_bo_ivl (transport, 50 * 1000); zmq_assert (rc == TRUE); // Set timeout before repeating NAK [us]. rc = pgm_transport_set_nak_rpt_ivl (transport, 200 * 1000); zmq_assert (rc == TRUE); // Set timeout for receiving RDATA. rc = pgm_transport_set_nak_rdata_ivl (transport, 200 * 1000); zmq_assert (rc == TRUE); // Set retries for NAK without NCF/DATA (NAK_DATA_RETRIES). rc = pgm_transport_set_nak_data_retries (transport, 5); zmq_assert (rc == TRUE); // Set retries for NCF after NAK (NAK_NCF_RETRIES). rc = pgm_transport_set_nak_ncf_retries (transport, 2); zmq_assert (rc == TRUE); // Set timeout for removing a dead peer [us]. rc = pgm_transport_set_peer_expiry (transport, 5 * 8192 * 1000); zmq_assert (rc == TRUE); // Set expiration time of SPM Requests [us]. rc = pgm_transport_set_spmr_expiry (transport, 25 * 1000); zmq_assert (rc == TRUE); // Set the size of the receive window. // Data rate is in [B/s]. options.rate is in [kb/s]. if (options.rate <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_rxw_max_rte (transport, options.rate * 1000 / 8); if (rc != TRUE) { errno = EINVAL; return -1; } // Recovery interval [s]. if (options.recovery_ivl <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_rxw_secs (transport, options.recovery_ivl); if (rc != TRUE) { errno = EINVAL; return -1; } } else { // Sender transport. // Waiting pipe won't be read. rc = pgm_transport_set_send_only (transport, TRUE); zmq_assert (rc == TRUE); if (options.sndbuf) { rc = pgm_transport_set_sndbuf (transport, (int) options.sndbuf); if (rc != TRUE) return -1; } // Set the size of the send window. // Data rate is in [B/s] options.rate is in [kb/s]. if (options.rate <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_txw_max_rte (transport, options.rate * 1000 / 8); if (rc != TRUE) { errno = EINVAL; return -1; } // Recovery interval [s]. if (options.recovery_ivl <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_txw_secs (transport, options.recovery_ivl); if (rc != TRUE) { errno = EINVAL; return -1; } // Set interval of background SPM packets [us]. rc = pgm_transport_set_ambient_spm (transport, 8192 * 1000); zmq_assert (rc == TRUE); // Set intervals of data flushing SPM packets [us]. guint spm_heartbeat[] = {4 * 1000, 4 * 1000, 8 * 1000, 16 * 1000, 32 * 1000, 64 * 1000, 128 * 1000, 256 * 1000, 512 * 1000, 1024 * 1000, 2048 * 1000, 4096 * 1000, 8192 * 1000}; rc = pgm_transport_set_heartbeat_spm (transport, spm_heartbeat, G_N_ELEMENTS(spm_heartbeat)); zmq_assert (rc == TRUE); } // Enable multicast loopback. if (options.use_multicast_loop) { rc = pgm_transport_set_multicast_loop (transport, true); zmq_assert (rc == TRUE); } // Bind a transport to the specified network devices. if (!pgm_transport_bind (transport, &pgm_error)) { if (pgm_error->domain == PGM_IF_ERROR && ( pgm_error->code == PGM_IF_ERROR_INVAL || pgm_error->code == PGM_IF_ERROR_XDEV || pgm_error->code == PGM_IF_ERROR_NODEV || pgm_error->code == PGM_IF_ERROR_NOTUNIQ || pgm_error->code == PGM_IF_ERROR_ADDRFAMILY || pgm_error->code == PGM_IF_ERROR_FAMILY || pgm_error->code == PGM_IF_ERROR_NODATA || pgm_error->code == PGM_IF_ERROR_NONAME || pgm_error->code == PGM_IF_ERROR_SERVICE)) { g_error_free (pgm_error); errno = EINVAL; return -1; } zmq_assert (false); } // For receiver transport preallocate pgm_msgv array. // TODO: ? if (receiver) { zmq_assert (in_batch_size > 0); size_t max_tsdu_size = get_max_tsdu_size (); pgm_msgv_len = (int) in_batch_size / max_tsdu_size; if ((int) in_batch_size % max_tsdu_size) pgm_msgv_len++; zmq_assert (pgm_msgv_len); pgm_msgv = (pgm_msgv_t*) malloc (sizeof (pgm_msgv_t) * pgm_msgv_len); } return 0; }
static gboolean on_startup (void) { struct pgm_transport_info_t* res = NULL; pgm_error_t* pgm_err = NULL; g_message ("startup."); g_message ("create transport."); /* parse network parameter into transport address structure */ char network[1024]; sprintf (network, "%s", g_network); if (!pgm_if_get_transport_info (network, NULL, &res, &pgm_err)) { g_error ("parsing network parameter: %s", pgm_err->message); pgm_error_free (pgm_err); return FALSE; } /* create global session identifier */ if (!pgm_gsi_create_from_hostname (&res->ti_gsi, &pgm_err)) { g_error ("creating GSI: %s", pgm_err->message); pgm_error_free (pgm_err); pgm_if_free_transport_info (res); return FALSE; } if (g_udp_encap_port) { res->ti_udp_encap_ucast_port = g_udp_encap_port; res->ti_udp_encap_mcast_port = g_udp_encap_port; } if (g_port) res->ti_dport = g_port; if (!pgm_transport_create (&g_transport, res, &pgm_err)) { g_error ("creating transport: %s", pgm_err->message); pgm_error_free (pgm_err); pgm_if_free_transport_info (res); return FALSE; } pgm_if_free_transport_info (res); /* set PGM parameters */ pgm_transport_set_nonblocking (g_transport, TRUE); pgm_transport_set_recv_only (g_transport, TRUE, FALSE); pgm_transport_set_max_tpdu (g_transport, g_max_tpdu); pgm_transport_set_rxw_sqns (g_transport, g_sqns); pgm_transport_set_hops (g_transport, 16); pgm_transport_set_peer_expiry (g_transport, pgm_secs(300)); pgm_transport_set_spmr_expiry (g_transport, pgm_msecs(250)); pgm_transport_set_nak_bo_ivl (g_transport, pgm_msecs(50)); pgm_transport_set_nak_rpt_ivl (g_transport, pgm_secs(2)); pgm_transport_set_nak_rdata_ivl (g_transport, pgm_secs(2)); pgm_transport_set_nak_data_retries (g_transport, 50); pgm_transport_set_nak_ncf_retries (g_transport, 50); /* assign transport to specified address */ if (!pgm_transport_bind (g_transport, &pgm_err)) { g_error ("binding transport: %s", pgm_err->message); pgm_error_free (pgm_err); pgm_transport_destroy (g_transport, FALSE); g_transport = NULL; return FALSE; } g_message ("startup complete."); return TRUE; }
int zmq::pgm_socket_t::open_transport () { // Can not open transport before destroying old one. zmq_assert (transport == NULL); // Zero counter used in msgrecv. nbytes_rec = 0; nbytes_processed = 0; pgm_msgv_processed = 0; // TODO: Converting bool to int? Not nice. int pgm_ok = true; GError *pgm_error = NULL; // Init PGM transport. // Ensure threading enabled, ensure timer enabled and find PGM protocol id. // // Note that if you want to use gettimeofday and sleep for openPGM timing, // set environment variables PGM_TIMER to "GTOD" // and PGM_SLEEP to "USLEEP". int rc = pgm_init (); if (rc != 0) { errno = EINVAL; return -1; } // PGM transport GSI. pgm_gsi_t gsi; std::string gsi_base; if (options.identity.size () > 0) { // Create gsi from identity string. gsi_base = options.identity; } else { // Generate random gsi. gsi_base = uuid_t ().to_string (); } rc = pgm_gsi_create_from_string (&gsi, gsi_base.c_str (), -1); if (rc != pgm_ok) { errno = EINVAL; return -1; } //zmq_log (1, "Transport GSI: %s, %s(%i)\n", pgm_print_gsi (&gsi), // __FILE__, __LINE__); struct pgm_transport_info_t *res = NULL; if (!pgm_if_get_transport_info (network, NULL, &res, &pgm_error)) { errno = EINVAL; return -1; } res->ti_gsi = gsi; res->ti_dport = port_number; // If we are using UDP encapsulation update gsr or res. if (udp_encapsulation) { res->ti_udp_encap_ucast_port = port_number; res->ti_udp_encap_mcast_port = port_number; } if (!pgm_transport_create (&transport, res, &pgm_error)) { pgm_if_free_transport_info (res); // TODO: tranlate errors from glib into errnos. errno = EINVAL; return -1; } pgm_if_free_transport_info (res); // Common parameters for receiver and sender. // Set maximum transport protocol data unit size (TPDU). rc = pgm_transport_set_max_tpdu (transport, pgm_max_tpdu); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Set maximum number of network hops to cross. rc = pgm_transport_set_hops (transport, 16); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Set nonblocking send/recv sockets. if (!pgm_transport_set_nonblocking (transport, true)) { errno = EINVAL; return -1; } if (receiver) { // Receiver transport. // Set transport->can_send_data = FALSE. // Note that NAKs are still generated by the transport. rc = pgm_transport_set_recv_only (transport, true, false); zmq_assert (rc == pgm_ok); if (options.rcvbuf) { rc = pgm_transport_set_rcvbuf (transport, (int) options.rcvbuf); if (rc != pgm_ok) return -1; } // Set NAK transmit back-off interval [us]. rc = pgm_transport_set_nak_bo_ivl (transport, 50 * 1000); zmq_assert (rc == pgm_ok); // Set timeout before repeating NAK [us]. rc = pgm_transport_set_nak_rpt_ivl (transport, 200 * 1000); zmq_assert (rc == pgm_ok); // Set timeout for receiving RDATA. rc = pgm_transport_set_nak_rdata_ivl (transport, 200 * 1000); zmq_assert (rc == pgm_ok); // Set retries for NAK without NCF/DATA (NAK_DATA_RETRIES). rc = pgm_transport_set_nak_data_retries (transport, 5); zmq_assert (rc == pgm_ok); // Set retries for NCF after NAK (NAK_NCF_RETRIES). rc = pgm_transport_set_nak_ncf_retries (transport, 2); zmq_assert (rc == pgm_ok); // Set timeout for removing a dead peer [us]. rc = pgm_transport_set_peer_expiry (transport, 5 * 8192 * 1000); zmq_assert (rc == pgm_ok); // Set expiration time of SPM Requests [us]. rc = pgm_transport_set_spmr_expiry (transport, 25 * 1000); zmq_assert (rc == pgm_ok); // Set the size of the receive window. // Data rate is in [B/s]. options.rate is in [kb/s]. if (options.rate <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_rxw_max_rte (transport, options.rate * 1000 / 8); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Recovery interval [s]. if (options.recovery_ivl <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_rxw_secs (transport, options.recovery_ivl); if (rc != pgm_ok) { errno = EINVAL; return -1; } } else { // Sender transport. // Set transport->can_recv = FALSE, waiting_pipe will not be read. rc = pgm_transport_set_send_only (transport, TRUE); zmq_assert (rc == pgm_ok); if (options.sndbuf) { rc = pgm_transport_set_sndbuf (transport, (int) options.sndbuf); if (rc != pgm_ok) return -1; } // Set the size of the send window. // Data rate is in [B/s] options.rate is in [kb/s]. if (options.rate <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_txw_max_rte (transport, options.rate * 1000 / 8); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Recovery interval [s]. if (options.recovery_ivl <= 0) { errno = EINVAL; return -1; } rc = pgm_transport_set_txw_secs (transport, options.recovery_ivl); if (rc != pgm_ok) { errno = EINVAL; return -1; } // Set interval of background SPM packets [us]. rc = pgm_transport_set_ambient_spm (transport, 8192 * 1000); zmq_assert (rc == pgm_ok); // Set intervals of data flushing SPM packets [us]. guint spm_heartbeat[] = {4 * 1000, 4 * 1000, 8 * 1000, 16 * 1000, 32 * 1000, 64 * 1000, 128 * 1000, 256 * 1000, 512 * 1000, 1024 * 1000, 2048 * 1000, 4096 * 1000, 8192 * 1000}; rc = pgm_transport_set_heartbeat_spm (transport, spm_heartbeat, G_N_ELEMENTS(spm_heartbeat)); zmq_assert (rc == pgm_ok); } // Enable multicast loopback. if (options.use_multicast_loop) { rc = pgm_transport_set_multicast_loop (transport, true); zmq_assert (rc == pgm_ok); } // Bind a transport to the specified network devices. if (!pgm_transport_bind (transport, &pgm_error)) { // TODO: tranlate errors from glib into errnos. return -1; } return 0; }
static gboolean on_startup ( G_GNUC_UNUSED gpointer data ) { g_message ("startup."); g_message ("create transport."); pgm_gsi_t gsi; int e = pgm_create_md5_gsi (&gsi); g_assert (e == 0); struct group_source_req recv_gsr, send_gsr; gsize recv_len = 1; e = pgm_if_parse_transport (g_network, AF_UNSPEC, &recv_gsr, &recv_len, &send_gsr); g_assert (e == 0); g_assert (recv_len == 1); if (g_source[0]) { ((struct sockaddr_in*)&recv_gsr.gsr_source)->sin_addr.s_addr = inet_addr(g_source); } if (g_udp_encap_port) { ((struct sockaddr_in*)&send_gsr.gsr_group)->sin_port = g_htons (g_udp_encap_port); ((struct sockaddr_in*)&recv_gsr.gsr_group)->sin_port = g_htons (g_udp_encap_port); } pgm_transport_list = NULL; e = pgm_transport_create (&g_transport, &gsi, 0, g_port, &recv_gsr, 1, &send_gsr); g_assert (e == 0); pgm_transport_set_recv_only (g_transport, FALSE); pgm_transport_set_max_tpdu (g_transport, g_max_tpdu); pgm_transport_set_rxw_sqns (g_transport, g_sqns); pgm_transport_set_multicast_loop (g_transport, g_multicast_loop); pgm_transport_set_hops (g_transport, 16); pgm_transport_set_peer_expiry (g_transport, pgm_secs(300)); pgm_transport_set_spmr_expiry (g_transport, pgm_msecs(250)); pgm_transport_set_nak_bo_ivl (g_transport, pgm_msecs(50)); pgm_transport_set_nak_rpt_ivl (g_transport, pgm_secs(2)); pgm_transport_set_nak_rdata_ivl (g_transport, pgm_secs(2)); pgm_transport_set_nak_data_retries (g_transport, 50); pgm_transport_set_nak_ncf_retries (g_transport, 50); e = pgm_transport_bind (g_transport); if (e < 0) { if (e == -1) g_critical ("pgm_transport_bind failed errno %i: \"%s\"", errno, strerror(errno)); else if (e == -2) g_critical ("pgm_transport_bind failed h_errno %i: \"%s\"", h_errno, hstrerror(h_errno)); else g_critical ("pgm_transport_bind failed e %i", e); g_main_loop_quit(g_loop); return FALSE; } g_assert (e == 0); /* create receiver thread */ GError* err; g_thread = g_thread_create_full (receiver_thread, g_transport, 0, TRUE, TRUE, G_THREAD_PRIORITY_HIGH, &err); if (!g_thread) { g_critical ("g_thread_create_full failed errno %i: \"%s\"", err->code, err->message); g_main_loop_quit(g_loop); return FALSE; } /* period timer to indicate some form of life */ // TODO: Gnome 2.14: replace with g_timeout_add_seconds() g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); g_message ("startup complete."); return FALSE; }