一個基於 exosip 庫的 UAC 和 UAS 的程式碼整理
阿新 • • 發佈:2019-02-19
———– UAC 代理客戶端的程式碼整理 —————
/** * 一個使用了 osip 和 eXosip 庫的 UAC 代理客戶端的演示程式 * * - 只是簡單的演示了使用了 osip 和 eXosip2 庫的 UAC 代理客戶端的如下幾個功能: * * i 發起呼叫 INVITE 請求 * * h 結束通話會話 * * s 執行方法 INFO * * m 執行方法 MESSAGE * * 編 譯:g++ -I/usr/local/include -L/usr/local/lib ua_client.cpp -o ua_client -leXosip2 -losip2 -losipparser2 -lpthread * */ #include <osip2/osip_mt.h> #include <eXosip2/eXosip.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <iostream> #include <string> using namespace std; int main(int argc, char* argv[]) { eXosip_event_t *je; osip_message_t *reg = NULL; osip_message_t *invite = NULL; osip_message_t *ack = NULL; osip_message_t *info = NULL; osip_message_t *message = NULL; int call_id, dialog_id; int i,flag; int flag1 = 1; int id; string strIdentity = "sip:[email protected]"; string strRegisterer = "sip:133.37.55.136:5060"; // server ip string strSrcCall = "sip:[email protected]"; string strDestCall = "sip:[email protected]:5060"; // server ip char command; char tmp[4096]; char localip[128]; string strHelp = string("\n\t--> 命令字元 功能描述 <--\n\n") + "\t\tr 向伺服器註冊\n" + "\t\tc 取消註冊\n" + "\t\ti 發起呼叫請求\n" + "\t\th 結束通話\n" + "\t\tq 退出程式\n" + "\t\ts 執行方法 INFO\n" + "\t\tm 執行方法 MESSAGE\n" + "\t\te 幫助\n\n"; cout << strHelp; string strMsg; i = eXosip_init (); if (i != 0) { cout << "\t--> Couldn't initialize eXosip! <--\n"; return -1; } else { cout << "\t--> eXosip_init successfully! <-- \n\n"; } i = eXosip_listen_addr (IPPROTO_UDP, NULL, 5061, AF_INET, 0); if (i != 0) { eXosip_quit (); cerr << "\n\t--> Couldn't initialize transport layer! <-- \n\n"; return -1; } flag = 1; while (flag) { cout << "請輸入一個命令字元:\t"; cin >> command; switch (command) { case 'r': cout << "\n\t--> This modal isn't commpleted! \n" << endl; break; case 'i': // 初始化的 INVITE 請求 i = eXosip_call_build_initial_invite (&invite, strDestCall.c_str(), strSrcCall.c_str(), NULL, "This is a call for a conversation"); if (i != 0) { cout << "\n --> Intial INVITE failed! <-- \n"; break; } // 符合 SDP 格式, 其中屬性 a 是自定義格式,也就是說可以存放自己的資訊, // 但是隻能是兩列,比如帳戶資訊 // 但是經測試,格式: v o t必不可少,原因未知,估計是協議棧在傳輸時需要檢查的 strMsg = string("v=0\r\n") + "o=anonymous 0 0 IN IP4 0.0.0.0\r\n" + "t=1 10\r\n" + "a=username:bluesea\r\n" + "a=password:123456\r\n"; osip_message_set_body (invite, strMsg.c_str(), strMsg.length()); osip_message_set_content_type (invite, "application/sdp"); // 這裡使用了鎖機制以保證同步 eXosip_lock (); i = eXosip_call_send_initial_invite (invite); eXosip_unlock (); flag1 = 1; while (flag1) { je = eXosip_event_wait (0, 200); if (je == NULL) { cout << "\n\t--> No response or the time is over! <--\n" << endl; break; } switch (je->type) { case EXOSIP_CALL_INVITE: cout << "\n\t--> a new invite reveived! <--\n" << endl; break; // announce processing by a remote app case EXOSIP_CALL_PROCEEDING: cout << "\n\t--> proceeding! <--\n" << endl; break; // announce ringback case EXOSIP_CALL_RINGING: cout << "\n\t--> ringing! <--\n" << "\n\tcall_id is " << je->cid << ", dialog_id is " << je->did << endl; break; // 收到請求,表示連線成功,下面傳送回覆確認 case EXOSIP_CALL_ANSWERED: cout << "\n\t--> ok! connected! <--\n" << endl; call_id = je->cid; dialog_id = je->did; cout << "\n\tcall_id is " << je->cid << ", dialog_id is " << je->did << endl; eXosip_call_build_ack (je->did, &ack); eXosip_call_send_ack (je->did, ack); flag1 = 0; break; case EXOSIP_CALL_CLOSED: cout << "\n\t--> the other sid closed! <--\n" << endl; break; case EXOSIP_CALL_ACK: cout << "\n\t--> ACK received! <--\n" << endl; break; default: cout << "\n\t--> other response!\n" <<endl; break; } eXosip_event_free (je); } break; case 'h': cout << "\n\t--> Holded ! \n" << endl; eXosip_lock (); eXosip_call_terminate (call_id, dialog_id); eXosip_unlock (); break; case 'c': cout << "\n\t--> This modal isn't commpleted! \n" << endl; break; case 's': // 傳輸 INFO 方法 eXosip_call_build_info (dialog_id, &info); snprintf (tmp , 4096, "hello,bluesea"); osip_message_set_body (info, tmp, strlen(tmp)); // 格式可以任意設定, text/plain 代表文字資訊 osip_message_set_content_type (info, "text/plain"); eXosip_call_send_request (dialog_id, info); break; case 'm': // 傳輸 MESSAGE方法,也就是即時訊息, // 和 INFO 方法相比,主要區別,是 MESSAGE 不用建立連線,直接傳輸資訊, // 而 INFO 必須在建立 INVITE 的基礎上傳輸。 cout << "\n\t--> the mothed :MESSAGE \n" << endl; eXosip_message_build_request (&message, "MESSAGE", strDestCall.c_str(), strSrcCall.c_str(), NULL); strMsg = "message: hello bluesea!"; osip_message_set_body (message, strMsg.c_str(), strMsg.length()); // 假設格式是xml osip_message_set_content_type (message, "text/xml"); eXosip_message_send_request (message); break; case 'q': eXosip_quit (); cout << "\n\t--> Exit the setup! \n" << endl;; flag = 0; break; case 'e': cout << strHelp << endl; break; default: cout << "\n\t--> 不支援的命令 <--\n" << endl; break; } } return 0; }
———– UAS 代理伺服器端的程式碼整理 —————
/** * 一個使用了 osip 和 eXosip 庫的 UAS 代理服務端的演示程式 * * - 只是簡單的演示了使用了 osip 和 eXosip2 庫的 UAS 代理服務端的如下幾個功能: * * 編 譯:g++ -I/usr/local/include -L/usr/local/lib ua_server.cpp -o ua_server -leXosip2 -losip2 -losipparser2 -lpthread * */ #include <eXosip2/eXosip.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <iostream> #include <fstream> #include <string> using namespace std; int main() { eXosip_event_t *je = NULL; osip_message_t *ack = NULL; osip_message_t *invite = NULL; osip_message_t *answer = NULL; sdp_message_t *remote_sdp = NULL; int call_id, dialog_id; int i,j; int id; char *sour_call = "sip:[email protected]"; char *dest_call = "sip:[email protected]:5061"; //client ip/port char command; char tmp[4096]; char localip[128]; int pos = 0; // 初始化 sip i = eXosip_init (); if (i != 0) { cerr << "\n\t--> Can't initialize eXosip!\n"; return -1; } else { cout << "\n\t--> eXosip_init successfully!\n"; } i = eXosip_listen_addr (IPPROTO_UDP, NULL, 5060, AF_INET, 0); if (i != 0) { eXosip_quit (); cerr << "\n\t--> eXosip_listen_addr error! Couldn't initialize transport layer!\n"; } for(;;) { // 偵聽是否有訊息到來 je = eXosip_event_wait (0, 50); // 協議棧帶有此語句,具體作用未知 eXosip_lock (); eXosip_default_action (je); eXosip_automatic_refresh (); eXosip_unlock (); if (je == NULL) // 沒有接收到訊息,繼續 { continue; } switch (je->type) { case EXOSIP_MESSAGE_NEW: // 新的訊息到來 cout << "\n\t*** EXOSIP_MESSAGE_NEW!\n" << endl; if (MSG_IS_MESSAGE (je->request)) // 如果接收到的訊息型別是 MESSAGE { { osip_body_t *body; osip_message_get_body (je->request, 0, &body); cout << "I get the msg is: " << body->body << endl; } // 按照規則,需要回復 OK 資訊 eXosip_message_build_answer (je->tid, 200, &answer); eXosip_message_send_answer (je->tid, 200, answer); } break; case EXOSIP_CALL_INVITE: // INVITE 請求訊息 // 得到接收到訊息的具體資訊 cout << "\n\tReceived a INVITE msg from " << je->request->req_uri->host << " : " << je->request->req_uri->port << ", username is " << je->request->req_uri->username << endl; // 得到訊息體,認為該訊息就是 SDP 格式. remote_sdp = eXosip_get_remote_sdp (je->did); call_id = je->cid; dialog_id = je->did; eXosip_lock (); eXosip_call_send_answer (je->tid, 180, NULL); i = eXosip_call_build_answer (je->tid, 200, &answer); if (i != 0) { cout << "\n\t--> This request msg is invalid! Cann't response!\n" << endl; eXosip_call_send_answer (je->tid, 400, NULL); } else { snprintf (tmp, 4096, "v=0\r\n" "o=anonymous 0 0 IN IP4 0.0.0.0\r\n" "t=1 10\r\n" "a=username:rainfish\r\n" "a=password:123\r\n"); // 設定回覆的SDP訊息體,下一步計劃分析訊息體 // 沒有分析訊息體,直接回復原來的訊息,這一塊做的不好。 osip_message_set_body (answer, tmp, strlen(tmp)); osip_message_set_content_type (answer, "application/sdp"); eXosip_call_send_answer (je->tid, 200, answer); cout << "\n\t--> send 200 over!" << endl; } eXosip_unlock (); // 顯示出在 sdp 訊息體中的 attribute 的內容,裡面計劃存放我們的資訊 cout << "\n\t--> The INFO is :\n" ; while (!osip_list_eol ( &(remote_sdp->a_attributes), pos)) { sdp_attribute_t *at; //這裡解釋了為什麼在SDP訊息體中屬性a裡面存放必須是兩列 at = (sdp_attribute_t *) osip_list_get ( &remote_sdp->a_attributes, pos); cout << "\n\t" << at->a_att_field << " : " << at->a_att_value << endl; pos ++; } break; case EXOSIP_CALL_ACK: cout << "\n\t--> ACK recieved!\n" << endl; // printf ("the cid is %s, did is %s\n", je->did, je->cid); break; case EXOSIP_CALL_CLOSED: cout << "\n\t--> the remote hold the session!\n" << endl; // eXosip_call_build_ack(dialog_id, &ack); // eXosip_call_send_ack(dialog_id, ack); i = eXosip_call_build_answer (je->tid, 200, &answer); if (i != 0) { printf ("This request msg is invalid!Cann't response!\n"); eXosip_call_send_answer (je->tid, 400, NULL); } else { eXosip_call_send_answer (je->tid, 200, answer); cout << "\n\t--> bye send 200 over!\n"; } break; case EXOSIP_CALL_MESSAGE_NEW: cout << "\n\t*** EXOSIP_CALL_MESSAGE_NEW\n" << endl; if (MSG_IS_INFO(je->request) ) // 如果傳輸的是 INFO 方法 { eXosip_lock (); i = eXosip_call_build_answer (je->tid, 200, &answer); if (i == 0) { eXosip_call_send_answer (je->tid, 200, answer); } eXosip_unlock (); { osip_body_t *body; osip_message_get_body (je->request, 0, &body); cout << "the body is " << body->body << endl; } } break; default: cout << "\n\t--> Could not parse the msg!\n" << endl; } } return 0; }