SIP协议栈--osip源码梳理
文章目录
- osip
- osip主体
- 结构体
- code main函数
- 状态机转化
- 结构体
- code状态转换
- sip事务
- 结构体
- code
- osip_dialog
- 结构体
- code 创建并发送200 OK响应
- osip_message
- 结构体
- code
- osip_event
- code 打印接收到的SIP消息
osip
OSIP(Open Source Implementation of SIP)是一个开源的SIP(Session Initiation Protocol)协议栈,主要用于开发VoIP(Voice over Internet Protocol)应用程序。
主要功能
- SIP消息解析和构建:OSIP提供了丰富的API来解析和构建SIP消息。
- 事务管理:支持UAC(User Agent Client)、UAS(User Agent Server)等不同角色下的事务处理。
- 状态机:内置的状态机帮助开发者更好地管理和维护SIP对话的状态。
- 扩展性:允许用户自定义头字段和其他SIP元素,以便满足特定应用的需求。
osip主体
结构体
struct osip
{void* application_context; //User defined Pointer// 用户定义的指针,允许开发者存储自定义的应用程序上下文数据//list of transactions for ict, ist, nict, nistosip_list_t osip_ict_transactions; //list of ict transactions// 客户端事务(Invite Client Transaction, ICT)的列表, // 包含多个osip_transaction_t对象,每个对象表示一个具体的ICTosip_list_t osip_ist_transactions; //list of ist transactions// 服务器事务(Invite Server Transaction, IST)的列表//包含多个osip_transaction_t对象,每个对象表示一个具体的ISTosip_list_t osip_nict_transactions; //list of nict transaction//处理除INVITE之外的所有其他类型的SIP请求(如REGISTER、SUBSCRIBE等osip_list_t osip_nist_transactions; //list of nist transactions// 用于处理接收到的除INVITE之外的所有其他类型的SIP请求。osip_list_t ixt_retransmissions; //list of ixt elements// 管理重新传输的消息元素的列表osip_message_cb_t msg_callbacks[OSIP_MESSAGE_CALLBACK_COUNT]; osip_kill_transaction_cb_t kill_callbacks[OSIP_KILL_CALLBACK_COUNT];osip_transport_error_cb_t tp_error_callbacks[OSIP_TRANSPORT_ERROR_CALLBACK_COUNT];int (*cb_send_message)(osip_transaction_t*, osip_message_t*, char*, int, int, int);#if defined(HAVE_DICT_DICT_H)dict* osip_ict_hastable; //htable of ict transactionsdict* osip_ist_hastable; //htable of ist transactionsdict* osip_nict_hastable; //htable of nict transactionsdict* osip_nist_hastable; //htable of nist transactions
#endif
}
回调函数:
1 消息回调 (osip_message_cb_t):
功能: 这些回调函数用于处理接收到的各种类型的SIP消息(如INVITE、ACK、BYE等)。每当OSIP库接收到一个新的SIP消息时,它会根据消息类型调用相应的回调函数。
作用: 开发者可以通过实现这些回调函数来定义如何处理不同的SIP消息,从而实现业务逻辑。
2 事务终止回调 (osip_kill_transaction_cb_t):
功能: 这些回调函数用于处理事务的终止。每当OSIP库决定终止一个事务时,它会调用相应的回调函数来清理资源或执行其他必要的操作。
作用: 开发者可以通过实现这些回调函数来确保资源得到正确的释放,并执行任何必要的清理工作。
3 传输层错误回调 (osip_transport_error_cb_t):
功能: 这些回调函数用于处理传输层错误。每当OSIP库检测到传输层错误(如网络连接失败、超时等)时,它会调用相应的回调函数来处理这些错误。
作用: 开发者可以通过实现这些回调函数来应对各种传输层问题,例如重试机制、日志记录等。
4 发送消息回调 (cb_send_message):
功能: 这个回调函数用于发送SIP消息。每当OSIP库需要发送一个SIP消息时,它会调用这个回调函数来完成实际的发送操作。
作用: 开发者可以通过实现这个回调函数来自定义消息的发送方式,例如使用特定的传输协议(TCP、UDP等),或者添加额外的日志记录。
code main函数
int main() {osip_t *osip;osip_init(&osip, OSIP_LOG_LEVEL_INFO);// 设置消息回调函数osip_message_cb_t msg_callbacks[OSIP_MESSAGE_CALLBACK_COUNT];for (int i = 0; i < OSIP_MESSAGE_CALLBACK_COUNT; i++) {msg_callbacks[i] = NULL;}msg_callbacks[OSIP_INVITE] = handle_invite;osip->msg_callbacks = msg_callbacks;// 模拟接收一个INVITE请求const char *invite_request_str ="INVITE sip:user@example.com SIP/2.0\r\n""Via: SIP/2.0/UDP localhost:5060;branch=z9hG4bK77ef4c2312983.1\r\n""Max-Forwards: 70\r\n""To: \"User\" <sip:user@example.com>\r\n""From: \"Caller\" <sip:caller@localhost>;tag=12345\r\n""Call-ID: unique-id@localhost\r\n""CSeq: 1 INVITE\r\n""Content-Length: 0\r\n\r\n";osip_message_t *invite_request;osip_message_init(&invite_request);osip_message_parse(invite_request, invite_request_str, strlen(invite_request_str));// 创建Invite Client Transaction (ICT)osip_transaction_t *transaction;osip_ict_new(&transaction, invite_request, osip);// 初始化事件osip_event_t *event;osip_event_init(&event, OSIP_ICT_NEW, transaction, invite_request, NULL, NULL);// 处理事件osip_handle_events(osip);// 清理资源osip_message_free(invite_request);osip_transaction_free(transaction);osip_shutdown(osip);return 0;
}
状态机转化
结构体
struct _transition_t
{state_t state;type_t type;void (*method) (void *, void *);struct _transition_t* next;struct _transition_t* parent;
};
struct _transition_t结构体用于描述有限状态机中的一个状态转换。每个转换由以下几个部分组成:
- 当前状态 (state_t state): 标识转换前的状态。
- 事件类型 (type_t type): 标识触发转换的事件。
- 处理方法 (void (*method) (void *, void *)): 指向处理转换的方法,包含具体的业务逻辑。
- 下一个转换 (struct _transition_t* next): 指向下一个转换,形成链表或树状结构。
- 父转换 (struct _transition_t* parent): 指向父转换,用于跟踪层次关系。
code状态转换
#include <stdio.h>
#include <stdlib.h>// 定义状态类型
typedef enum {STATE_IDLE,STATE_RUNNING,STATE_PAUSED,STATE_STOPPED
} state_t;// 定义事件类型
typedef enum {EVENT_START,EVENT_PAUSE,EVENT_RESUME,EVENT_STOP
} type_t;// 定义过渡结构体
struct _transition_t {state_t state;type_t type;void (*method)(void *, void *);struct _transition_t *next;struct _transition_t *parent;
};// 状态转换方法的原型
void transition_method_start(void *fsm_instance, void *event_data);
void transition_method_pause(void *fsm_instance, void *event_data);
void transition_method_resume(void *fsm_instance, void *event_data);
void transition_method_stop(void *fsm_instance, void *event_data);// 模拟的状态机实例
typedef struct {state_t current_state;
} fsm_instance_t;// 状态转换方法的具体实现
void transition_method_start(void *fsm_instance, void *event_data) {((fsm_instance_t *)fsm_instance)->current_state = STATE_RUNNING;printf("Transition from IDLE to RUNNING\n");
}void transition_method_pause(void *fsm_instance, void *event_data) {((fsm_instance_t *)fsm_instance)->current_state = STATE_PAUSED;printf("Transition from RUNNING to PAUSED\n");
}void transition_method_resume(void *fsm_instance, void *event_data) {((fsm_instance_t *)fsm_instance)->current_state = STATE_RUNNING;printf("Transition from PAUSED to RUNNING\n");
}void transition_method_stop(void *fsm_instance, void *event_data) {((fsm_instance_t *)fsm_instance)->current_state = STATE_STOPPED;printf("Transition from %d to STOPPED\n", ((fsm_instance_t *)fsm_instance)->current_state);
}// 查找并执行状态转换
void process_event(fsm_instance_t *fsm_instance, type_t event_type, struct _transition_t *transitions) {struct _transition_t *current = transitions;while (current != NULL) {if (current->state == fsm_instance->current_state && current->type == event_type) {current->method(fsm_instance, NULL);return;}current = current->next;}printf("No valid transition found for state %d and event %d\n", fsm_instance->current_state, event_type);
}int main() {// 初始化状态机实例fsm_instance_t fsm_instance;fsm_instance.current_state = STATE_IDLE;// 创建状态转换链表struct _transition_t *transitions = NULL;// 添加状态转换struct _transition_t *start_transition = malloc(sizeof(struct _transition_t));start_transition->state = STATE_IDLE;start_transition->type = EVENT_START;start_transition->method = transition_method_start;start_transition->next = NULL;start_transition->parent = NULL;transitions = start_transition;struct _transition_t *pause_transition = malloc(sizeof(struct _transition_t));pause_transition->state = STATE_RUNNING;pause_transition->type = EVENT_PAUSE;pause_transition->method = transition_method_pause;pause_transition->next = NULL;pause_transition->parent = start_transition;start_transition->next = pause_transition;struct _transition_t *resume_transition = malloc(sizeof(struct _transition_t));resume_transition->state = STATE_PAUSED;resume_transition->type = EVENT_RESUME;resume_transition->method = transition_method_resume;resume_transition->next = NULL;resume_transition->parent = pause_transition;pause_transition->next = resume_transition;struct _transition_t *stop_transition = malloc(sizeof(struct _transition_t));stop_transition->state = STATE_RUNNING;stop_transition->type = EVENT_STOP;stop_transition->method = transition_method_stop;stop_transition->next = NULL;stop_transition->parent = resume_transition;resume_transition->next = stop_transition;// 处理事件process_event(&fsm_instance, EVENT_START, transitions); // 从IDLE到RUNNINGprocess_event(&fsm_instance, EVENT_PAUSE, transitions); // 从RUNNING到PAUSEDprocess_event(&fsm_instance, EVENT_RESUME, transitions);// 从PAUSED到RUNNINGprocess_event(&fsm_instance, EVENT_STOP, transitions); // 从RUNNING到STOPPED// 清理资源free(start_transition);free(pause_transition);free(resume_transition);free(stop_transition);return 0;
}
sip事务
结构体
struct osip_transaction
{ void* your_instance; // User Defined Pointerint transactionid; // Internal Transaction Identifierosip_fifo_t* transactionff; // events must be added in this fifoosip_via_t* topvia; // CALL-LEG definition (Top Via)osip_from_t* from; // CALL-LEG definition (From) osip_to_t* to; // CALL-LEG definition (To) osip_call_id_t* callid; // CALL-LEG definition (Call-ID)osip_cseq_t* cseq; // CALL-LEG definition (CSeq) osip_message_t* orig_request; // Initial request osip_message_t* last_response; // Last response osip_message_t* ack; // ack request sent state_t state; // Current state of the transactiontime_t birth_time; // birth date of transaction time_t completed_time; // end date of transaction unsigned long birth_time_ms; int in_socket; // Optional socket for incoming messageint out_socket; // Optional place for outgoing messagevoid* config; // internal transaction is managed by osip_t osip_fsm_type_t ctx_type; // Type of the transactionosip_ict_t* ict_context; // internalosip_ist_t* ist_context; // internalosip_nict_t* nict_context; // internalosip_nist_t* nist_context; // internalosip_srv_record_t record; // internal#ifdef FSM_TIMER_OUT_LISTunsigned short usTimerCtrl; // the timer start or stop controlunsigned short usTimerFlag; // the timer has or not deleted
#endif
#ifdef FSM_OPTIMIZM_ARRAYint nArrayIndex;
#endif
};
封装了与一个特定SIP事务相关的所有信息和状态,包括请求、响应、状态转换等。通过这个结构体,OSIP库可以有效地处理不同类型的SIP事务
- 事务标识和上下文:
your_instance: 用户定义的指针,用于存储自定义的应用程序上下文数据。
transactionid: 内部事务标识符,唯一标识一个事务。
config: 指向管理事务的osip_t实例。 - 事务状态和时间:
state: 当前事务的状态。
birth_time: 事务的创建时间。
completed_time: 事务的完成时间。
birth_time_ms: 事务的创建时间(毫秒级精度)。 - SIP消息相关:
topvia, from, to, callid, cseq: 指向SIP消息中的关键头字段。
orig_request: 初始请求消息。
last_response: 最后一个接收到的响应消息。
ack: 发送出去的ACK请求消息。 - 事务类型和上下文:
ctx_type: 事务的类型(ICT、IST、NICT、NIST)。
ict_context, ist_context, nict_context, nist_context: 指向不同类型事务的上下文。 - 网络和定时器管理:
in_socket, out_socket: 可选的套接字,用于接收和发送消息。
usTimerCtrl, usTimerFlag: 控制和指示定时器的状态。
record: 内部使用的记录结构,用于存储与事务相关的服务记录。
code
创建并发送100 Trying响应
- 初始化OSIP库:
使用osip_init初始化OSIP库。
设置日志级别为OSIP_LOG_LEVEL_INFO。 - 设置消息回调函数:
定义一个回调函数handle_invite来处理INVITE请求。
将回调函数注册到msg_callbacks数组中,并将其赋值给osip->msg_callbacks。 - 模拟接收INVITE请求:
定义一个字符串表示一个INVITE请求。
初始化一个osip_message_t对象并解析该字符串。 - 创建Invite Client Transaction (ICT):
使用osip_ict_new创建一个新的Invite Client Transaction (ICT),并将解析好的INVITE请求传递给它。 - 初始化事件:
创建一个osip_event_t对象,并将其类型设置为OSIP_ICT_NEW,同时关联事务和请求。 - 处理事件:
调用osip_handle_events处理事件,触发回调函数handle_invite。 - 处理INVITE请求:
在handle_invite回调函数中,解析接收到的INVITE请求。
打印关键头字段(如Via、From、To、Call-ID、CSeq)。
创建并发送100 Trying响应。 - 清理资源:
#include <osipparser2/osip_parser.h>
#include <osipparser2/osip_message.h>
#include <osipparser2/osip_header.h>
#include <osip2/osip.h>
#include <stdio.h>void handle_invite(osip_event_t *event) {osip_transaction_t *transaction = event->transaction;osip_message_t *request = transaction->orig_request;printf("Received INVITE request:\n%s\n", request->buffer);// 打印关键头字段printf("Via: %s\n", request->topvia->host);printf("From: %s <%s>\n", request->from->displayname, request->from->url->uri);printf("To: %s <%s>\n", request->to->displayname, request->to->url->uri);printf("Call-ID: %s\n", request->callid->number);printf("CSeq: %d %s\n", request->cseq->seq_number, request->cseq->method);// 创建并发送100 Trying响应osip_message_t *response;osip_message_init(&response);osip_message_set_version(response, "SIP/2.0");osip_message_set_status_code(response, 100);osip_message_set_reason_phrase(response, "Trying");osip_via_t *via;osip_via_init(&via);via->version = osip_strdup("2.0");via->protocol = osip_strdup("UDP");via->host = osip_strdup("localhost");via->port = osip_strdup("5060");osip_list_add(&response->vias, via, -1);osip_message_set_call_id(response, request->callid->number);osip_message_set_cseq(response, request->cseq->number, request->cseq->method);osip_message_set_from(response, request->from->displayname, request->from->url->username, request->from->url->host);osip_message_set_to(response, request->to->displayname, request->to->url->username, request->to->url->host);char *response_buffer;size_t response_length;osip_message_to_str(response, &response_buffer, &response_length);printf("Sending 100 Trying response:\n%.*s\n", (int)response_length, response_buffer);osip_transaction_send(transaction, response);osip_free(response_buffer);osip_message_free(response);
}
osip_dialog
结构体
struct osip_dialog结构体在OSIP库中用于管理SIP对话(Dialog)。SIP对话是一系列相关的SIP消息交换,通常涉及一个初始请求(如INVITE)及其响应序列。
struct osip_dialog{char *call_id; /**< Call-ID string*/osip_call_id_t* call_id_clone; /**< Call-ID header */ char *local_tag; /**< local tag */char *remote_tag; /**< remote tag */osip_list_t route_set; /**< route set */int local_cseq; /**< last local cseq */int remote_cseq; /**< last remote cseq*/osip_to_t *remote_uri; /**< remote_uri */osip_from_t *local_uri; /**< local_uri */osip_contact_t *remote_contact_uri; /**< remote contact_uri */int secure; /**< use secure transport layer */osip_dialog_type_t type; /**< type of dialog (CALLEE or CALLER) */state_t state; /**< DIALOG_EARLY || DIALOG_CONFIRMED || DIALOG_CLOSED */void *your_instance; /**< for application data reference */};
code 创建并发送200 OK响应
- 初始化OSIP库:
使用osip_init初始化OSIP库。
设置日志级别为OSIP_LOG_LEVEL_INFO。 - 设置用户定义的上下文:
将application_context设置为NULL,可以根据需要设置自定义的上下文数据。 - 设置消息回调函数:
定义一个回调函数handle_invite来处理INVITE请求。
将回调函数注册到msg_callbacks数组中,并将其赋值给osip->msg_callbacks。 - 设置事务终止回调函数:
定义一个回调函数kill_transaction_cb来处理事务终止。
将回调函数注册到kill_callbacks数组中,并将其赋值给osip->kill_callbacks。 - 设置传输层错误回调函数:
定义一个回调函数transport_error_cb来处理传输层错误。
将回调函数注册到tp_error_callbacks数组中,并将其赋值给osip->tp_error_callbacks。 - 设置发送消息的回调函数:
定义一个回调函数send_message_cb来发送SIP消息。
将回调函数赋值给osip->cb_send_message。 - 模拟接收INVITE请求:
定义一个字符串表示一个INVITE请求。
初始化一个osip_message_t对象并解析该字符串。 - 创建Invite Server Transaction (IST):
使用osip_ist_new创建一个新的Invite Server Transaction (IST),并将解析好的INVITE请求传递给它。
初始化事件:
创建一个osip_event_t对象,并将其类型设置为OSIP_IST_NEW,同时关联事务和请求。 - 处理事件:
调用osip_handle_events处理事件,触发回调函数handle_invite。 - 处理INVITE请求:
在handle_invite回调函数中,解析接收到的INVITE请求。
打印关键头字段(如Via、From、To、Call-ID、CSeq)。
创建并发送100 Trying响应。
创建并发送200 OK响应。
创建对话osip_dialog_t并初始化为UAS(User Agent Server)模式。
将对话添加到OSIP实例中的IST事务列表中。 - 清理资源:
释放分配的内存和资源,包括消息、事务和OSIP实例。
#include <osipparser2/osip_parser.h>
#include <osipparser2/osip_message.h>
#include <osipparser2/osip_header.h>
#include <osip2/osip.h>
#include <stdio.h>
#include <stdlib.h>// 回调函数:处理INVITE请求
void handle_invite(osip_event_t *event) {osip_transaction_t *transaction = event->transaction;osip_message_t *request = transaction->orig_request;printf("Received INVITE request:\n%s\n", request->buffer);// 打印关键头字段printf("Via: %s\n", request->topvia->host);printf("From: %s <%s>\n", request->from->displayname, request->from->url->uri);printf("To: %s <%s>\n", request->to->displayname, request->to->url->uri);printf("Call-ID: %s\n", request->callid->number);printf("CSeq: %d %s\n", request->cseq->seq_number, request->cseq->method);// 创建并发送100 Trying响应osip_message_t *response;osip_message_init(&response);osip_message_set_version(response, "SIP/2.0");osip_message_set_status_code(response, 100);osip_message_set_reason_phrase(response, "Trying");osip_via_t *via;osip_via_init(&via);via->version = osip_strdup("2.0");via->protocol = osip_strdup("UDP");via->host = osip_strdup("localhost");via->port = osip_strdup("5060");osip_list_add(&response->vias, via, -1);osip_message_set_call_id(response, request->callid->number);osip_message_set_cseq(response, request->cseq->number, request->cseq->method);osip_message_set_from(response, request->from->displayname, request->from->url->username, request->from->url->host);osip_message_set_to(response, request->to->displayname, request->to->url->username, request->to->url->host);char *response_buffer;size_t response_length;osip_message_to_str(response, &response_buffer, &response_length);printf("Sending 100 Trying response:\n%.*s\n", (int)response_length, response_buffer);osip_transaction_send(transaction, response);osip_free(response_buffer);osip_message_free(response);// 创建并发送200 OK响应osip_message_t *final_response;osip_message_init(&final_response);osip_message_set_version(final_response, "SIP/2.0");osip_message_set_status_code(final_response, 200);osip_message_set_reason_phrase(final_response, "OK");osip_via_t *final_via;osip_via_init(&final_via);final_via->version = osip_strdup("2.0");final_via->protocol = osip_strdup("UDP");final_via->host = osip_strdup("localhost");final_via->port = osip_strdup("5060");osip_list_add(&final_response->vias, final_via, -1);osip_message_set_call_id(final_response, request->callid->number);osip_message_set_cseq(final_response, request->cseq->number, request->cseq->method);osip_message_set_from(final_response, request->from->displayname, request->from->url->username, request->from->url->host);osip_message_set_to(final_response, request->to->displayname, request->to->url->username, request->to->url->host);osip_contact_t *contact;osip_contact_init(&contact);contact->url = osip_uri_clone(request->to->url);osip_list_add(&final_response->contacts, contact, -1);osip_message_to_str(final_response, &response_buffer, &response_length);printf("Sending 200 OK response:\n%.*s\n", (int)response_length, response_buffer);osip_transaction_send(transaction, final_response);osip_free(response_buffer);osip_message_free(final_response);// 创建对话osip_dialog_t *dialog;osip_dialog_init(&dialog);osip_dialog_init_as_uas(dialog, request, final_response);// 将对话添加到OSIP实例中osip_list_add(&transaction->config->osip_ist_transactions, dialog, -1);// 打印对话信息printf("Dialog created with Call-ID: %s\n", dialog->callid);
}// 回调函数:处理事务终止
void kill_transaction_cb(osip_event_t *event) {osip_transaction_t *transaction = event->transaction;printf("Transaction terminated: ID=%d\n", transaction->transactionid);// 清理对话if (transaction->ctx_type == OSIP_IST) {osip_ist_t *ist = (osip_ist_t *)transaction->ict_context;if (ist->dialog != NULL) {osip_dialog_free(ist->dialog);ist->dialog = NULL;}}
}// 回调函数:处理传输层错误
void transport_error_cb(int error_code, char *error_description) {printf("Transport error: Code=%d, Description=%s\n", error_code, error_description);
}// 发送消息的回调函数
int send_message_cb(osip_transaction_t* transaction, osip_message_t* message, char* host, int port, int flags, int other_flags) {char *message_buffer;size_t message_length;osip_message_to_str(message, &message_buffer, &message_length);printf("Sending message to %s:%d:\n%.*s\n", host, port, (int)message_length, message_buffer);osip_free(message_buffer);return 0; // 成功
}int main() {osip_t *osip;osip_init(&osip, OSIP_LOG_LEVEL_INFO);// 设置用户定义的上下文osip->application_context = NULL;// 设置消息回调函数osip_message_cb_t msg_callbacks[OSIP_MESSAGE_CALLBACK_COUNT];for (int i = 0; i < OSIP_MESSAGE_CALLBACK_COUNT; i++) {msg_callbacks[i] = NULL;}msg_callbacks[OSIP_INVITE] = handle_invite;osip->msg_callbacks = msg_callbacks;// 设置事务终止回调函数osip_kill_transaction_cb_t kill_callbacks[OSIP_KILL_CALLBACK_COUNT];for (int i = 0; i < OSIP_KILL_CALLBACK_COUNT; i++) {kill_callbacks[i] = NULL;}kill_callbacks[OSIP_ICT] = kill_transaction_cb;osip->kill_callbacks = kill_callbacks;// 设置传输层错误回调函数osip_transport_error_cb_t tp_error_callbacks[OSIP_TRANSPORT_ERROR_CALLBACK_COUNT];for (int i = 0; i < OSIP_TRANSPORT_ERROR_CALLBACK_COUNT; i++) {tp_error_callbacks[i] = NULL;}tp_error_callbacks[OSIP_TP_ERROR] = transport_error_cb;osip->tp_error_callbacks = tp_error_callbacks;// 设置发送消息的回调函数osip->cb_send_message = send_message_cb;// 模拟接收一个INVITE请求const char *invite_request_str ="INVITE sip:user@example.com SIP/2.0\r\n""Via: SIP/2.0/UDP localhost:5060;branch=z9hG4bK77ef4c2312983.1\r\n""Max-Forwards: 70\r\n""To: \"User\" <sip:user@example.com>\r\n""From: \"Caller\" <sip:caller@localhost>;tag=12345\r\n""Call-ID: unique-id@localhost\r\n""CSeq: 1 INVITE\r\n""Contact: <sip:user@localhost:5060>\r\n""Content-Length: 0\r\n\r\n";osip_message_t *invite_request;osip_message_init(&invite_request);osip_message_parse(invite_request, invite_request_str, strlen(invite_request_str));// 创建Invite Server Transaction (IST)osip_transaction_t *transaction;osip_ist_new(&transaction, invite_request, osip);// 初始化事件osip_event_t *event;osip_event_init(&event, OSIP_IST_NEW, transaction, invite_request, NULL, NULL);// 处理事件osip_handle_events(osip);// 清理资源osip_message_free(invite_request);osip_transaction_free(transaction);osip_shutdown(osip);return 0;
}
osip_message
结构体
osip_message_t结构体用于表示一个完整的SIP消息,包括请求和响应消息。它包含了所有必要的头部字段、消息体以及其他相关信息
主要字段:
- 版本信息 osip_version_t *version;
- 请求行(仅限请求消息) osip_request_line_t *reqline;
- 状态行(仅限响应消息) osip_status_line_t *statusline;
- 头部字段 osip_list_t headers;
- 消息体 char *body;
- 缓冲区(存储整个SIP消息的原始字符串表示, 通过解析buffer可以填充其他字段。) char *buffer;
- 长度 size_t length;
struct osip_message{int nm_nCh; //对应nAppChint nm_BId; //对应板卡的IDint sip_length;//this sip message lengthchar* sip_version; /**< SIP version (SIP request only) */osip_uri_t* req_uri; /**< Request-Uri (SIP request only) */char* sip_method; /**< METHOD (SIP request only) */int status_code; /**< Status Code (SIP answer only) */char* reason_phrase; /**< Reason Phrase (SIP answer only) */osip_list_t accepts; /**< Accept headers */osip_list_t accept_encodings; /**< Accept-Encoding headers */osip_list_t accept_languages; /**< Accept-Language headers */osip_list_t alert_infos; /**< Alert-Info headers */osip_list_t allows; /**< Allows headers */osip_list_t authentication_infos; /**< authentication_info headers */osip_list_t authorizations; /**< Authorizations headers */osip_call_id_t* call_id; /**< Call-ID header */osip_list_t call_infos; /**< Call-Infos header */osip_list_t contacts; /**< Contacts headers */osip_list_t content_encodings; /**< Content-Encodings headers */osip_content_length_t* content_length; /**< Content-Length header */osip_content_type_t* content_type; /**< Content-Type header */osip_cseq_t* cseq; /**< CSeq header */osip_list_t error_infos; /**< Error-Info headers */osip_from_t* from; /**< From header */osip_mime_version_t* mime_version; /**< Mime-Version header */osip_list_t proxy_authenticates; /**< Proxy-Authenticate headers */osip_list_t proxy_authentication_infos; /**< P-Authentication-Info headers */osip_list_t proxy_authorizations; /**< Proxy-authorization headers */osip_list_t record_routes; /**< Record-Route headers */osip_list_t routes; /**< Route headers */osip_to_t* to; /**< To header */osip_list_t vias; /**< Vias headers */osip_list_t www_authenticates; /**< WWW-Authenticate headers */osip_list_t headers; /**< Other headers */osip_list_t bodies; /**< List of attachements *//**1: structure and buffer "message" are identical.*2: buffer "message" is not up to date with the structure info* (call osip_message_to_str to update it).*/int message_property; /**@internal */char* message; /**@internal */size_t message_length; /**@internal */void* application_data; /**can be used by upper layer*//* +++ start +++ add by ykf problem:DS-45612 2016,04,29*/char remote_addr_from_iplayer[128]; //IP层获取的远端地址和端口,其实就是来源地址int remote_port_from_iplayer; /*+++ end +++ problem:DS-45612 2016,04,29*/unsigned long ulLocalSipUsedEthPort; //要发送该SIP消息的本地SIP信令端口 用于多SIP端口时发送IVT时确定socketint nNetNo; char recv_register_hostaddr[128]; //register消息的来源地址int recv_register_hostport; //register消息的来源地址的端口int ulids_addr; //ids 侵入检测源地址char bnoresource; //added by lqf for 资源不足需回复503char bNotSupportPayload; //added by lqf for 编码格式不支持回复415char recv_addr[128]; //sip消息的来源地址int recv_port; //sip消息的来源地址的端口//+++start+++ added by ykf for DS-50678 限制同一被叫号码外呼次数 2016.12.21int nCallFiledSipValue; //呼叫失败自定义SIP发送值//+++end+++ added by ykf for DS-50678 int nSipSessionExpiresEnable;
};
code
主要函数:
-
初始化和销毁
osip_message_init(osip_message_t **): 初始化一个osip_message_t对象。
osip_message_free(osip_message_t *): 释放一个osip_message_t对象及其占用的资源。 -
解析和构建
osip_message_parse(osip_message_t *, const char *, size_t): 解析一个SIP消息字符串buffer并填充osip_message_t对象。
osip_message_to_str(osip_message_t *, char **, size_t *): 将osip_message_t对象转换为SIP消息字符串buffer -
获取和设置头部字段
osip_message_get_header(const osip_message_t *, const char *, int): 获取指定名称的头部字段。
osip_message_set_header(osip_message_t *, const char *, const char *): 设置指定名称的头部字段。
osip_message_add_header(osip_message_t *, const char *, const char *): 添加一个新的头部字段 -
处理请求和响应
对于请求消息,可以通过osip_message_t->reqline访问请求行
对于响应消息,可以通过osip_message_t->statusline访问状态行
#include <osipparser2/osip_parser.h>
#include <osipparser2/osip_message.h>
#include <osipparser2/osip_header.h>
#include <stdio.h>
#include <stdlib.h>int main() {osip_message_t *sip_message;// 初始化SIP消息osip_message_init(&sip_message);// 定义一个SIP请求字符串const char *request_str ="INVITE sip:user@example.com SIP/2.0\r\n""Via: SIP/2.0/UDP localhost:5060;branch=z9hG4bK77ef4c2312983.1\r\n""Max-Forwards: 70\r\n""To: \"User\" <sip:user@example.com>\r\n""From: \"Caller\" <sip:caller@localhost>;tag=12345\r\n""Call-ID: unique-id@localhost\r\n""CSeq: 1 INVITE\r\n""Contact: <sip:user@localhost:5060>\r\n""Content-Length: 0\r\n\r\n";// 解析SIP请求字符串osip_message_parse(sip_message, request_str, strlen(request_str));// 打印解析后的SIP消息printf("Parsed SIP Request:\n%s\n", sip_message->buffer);// 访问请求行printf("Request Line: %s %s %s\n",sip_message->reqline->method,sip_message->reqline->uri,sip_message->reqline->version);// 访问头部字段printf("Via: %s\n", sip_message->topvia->host);printf("From: %s <%s>\n", sip_message->from->displayname, sip_message->from->url->uri);printf("To: %s <%s>\n", sip_message->to->displayname, sip_message->to->url->uri);printf("Call-ID: %s\n", sip_message->callid->number);printf("CSeq: %d %s\n", sip_message->cseq->seq_number, sip_message->cseq->method);printf("Contact: %s\n", sip_message->contact->url->uri);// 构建一个新的SIP响应消息osip_message_t *response;osip_message_init(&response);// 设置版本osip_version_t *version;osip_version_init(&version);version->major = 2;version->minor = 0;response->version = version;// 设置状态行osip_status_line_t *statusline;osip_status_line_init(&statusline);statusline->code = 200;statusline->reason_phrase = osip_strdup("OK");response->statusline = statusline;// 复制一些头部字段osip_via_t *via;osip_via_clone(sip_message->topvia, &via);osip_list_add(&response->vias, via, -1);osip_from_t *from;osip_from_clone(sip_message->from, &from);response->from = from;osip_to_t *to;osip_to_clone(sip_message->to, &to);response->to = to;osip_call_id_t *callid;osip_call_id_clone(sip_message->callid, &callid);response->callid = callid;osip_cseq_t *cseq;osip_cseq_clone(sip_message->cseq, &cseq);response->cseq = cseq;// 添加Contact头字段osip_contact_t *contact;osip_contact_init(&contact);contact->url = osip_uri_clone(sip_message->to->url);osip_list_add(&response->contacts, contact, -1);// 设置内容长度response->contentlength = osip_content_length_init();response->contentlength->value = osip_strdup("0");// 转换为字符串char *response_buffer;size_t response_length;osip_message_to_str(response, &response_buffer, &response_length);// 打印构建后的SIP响应printf("Constructed SIP Response:\n%.*s\n", (int)response_length, response_buffer);// 清理资源osip_message_free(sip_message);osip_message_free(response);osip_free(response_buffer);return 0;
}
osip_event
osip_event 结构体在OSIP(Open Source Implementation of SIP)库中用于表示SIP事件。这些事件可以是各种类型的网络事件、定时器事件或其他与SIP处理相关的事件
主要字段:
- type_t type 表示事件的类型。
- int transactionid 标识与该事件相关的SIP事务ID
- int sip_length 记录本SIP消息的长度。
- unsigned int uinttick 标记这个事件产生的系统时间戳。
- int socket 记录这个事件关联的套接字
- osip_message_t sip* 存储与事件相关的SIP消息
struct osip_event{type_t type; //Event Typeint transactionid; //identifier of the related osip transactionint sip_length;//本SIP消息的长度unsigned int uinttick; //标记这个event产生的时间,用于上层计算系统的繁忙程度int socket; //记录这个事件关联的socket。用于tcp和tls协议。osip_message_t* sip; //SIP message (optional)};
code 打印接收到的SIP消息
#include <osip2/osip.h>
#include <stdio.h>void handle_sip_event(osip_event_t *event) {switch (event->type) {case OSIP_MESSAGE_RECEIVED:printf("Received SIP Message:\n");osip_message_to_str(event->sip, NULL);printf("%s\n", event->sip->message);break;case OSIP_TIMEOUT_NORESPONSE:printf("Timeout for transaction ID %d\n", event->transactionid);break;default:printf("Unhandled event type: %d\n", event->type);break;}
}int main() {osip_event_t *event;// 初始化OSIP库if (osip_init(NULL) != 0) {fprintf(stderr, "Failed to initialize OSIP\n");return -1;}// 创建一个新的SIP事件event = osip_new_outgoing_sipmessage(OSIP_INVITE);if (event == NULL) {fprintf(stderr, "Failed to create a new SIP event\n");osip_free();return -1;}// 设置事件类型为接收消息event->type = OSIP_MESSAGE_RECEIVED;// 假设我们有一个SIP消息osip_message_t *sip_msg;if (osip_message_init(&sip_msg) != 0) {fprintf(stderr, "Failed to create a new SIP message\n");osip_free();return -1;}// 设置SIP方法为INVITEsip_msg->sip_method = osip_strdup("INVITE");// 设置请求URIif (osip_uri_parse(sip_msg->msg_osip, "sip:user@example.com", &sip_msg->req_uri) != 0) {fprintf(stderr, "Failed to parse request URI\n");osip_message_free(sip_msg);osip_free();return -1;}// 将SIP消息附加到事件event->sip = sip_msg;// 处理事件handle_sip_event(event);// 清理资源osip_message_free(sip_msg);osip_free();return 0;
}
相关文章:
SIP协议栈--osip源码梳理
文章目录 osiposip主体结构体code main函数 状态机转化结构体code状态转换 sip事务结构体code osip_dialog结构体code 创建并发送200 OK响应 osip_message结构体code osip_eventcode 打印接收到的SIP消息 osip OSIP(Open Source Implementation of SIP)…...

Python零基础入门到高手8.4节: 元组与列表的区别
目录 8.4.1 不可变数据类型 8.4.2 可变数据类型 8.4.3 元组与列表的区别 8.4.4 今天彩票没中奖 8.4.1 不可变数据类型 不可变数据类型是指不可以对该数据类型进行原地修改,即只读的数据类型。迄今为止学过的不可变数据类型有字符串,元组。 在使用[]…...

深度学习入门:深度学习(完结)
目录 1、加深网络1.1 向更深的网络出发1.2 进一步提高识别精度1.3 加深层的动机 2、深度学习的小历史2.1 ImageNet2.2 VGG2.3 GoogleNet2.4 ResNet 3、深度学习的高速化3.1 需要努力解决的问题3.2 基于GPU的高速化3.3 分布式学习3.4 运算精度的位数缩减 4、深度学习的应用案例4…...
OpenCV CUDA模块中矩阵操作------矩阵元素求和
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在OpenCV的CUDA模块中,矩阵元素求和类函数主要用于计算矩阵元素的总和、绝对值之和以及平方和。这些操作对于图像处理中的特征提取、…...

使用Scrapeless Scraping Browser的自动化和网页抓取最佳实践
引言:人工智能时代浏览器自动化和数据收集的新范式 随着生成性人工智能、人工智能代理和数据密集型应用程序的快速崛起,浏览器正在从传统的“用户互动工具”演变为智能系统的“数据执行引擎”。在这一新范式中,许多任务不再依赖单一的API端点…...

java数组题(5)
(1): 思路: 1.首先要对数组nums排序,这样两数之间的差距最小。 2.题目要求我们通过最多 k 次递增操作,使数组中某个元素的频数(出现次数)最大化。经过上面的排序,最大数…...
使用Thrust库实现异步操作与回调函数
文章目录 使用Thrust库实现异步操作与回调函数基本异步操作插入回调函数更复杂的回调示例注意事项 使用Thrust库实现异步操作与回调函数 在Thrust库中,你可以通过CUDA流(stream)来实现异步操作,并在适当的位置插入回调函数。以下是如何实现的详细说明&a…...

物联网无线传感方向专业词汇解释
涡旋电磁波(VEMW):一种具有轨道角动量的电磁波,其特性在于能够在传播过程中携带额外的相位信息,从而增加通信系统的容量和灵活性。波前:波动传播过程中,同一时刻振动相位相同的所有点构成的几何曲面,代表波…...

Maven 插件参数注入与Mojo开发详解
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…...
C++中void*知识详解和注意事项
一、void* 是什么? 在 C/C 中,void* 表示一个通用指针类型(generic pointer),可以指向任意类型的对象,但 不能直接解引用或进行算术运算,必须先进行类型转换。 void* ptr; // 可以指向任意类型…...

2024年全国青少年信息素养大赛——算法创意实践挑战赛复赛真题(小学组)——玫瑰花地的面积
2024年全国青少年信息素养大赛——算法创意实践挑战赛复赛真题(小学组)——玫瑰花地的面积 上面试卷可点下方,支持在线编程,在线测评~ 2024年全国信息素养大赛 算法创意实践挑战赛复赛(小学组)_c_少儿编程题库学习中心-嗨信奥 5月17号 全国青…...

【补充笔记】修复“NameError: name ‘ZhNormalizer‘ is not defined”的直接方法
#工作记录 一、问题描述 在运行CosyVoice_For_Windows项目时,出现以下报错: File "F:\PythonProjects\CosyVoice_For_Windows\cosyvoice\cli\frontend.py", line 74, in __init__ self.zh_tn_model ZhNormalizer(remove_erhuaFalse, fu…...

预训练模型实战手册:用BERT/GPT-2微调实现10倍效率提升,Hugging Face生态下的迁移学习全链路实践
更多AI大模型应用开发学习内容,尽在聚客AI学院。 一. 预训练模型(PTM)核心概念 1.1 什么是预训练模型? 预训练模型(Pre-trained Model, PTM)是在大规模通用数据上预先训练的模型,通过自监督学…...
并发笔记-给数据上锁(二)
文章目录 核心挑战 (The CRUX)29.1 并发计数器 (Concurrent Counters)1. 简单非并发计数器 (Figure 29.1)2. 同步计数器(单锁版本 - Coarse-Grained Lock, Figure 29.2)3. 可伸缩计数:近似/懒惰计数器 (Approximate/Sloppy Counter, Figure 2…...

mac docker弹窗提示Docker 启动没有响应
一、原因分析 这台笔记电脑是Mac M3操作系统,安装Docker之后,Docker应用程序一直启动不起来。 二、解决办法 sudo rm /Library/PrivilegedHelperTools/com.docker.vmnetd sudo cp /Applications/Docker.app/Contents/Library/LaunchServices/com.docker.vmnetd /Library/Pri…...
每日算法刷题计划Day7 5.15:leetcode滑动窗口4道题,用时1h
一.定长滑动窗口 【套路】教你解决定长滑窗!适用于所有定长滑窗题目! 模版套路 1.题目描述 1.计算所有长度恰好为 k 的子串中,最多可以包含多少个元音字母 2.找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。 3.…...
如何利用 Python 爬虫按关键字搜索京东商品:实战指南
在电商领域,京东作为国内知名的电商平台,拥有海量的商品数据。通过 Python 爬虫技术,我们可以高效地按关键字搜索京东商品,并获取其详细信息。这些信息对于市场分析、选品上架、库存管理和价格策略制定等方面具有重要价值。本文将…...

Ubuntu 22.04搭建OpenStreeMap地址解析服务(保姆级教程)
1.数据准备 1.1.全球数据 下载地址:https://planet.openstreetmap.org/ 1.2.特定区域的数据 下载地址:Geofabrik Download Server 2.安装必要的软件包 2.1.更新系统软件包 sudo apt updatesudo apt upgrade 2.2.安装所需要的软件包 执行下面的命…...

sqli—labs第五关——报错注入
一:判断输入类型 首先测试 ?id1 回显You are in... 渐进测试?id1 报错分析: 出现引号提示——“”,可能是字符型 继续测试?id1--(用注释符修复了语法错误) 回显You are in... 说明就是字符型 因为能用注释符…...

从海洋生物找灵感:造个机器人RoboPteropod,它能在水下干啥?
大家好!在如今人类对水下环境探索不断深入的时代,从水下考古到珊瑚礁考察,各种任务都离不开水下机器人的助力。但传统水下机器人尺寸较大,在狭窄的水下空间施展不开。今天,我们就来认识一款受海洋小生物启发而设计的仿…...

FastAPI系列16:从API文档到TypeScript 前端客户端(SDKs)
从API文档到TypeScript 前端客户端(SDKs) 快速入门生成一个TypeScript 客户端测试生成的TypeScript 客户端 API标签与客户端生成生成带有标签的 TypeScript 客户端 自定义Operation ID使用自定义Operation ID生成TypeScript客户端 在 FastAPI系列15&…...
为什么 Redis 设计为单线程?6.0 版本为何引入多线程?
Redis 6.0引入多线程的核心目的是优化网络I/O处理,通过分离I/O操作与命令执行,在保持数据一致性的前提下,充分利用多核CPU资源提升高并发场景下的性能,同时保持向后兼容性。以下是对Redis单线程设计与6.0版本引入多线程的详细解析…...
C# 使用HttpClient下载文件
本章讲述:如何在C#中使用HttpClient直接从阿里云OSS下载文件。 步骤1: 添加必要的命名空间 using System; using System.IO; using System.Net.Http; 步骤2: 创建下载方法 以下是使用HttpClient下载文件的示例代码: public class OssDownloader {//d…...

CS016-2-unity ecs
目录 【23】射击改进 【24】僵尸生成器 编辑【25】随机行走 【27】射击光效 【23】射击改进 a. 当距离目标太远的时候,要继续移动。而当距离目标到达攻击距离之后,则停止移动。 上图中的if:判断自身和目标的距离是否大于攻击距离&#…...

CST软件对OPERACST软件联合仿真汽车无线充电站对人体的影响
上海又收紧了新能源车的免费上牌政策。所以年前一些伙伴和我探讨过买新能源汽车的问题,小伙伴们基本纠结的点是买插电还是纯电?我个人是很抗拒新能源车的,也开过坐过。个人有几个观点: 溢价过高,不保值。实际并不环保…...

华为2024年报:鸿蒙生态正在取得历史性突破
华为于2025年03月31日发布2024年年度报告。报告显示,华为经营结果符合预期,实现全球销售收入 8,621 亿元人民币,净利润 626 亿元人民币。2024 年研发投入达到 1,797 亿元人民币,约占全年收入的 20.8%,近十年累计投入的…...
策略模式-枚举实现
策略模式的实现方法有很多,可以通过策略类if,else实现。下面是用枚举类实现策略模式的方法。 定义一个枚举类,枚举类有抽象方法,每个枚举都实现抽象方法。这个策略,实现方法是工具类的很实现,代码简单好理解 枚举实现…...
C++中多重继承下的虚表结构
在 C 的多重继承 中,虚表(vtable)结构会变得更加复杂。 一、基础回顾:单继承下的虚表结构 类中含有虚函数 → 编译器生成虚表(每类一张);每个对象有一个隐藏的虚表指针(vptr&#x…...

LabVIEW的CAN通讯测试程序
该程序是基于 NI LabVIEW 平台开发的 CAN(Controller Area Network,控制器局域网)通讯测试程序。主要功能是对 CAN 通讯过程进行模拟、数据传输与验证,确保 CAN 通讯的正常运行和数据的准确传输。 程序详细说明 接口选择ÿ…...

Spring Boot 使用Itext绘制并导出PDF
最终效果 其实可以加分页,但是没有那么精细的需求,所以我最后就没有加,有兴趣的可以尝试下。 项目依赖 <!-- Spring Boot 版本有点老 --> <spring-boot.version>2.3.12.RELEASE</spring-boot.version><!-- 依…...