socket应用实例之RTP音频流传输

套接字的特性由3个属性确定,它们分别是:域、端口号、协议类型。
(1)套接字的域
它指定套接字通信中使用的网络介质,最常见的套接字域有两种:
一是AF_INET,它指的是Internet网络。当客户使用套接字进行跨网络的连接时,它就需要用到服务器计算机的IP地址和端口来指定一台联网机器上的某个特定服务,所以在使用socket作为通信的终点,服务器应用程序必须在开始通信之前绑定一个端口,服务器在指定的端口等待客户的连接。
另一个域AF_UNIX,表示UNIX文件系统,它就是文件输入/输出,而它的地址就是文件名。
(2)套接字的端口号
每一个基于TCP/IP网络通讯的程序(进程)都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留Socket中的输入/输出信息,端口号是一个16位无符号整数,范围是0-65535,以区别主机上的每一个程序(端口号就像房屋中的房间号),低于256的端口号保留给标准应用程序,比如pop3的端口号就是110,每一个套接字都组合进了IP地址、端口,这样形成的整体就可以区别每一个套接字。
(3)套接字协议类型
因特网提供三种通信机制,
一是流套接字SOCK_STREAM:流套接字在域中通过TCP/IP连接实现,同时也是AF_UNIX中常用的套接字类型。流套接字提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送的机制。
二是数据报套接字SOCK_DGRAM:它不需要建立连接和维持一个连接,它们在域中通常是通过UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并一需要总是要建立和维持一个连接。
三是原始套接字:原始套接字允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW。

                                      
 

                                        

RTP音频流传输的实现:

发送端:

 
  1. #include <stdio.h>

  2. #include <unistd.h>

  3. #include <string.h>

  4. #include <sys/types.h>

  5. #include <netinet/in.h>

  6. #include <sys/socket.h>

  7. #include<arpa/inet.h>

  8.  
  9. #define TS_PACKET_SIZE 188

  10. #define MTU 1500

  11.  
  12.  
  13. struct rtp_header{

  14. unsigned char cc:4;

  15. unsigned char x:1;

  16. unsigned char p:1;

  17. unsigned char v:2;

  18.  
  19. unsigned char pt:7;

  20. unsigned char m:1;

  21.  
  22. unsigned short sequence_number;

  23. unsigned int timestamp;

  24. unsigned int ssrc;

  25. };

  26.  
  27. void init_rtp_header(struct rtp_header *h){

  28. h->v = 2;

  29. h->p = 0;

  30. h->x = 0;

  31. h->cc = 0;

  32. h->m = 0;

  33. h->pt = 33;

  34. h->sequence_number =0;

  35. h->timestamp = 123;

  36. h->ssrc =0;

  37. }

  38.  
  39.  
  40. void sequence_number_increase(struct rtp_header *header){

  41. unsigned short sequence = ntohs(header->sequence_number);

  42. sequence++;

  43. header->sequence_number = htons(sequence);

  44. }

  45.  
  46.  
  47.  
  48. int main(int argc,char *argv[]){

  49. char buf[MTU];

  50. unsigned int count = 0;

  51. if(argc < 2){

  52. printf(" need wav path \n");

  53. return -1;

  54. }

  55.  
  56. // Init RTP Header

  57. init_rtp_header((struct rtp_header*)buf);

  58. count = sizeof(struct rtp_header);

  59.  
  60. // Init socket

  61. int sock = socket(AF_INET, SOCK_DGRAM, 0);

  62. struct sockaddr_in dest_addr;

  63.  
  64. dest_addr.sin_family=AF_INET;

  65. dest_addr.sin_port = htons(5003);

  66. dest_addr.sin_addr.s_addr =inet_addr("10.46.169.189");

  67.  
  68. // dest_addr.sin_addr.s_addr =INADDR_ANY;

  69. bzero(&(dest_addr.sin_zero),8);

  70.  
  71. // Open TS file

  72. FILE *ts_file = fopen(argv[1], "r+");

  73. char h[44];

  74. fread(h,1,44,ts_file);

  75. int n=0;

  76. while(!feof(ts_file)){

  77. int read_len = fread(buf+count, 1, TS_PACKET_SIZE, ts_file);

  78. // if(*(buf+count) != 0x47){

  79. // fprintf(stderr, "Bad sync header!\n");

  80. // continue;

  81. // }

  82. count += read_len;

  83.  
  84. // printf("count = %d\n",count);

  85. if (count + TS_PACKET_SIZE > MTU){// We should send

  86. printf("haha_count = %d\n",count);

  87. sequence_number_increase((struct rtp_header*)buf);

  88. sendto(sock, buf, count, 0, (const struct sockaddr*)&dest_addr, sizeof(dest_addr));

  89. count = sizeof(struct rtp_header);

  90. usleep(10000);

  91. n++;

  92. }

  93. }

  94. printf("numofcount:%d\n",n*1316);

  95.  
  96. fclose(ts_file);

  97. }

接收端:

 
  1. #include <stdlib.h>

  2. #include <stdio.h>

  3. #include <unistd.h>

  4. #include <fcntl.h>

  5. #include <sys/stat.h>

  6. #include <sys/types.h>

  7. #include <dirent.h>

  8. #include <sys/file.h>

  9. #include <errno.h>

  10. #include <netdb.h>

  11. #include <netinet/in.h>

  12. #include <arpa/inet.h>

  13. #include <sys/ioctl.h>

  14. #include <sys/time.h>

  15. #include <string.h>

  16. #include <stdarg.h>

  17. #include <sys/ipc.h>

  18. #include <sys/sem.h>

  19. #include <signal.h>

  20. #include <sys/wait.h>

  21. #include <regex.h>

  22.  
  23. struct rtp_header{

  24. unsigned char cc:4;

  25. unsigned char x:1;

  26. unsigned char p:1;

  27. unsigned char v:2;

  28. unsigned char pt:7;

  29. unsigned char m:1;

  30. unsigned short sequence_number;

  31. unsigned int timestamp;

  32. unsigned int ssrc;

  33. };

  34.  
  35. void main(int argc, char **argv)

  36. {

  37.  
  38. int socka;

  39. int nPortA = 8000;

  40. fd_set rfd; // 读描述符集

  41. struct timeval timeout; // 定时变量

  42. struct sockaddr_in addr; // 告诉sock 应该在什么地方licence

  43. char recv_buf[1500]; // 接收缓冲区

  44.  
  45. int nRecLen; // 客户端地址长度!!!!!!

  46.  
  47. struct sockaddr_in cli; // 客户端地址

  48. int nRet; // select返回值

  49.  
  50. socka = socket(AF_INET, SOCK_DGRAM, 0); // 创建数据报socka

  51. if (socka == -1)

  52. {

  53. printf("socket()/n");

  54. return;

  55. }

  56.  
  57. memset(&addr, 0, sizeof(addr));

  58.  
  59. addr.sin_family = AF_INET; // IP协议

  60. addr.sin_port = htons(nPortA); // 端口

  61. addr.sin_addr.s_addr = htonl(INADDR_ANY); // 在本机的所有ip上开始监听

  62. if (bind(socka, (struct sockaddr*)&addr, sizeof(addr)) == -1)// bind socka

  63. {

  64. printf("bind()/n");

  65. return;

  66. }

  67.  
  68.  
  69. // 设置超时时间为6s

  70. timeout.tv_sec = 6;

  71. timeout.tv_usec = 0;

  72.  
  73. memset(recv_buf, 0, sizeof(recv_buf)); // 清空接收缓冲区

  74. while (1)

  75. {

  76. FD_ZERO(&rfd); // 在使用之前总是要清空

  77.  
  78. // 开始使用select

  79. FD_SET(socka, &rfd); // 把socka放入要测试的描述符集中

  80.  
  81.  
  82. nRet = select(socka+1, &rfd, NULL, NULL, &timeout);// 检测是否有套接口是否可读

  83. if (nRet == -1)

  84. {

  85. printf("select()\n");

  86. return;

  87. }

  88. else if (nRet == 0) // 超时

  89. {

  90. //printf("timeout\n");

  91. continue;

  92. }

  93. else // 检测到有套接口可读

  94. {

  95. if (FD_ISSET(socka, &rfd)) // socka可读

  96. {

  97. nRecLen = sizeof(cli);

  98. int nRecEcho = recvfrom(socka, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&cli, &nRecLen);

  99. if (nRecEcho == -1)

  100. {

  101. printf("recvfrom()\n");

  102. break;

  103. }

  104.  
  105. struct rtp_header *p = (struct rtp_header *)recv_buf;

  106. printf("v = %d\n",p->v);

  107. printf("p = %d\n",p->p);

  108. printf("x = %d\n",p->x);

  109. printf("cc = %d\n",p->cc);

  110. printf("sequence_number = %d\n",ntohs(p->sequence_number));

  111. printf("ssrc = %d\n",ntohs(p->ssrc));

  112. }

  113.  
  114. }

  115. }

  116. }

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值