用户接口设计
设计需求:
接口API, RFC4960有规范, 基本上按照设计就可以了. 需要考虑的地方时, 1. 提供简化的接口,方便懒人. 2. 由于设计此SCTP库的目的是用于p2p传输, 所以,要考虑方便服务于p2p的库.
定义了一个如下回调函数表, 当有连接请求或数据到达时, 回调.
typedef struct sctp_call_back_ {
sctp_pkg_snd_fn sctp_pkg_snd;
data_received_fn data_received_cb;
sctp_assoc_up_fn assoc_up_cb;
sctp_assoc_down_fn assoc_down_cb;
sctp_retrieve_fn retrieve_cb;
sctp_debug_fn debug_cb;
}sctp_call_back;
sctp_pkg_snd 是可选的参数, 如果你想对数据的发送方法做特殊处理, 可以覆盖库的缺省动作. 如果不设置此函数,那么, 将直接发送到对方IP地址:接收SCTP包的UDP端口.
data_received_cb, 有SCTP包到达时被触发.
retrieve_cb, sctp一个流断掉后, 没有确认的数据包可以用此函数提取, 保证高可靠性.
接口API:
入参, udp_port, 用于sctp流的udp端口. ctx, 可选, 用户提供的上下文, 对sctp库来说,透明,方便上层应用.
返回成功/失败
BOOL sctp_init(sctp_call_back* sctp_cb, uint16 udp_port, void *ctx)
返回 instance, 起一个sctp的服务socket, 相当于listen
int sctp_listen(uint16 local_port, uint32 local_ip);
返回assoc id, 相当与connect
peer_id是可选项, 16字节. 用来和p2p库一起使用时, 此peer_id包含了对方的IP地址,端口等. 对方可以是一个有公共网IP的节点,此时, 根据peer_id可以得到对方地址直接使用. 当对方在一个私网中,此id可用来请p2p库帮忙路由数据包到达对方. 简单的说, 在p2p网中, 重要的是peer_id,有了它就可以找到对方, 此peer_id起到的作用就是IP地址的作用.
int sctp_connect(int instance, uint32 peer_ip, uint16 dst_port, uchar *peer_id);
相当于send
int sctp_send_data(int assoc_id, uchar *buf, uint16 len)
例子:
初始化:
入口参数是用来传输sctp数据的udp端口号. 如果是在局域网中,这个端口可能和NAT后的端口不一样,简单的说,就是本机服务端口:
int app_sctp_init(uint16 sctp_udp_port)
{
sctp_call_back call_cb;
memset(&call_cb, 0, sizeof(call_cb));
call_cb.assoc_down_cb = assoc_down;
call_cb.assoc_up_cb = assoc_up;
call_cb.data_received_cb = data_rcvd;
call_cb.sctp_pkg_snd = send_sctp_pkg;
sctp_init(&call_cb, VIDEO_UDP_PORT, p2p_hdl);
return TRUE;
}
回调函数:
int assoc_down(int assoc_id, int reason)
{
if (vedioDlg->videoAssoc == assoc_id)
{
vedioDlg->videoAssoc = 0;
vedioDlg->ShowWindow(SW_HIDE);
memset(guiDlg->active_video_peer, 0, 16);
}
return 0;
}
int assoc_up(int instance, int assoc_id, unsigned short peer_port, int in_stream, int out_stream)
{
vedioDlg->videoAssoc = assoc_id;
vedioDlg->ShowWindow(SW_SHOW);
return 0;
}
int data_rcvd(int assoc_id, unsigned char *buf, unsigned short len,
unsigned short stream_id, int protocol, int part_data)
{
if (vedioDlg->isCaptureStarted == FALSE)
{
// user has not click start video yet
sctp_consume_data(assoc_id, buf, len);
return len;
}
PostMessage(guiDlg->m_hWnd, WM_VIDEODATA, (WPARAM)buf, (LPARAM)len);
return len;
}