void srt::CChannel::createSocket(int family)
{
m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP);
}
void srt::CChannel::open(int family)
{
// 创建一个 socket,使用传入的协议族(family)参数。
createSocket(family);
// 定义 addrinfo 结构体,用于指定 getaddrinfo 函数的搜索条件。
addrinfo hints;
// 定义 addrinfo 结构体指针,用于存储 getaddrinfo 函数的返回结果。
addrinfo* res;
// 将 hints 结构体初始化为 0。
memset(&hints, 0, sizeof(struct addrinfo));
// 设置搜索条件。
hints.ai_flags = AI_PASSIVE; // 使用被动模式,通常用于服务器端,表示地址适用于绑定。
hints.ai_family = family; // 设置协议族,如 AF_INET、AF_INET6 等。
hints.ai_socktype = SOCK_DGRAM; // 设置 socket 类型为 UDP。
// 使用 getaddrinfo 函数解析本地地址,返回结果存储在 res 指针中。
const int eai = ::getaddrinfo(NULL, "0", &hints, &res);
// 检查 getaddrinfo 是否成功。如果失败,不执行绑定操作,直接跳到设置 UDP 套接字选项。
if (eai != 0)
{
// 如果 getaddrinfo 失败,这里没有处理错误,而是直接跳过绑定步骤。
// 这可能不是最佳实践,因为通常应该处理错误,例如记录日志或抛出异常。
}
// 绑定 socket 到获取的地址,这里缺少了对 getaddrinfo 失败的检查。
// 如果 getaddrinfo 失败,res 将是 NULL,这将导致绑定操作失败。
if (0 != ::bind(m_iSocket, res->ai_addr, (socklen_t)res->ai_addrlen))
{
// 如果 bind 函数失败,这里没有处理错误,而是直接跳过设置 UDP 套接字选项。
// 同样,这可能不是最佳实践,因为通常应该处理错误。
}
// 设置 UDP socket 选项,如缓冲区大小等。
setUDPSockOpt();
}
void srt::CChannel::open(int family)
{
// 创建一个 socket,使用传入的协议族(family)参数。
createSocket(family);
// sendto 或 WSASendTo 也会自动绑定 socket,所以这里不需要显式绑定。
addrinfo hints; // 定义 addrinfo 结构体,用于指定 getaddrinfo 函数的搜索条件。
addrinfo* res; // 定义 addrinfo 结构体指针,用于存储 getaddrinfo 函数的返回结果。
// 将 hints 结构体初始化为 0。
memset(&hints, 0, sizeof(struct addrinfo));
// 设置搜索条件。
hints.ai_flags = AI_PASSIVE; // 使用被动模式,通常用于服务器端,表示地址适用于绑定。
hints.ai_family = family; // 设置协议族,如 AF_INET、AF_INET6 等。
hints.ai_socktype = SOCK_DGRAM; // 设置 socket 类型为 UDP。
// 使用 getaddrinfo 函数解析本地地址,返回结果存储在 res 指针中。
const int eai = ::getaddrinfo(NULL, "0", &hints, &res);
if (eai != 0)
{
// 如果 getaddrinfo 函数失败,抛出异常。这里的错误码 eai 被传递给异常。
throw CUDTException(MJ_SETUP, MN_NORES, eai);
}
// 将 res 指向的地址信息用于绑定 socket。
// Windows 下 ai_addrlen 类型为 size_t(无符号),而 bind 函数需要 int 类型。
if (0 != ::bind(m_iSocket, res->ai_addr, (socklen_t)res->ai_addrlen))
{
// 如果 bind 函数失败,释放 res 指向的地址信息并抛出异常。
::freeaddrinfo(res);
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
}
// 将绑定的地址信息保存到 m_BindAddr 成员变量中。
m_BindAddr = sockaddr_any(res->ai_addr, (sockaddr_any::len_t)res->ai_addrlen);
#ifdef SRT_ENABLE_PKTINFO
// 如果启用了 SRT_ENABLE_PKTINFO 特性,标记为绑定掩码。
m_bBindMasked = true;
#endif
// 释放 getaddrinfo 函数分配的地址信息。
::freeaddrinfo(res);
// 使用日志记录绑定到的本地地址。
HLOGC(kmlog.Debug, log << "CHANNEL: Bound to local address: " << m_BindAddr.str());
// 设置 UDP socket 选项,如缓冲区大小等。
setUDPSockOpt();
}
void srt::CChannel::attach(UDPSOCKET udpsock, const sockaddr_any& udpsocks_addr)
{
// 这行注释说明在调用此函数之前,已经使用 getsockname() 获取了 socket 的名称,
// 并将结果存储在 udpsocks_addr 中。
// 将传入的 UDP socket 描述符赋值给类的成员变量 m_iSocket。
m_iSocket = udpsock;
// 将传入的 UDP socket 地址赋值给类的成员变量 m_BindAddr。
m_BindAddr = udpsocks_addr;
// 调用 setUDPSockOpt 函数来设置 UDP socket 选项,如缓冲区大小等。
setUDPSockOpt();
}
void srt::CChannel::setUDPSockOpt()
{
// BSD系统如果请求的缓冲区大小超过了系统的最大值,setsockopt会失败。
// 这里定义了一个名为maxsize的变量,其值为64000,用作缓冲区大小的上限。
int maxsize = 64000;
// 尝试设置UDP接收缓冲区的大小为m_mcfg.iUDPRcvBufSize。
// 如果设置失败(返回值非0),则尝试将接收缓冲区大小设置为系统最大值maxsize。
if (0 != ::setsockopt(
m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&m_mcfg.iUDPRcvBufSize, sizeof m_mcfg.iUDPRcvBufSize))
{
::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&maxsize, sizeof maxsize);
}
// 尝试设置UDP发送缓冲区的大小为m_mcfg.iUDPSndBufSize。
// 如果设置失败(返回值非0),则尝试将发送缓冲区大小设置为系统最大值maxsize。
if (0 != ::setsockopt(
m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m_mcfg.iUDPSndBufSize, sizeof m_mcfg.iUDPSndBufSize))
{
::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&maxsize, sizeof maxsize);
}
}
int srt::CChannel::getSndBufSize() //设置发送buffer大小
{
socklen_t size = (socklen_t)sizeof m_mcfg.iUDPSndBufSize;
::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_mcfg.iUDPSndBufSize, &size);
return m_mcfg.iUDPSndBufSize;
}
int srt::CChannel::getRcvBufSize() //获取接收buffer大小
{
socklen_t size = (socklen_t)sizeof m_mcfg.iUDPRcvBufSize;
::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_mcfg.iUDPRcvBufSize, &size);
return m_mcfg.iUDPRcvBufSize;
}
void srt::CChannel::getSockAddr(sockaddr_any& w_addr) const
{
// getsockname 函数只需要足够的目标空间来复制套接字名称,
// 它不需要与地址族相关联。因此,任何名称的最大空间,
// 不论家族如何,都可以胜任。
socklen_t namelen = (socklen_t)w_addr.storage_size(); // 获取 w_addr 提供的存储空间大小。
::getsockname(m_iSocket, (w_addr.get()), (&namelen)); // 使用 getsockname 函数获取当前套接字的本地地址。
w_addr.len = namelen; // 更新 w_addr 的长度为获取的地址长度。
}
void srt::CChannel::getPeerAddr(sockaddr_any& w_addr) const
{
// 获取 w_addr 提供的存储空间大小。
socklen_t namelen = (socklen_t)w_addr.storage_size();
// 使用 getpeername 函数获取当前套接字的远程(对端)地址。
::getpeername(m_iSocket, (w_addr.get()), (&namelen));
// 更新 w_addr 的长度为获取的地址长度。
w_addr.len = namelen;
}