hylarana_transport/transmission/server.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
use std::{ffi::c_int, io::Error, net::SocketAddr};
use os_socketaddr::OsSocketAddr;
use super::{srt_getsockstate, SRT_SOCKSTATUS};
use super::{
error, options::Descriptor, socket::Socket, srt_accept, srt_bind, srt_bstats, srt_close,
srt_create_socket, srt_getsockname, srt_listen, TraceStats, SRTSOCKET, SRT_INVALID_SOCK,
};
pub struct Server {
fd: SRTSOCKET,
}
unsafe impl Send for Server {}
unsafe impl Sync for Server {}
impl Server {
/// Reports the current statistics
///
/// Arguments:
///
/// u: Socket from which to get statistics
/// perf: Pointer to an object to be written with the statistics
/// clear: 1 if the statistics should be cleared after retrieval
pub fn get_stats(&self) -> Result<TraceStats, Error> {
let mut stats = TraceStats::default();
if unsafe { srt_bstats(self.fd, &mut stats, true as i32) } != 0 {
return Err(error());
}
Ok(stats)
}
/// Binds a socket to a local address and port. Binding specifies the local
/// network interface and the UDP port number to be used for the socket.
/// When the local address is a wildcard (`INADDR_ANY` for IPv4 or
/// `in6addr_any` for IPv6), then it's bound to all interfaces.
///
/// **IMPORTANT**: When you bind an IPv6 wildcard address, note that the
/// `SRTO_IPV6ONLY` option must be set on the socket explicitly to 1 or 0
/// prior to calling this function. See
/// `SRTO_IPV6ONLY`(API-socket-options.md#SRTO_IPV6ONLY) for more details.
///
/// Binding is necessary for every socket to be used for communication. If
/// the socket is to be used to initiate a connection to a listener
/// socket, which can be done, for example, by the
/// `srt_connect`(#srt_connect) function, the socket is bound
/// implicitly to the wildcard address according to the IP family
/// (`INADDR_ANY` for `AF_INET` or `in6addr_any` for `AF_INET6`) and
/// port number 0. In all other cases, a socket must be bound explicitly
/// by using the functionality of this function first.
///
/// When the port number parameter is 0, then the effective port number will
/// be system-allocated. To obtain this effective port number you can
/// use `srt_getsockname`(#srt_getsockname).
///
/// This call is obligatory for a listening socket before calling
/// `srt_listen`(#srt_listen) and for rendezvous mode before calling
/// `srt_connect`(#srt_connect); otherwise it's optional. For a
/// listening socket it defines the network interface and the port where
/// the listener should expect a call request.
///
/// In the case of rendezvous mode there are two parties that connect to one
/// another. For every party there must be chosen a local binding
/// endpoint (local address and port) to which they expect connection
/// from the peer. Let's say, we have a Party 1 that selects an endpoint
/// A and a Party 2 that selects an endpoint B. In this case the Party 1
/// binds the socket to the endpoint A and then connects to the endpoint B,
/// and the Party 2 the other way around. Both sockets must be set
/// `SRTO_RENDEZVOUS`(API-socket-options.md#SRTO_RENDEZVOUS) to *true* to
/// make this connection possible.
///
/// For a connecting socket the call to `srt_bind` is optional, but can be
/// used to set up the outgoing port for communication as well as the
/// local interface through which it should reach out to the remote
/// endpoint, should that be necessary.
///
/// Whether binding is possible depends on some runtime conditions, in
/// particular:
///
/// * No socket in the system has been bound to this port ("free binding"),
/// or
///
/// * A socket bound to this port is bound to a certain address, and this
/// binding is
/// using a different non-wildcard address ("side binding"), or
///
/// * A socket bound to this port is bound to a wildcard address for a
/// different IP
/// version than the version requested for this binding ("side wildcard
/// binding", see also `SRTO_IPV6ONLY` socket option).
///
/// It is also possible to bind to the already busy port as long as the
/// existing binding ("shared binding") is possessed by an SRT socket
/// created in the same application, and:
///
/// * Its binding address and UDP-related socket options match the socket to
/// be bound.
/// * Its `SRTO_REUSEADDR`(API-socket-options.md#SRTO_REUSEADDRS) is set to
/// *true* (default).
///
/// If none of the free, side and shared binding options is currently
/// possible, this function will fail. If the socket blocking the
/// requested endpoint is an SRT socket in the current application, it
/// will report the `SRT_EBINDCONFLICT` error, while if it was another
/// socket in the system, or the problem was in the system in general,
/// it will report `SRT_ESOCKFAIL`. Here is the table that shows possible
/// situations:
///
/// Where:
///
/// * free: This binding can coexist with the requested binding.
///
/// * blocked: This binding conflicts with the requested binding.
///
/// * shareable: This binding can be shared with the requested binding if
/// it's compatible.
///
/// * (ADDRESS) shareable, else free: this binding is shareable if the
/// existing binding address is
/// equal to the requested ADDRESS. Otherwise it's free.
///
/// If the binding is shareable, then the operation will succeed if the
/// socket that currently occupies the binding has the `SRTO_REUSEADDR`
/// option set to true (default) and all UDP settings are the same as in
/// the current socket. Otherwise it will fail. Shared binding means
/// sharing the underlying UDP socket and communication queues between SRT
/// sockets. If all existing bindings on the same port are "free" then
/// the requested binding will allocate a distinct UDP socket for this
/// SRT socket ("side binding").
///
/// **NOTE**: This function cannot be called on a socket group. If you need
/// to have the group-member socket bound to the specified source
/// address before connecting, use
/// `srt_connect_bind`(#srt_connect_bind) for that purpose or set the
/// appropriate source address using `srt_prepare_endpoint`(#
/// srt_prepare_endpoint).
///
/// **IMPORTANT information about IPv6**: If you are going to bind to the
/// `in6addr_any` IPv6 wildcard address (known as `::`), the `SRTO_IPV6ONLY`
/// option must be first set explicitly to 0 or 1, otherwise the binding
/// will fail. In all other cases this option is meaningless. See
/// `SRTO_IPV6ONLY` option for more information.
pub fn bind(addr: SocketAddr, opt: Descriptor, backlog: u32) -> Result<Self, Error> {
let fd = unsafe { srt_create_socket() };
if fd == SRT_INVALID_SOCK {
return Err(error());
} else {
opt.apply_socket(fd)?;
}
let addr: OsSocketAddr = addr.into();
if unsafe { srt_bind(fd, addr.as_ptr() as *const _, addr.len() as c_int) } == -1 {
return Err(error());
}
if unsafe { srt_listen(fd, backlog as c_int) } == -1 {
return Err(error());
}
Ok(Self { fd })
}
/// Accepts a pending connection, then creates and returns a new socket or
/// group ID that handles this connection. The group and socket can be
/// distinguished by checking the `SRTGROUP_MASK` bit on the returned ID.
///
/// * `lsn`: the listener socket previously configured by
/// `srt_listen`(#srt_listen)
/// * `addr`: the IP address and port specification for the remote party
/// * `addrlen`: INPUT: size of `addr` pointed object. OUTPUT: real size of
/// the
/// returned object
///
/// **NOTE:** `addr` is allowed to be NULL, in which case it's understood
/// that the application is not interested in the address from which the
/// connection originated. Otherwise `addr` should specify an object
/// into which the address will be written, and `addrlen` must also
/// specify a variable to contain the object size. Note also that in the
/// case of group connection only the initial connection that
/// establishes the group connection is returned, together with its address.
/// As member connections are added or broken within the group, you can
/// obtain this information through `srt_group_data`(#srt_group_data)
/// or the data filled by `srt_sendmsg2`(#srt_sendmsg) and
/// `srt_recvmsg2`(#srt_recvmsg2).
///
/// If the `lsn` listener socket is configured for blocking mode
/// (`SRTO_RCVSYN`(API-socket-options.md#SRTO_RCVSYN) set to true,
/// default), the call will block until the incoming connection is
/// ready. Otherwise, the call always returns immediately. The
/// `SRT_EPOLL_IN` epoll event should be checked on the `lsn` socket
/// prior to calling this function in that case.
///
/// If the pending connection is a group connection (initiated on the peer
/// side by calling the connection function using a group ID, and
/// permitted on the listener socket by the
/// `SRTO_GROUPCONNECT`(API-socket-options.md#SRTO_GROUPCONNECT)
/// flag), then the value returned is a group ID. This function then creates
/// a new group, as well as a new socket for this connection, that will
/// be added to the group. Once the group is created this way, further
/// connections within the same group, as well as sockets for them, will
/// be created in the background. The `SRT_EPOLL_UPDATE`(#
/// SRT_EPOLL_UPDATE) event is raised on the `lsn` socket when
/// a new background connection is attached to the group, although it's
/// usually for internal use only.
pub fn accept(&self) -> Result<(Socket, SocketAddr), Error> {
let status = unsafe { srt_getsockstate(self.fd) };
if status != SRT_SOCKSTATUS::SRTS_LISTENING {
return Err(Error::other(format!("{:?}", status)));
}
let mut addr = OsSocketAddr::new();
let mut addrlen = addr.capacity() as c_int;
let fd = unsafe { srt_accept(self.fd, addr.as_mut_ptr() as *mut _, &mut addrlen) };
if fd != SRT_INVALID_SOCK {
if let Some(addr) = addr.into() {
return Ok((Socket::new(fd), addr));
}
}
Err(error())
}
/// Extracts the address to which the socket was bound. Although you should
/// know the address(es) that you have used for binding yourself, this
/// function can be useful for extracting the local outgoing port number
/// when it was specified as 0 with binding for system autoselection. With
/// this function you can extract the port number after it has been
/// autoselected.
pub fn local_addr(&self) -> Option<SocketAddr> {
let mut addr = OsSocketAddr::new();
let mut addrlen = addr.capacity() as c_int;
unsafe {
srt_getsockname(self.fd, addr.as_mut_ptr() as *mut _, &mut addrlen);
}
addr.into()
}
/// Closes the socket or group and frees all used resources. Note that
/// underlying UDP sockets may be shared between sockets, so these are
/// freed only with the last user closed.
pub fn close(&self) {
unsafe { srt_close(self.fd) };
}
}
impl Drop for Server {
fn drop(&mut self) {
self.close()
}
}