录制UDP组播数据到文件(2)

来源:互联网 发布:支持微信提现赚钱软件 编辑:程序博客网 时间:2024/05/14 10:38
3.2. 初始化UDP组播数据接收
1  int init_udp(UDPContext **pp_udpctx, 
2        char *host, char *addr, int port)
3  {
4    UDPContext *p_ctx = NULL;
5    int  error;
6    int  ret;
7    char *err_msg;
8
9    struct addrinfo hints, *res = 0;
10   char pc_port[16];
11   const char *service = "0";
12   int udp_fd = -1;
13   int length;
14   int tmp;
15
16   p_ctx = udp_mallocz(sizeof(UDPContext));
17   if (!pp_udpctx)
18   {
19     err_msg = strerror(errno);
20     printf("ERR_MSG: av_mallocz,  %s\n", err_msg);
21     return FAILED;
22   }
23   (*pp_udpctx) = p_ctx;
24
25   p_ctx->ttl = 16;
26   p_ctx->pkt_buf_size = UDP_MAX_PKT_SIZE;
27   p_ctx->cir_buf_size = MAX_CIR_BUF_SIZE;
28
29   strcpy(p_ctx->host, host);
30   strcpy(p_ctx->addr, addr);
31   p_ctx->port = port;
32
33   if (port > 0)
34   {
35     snprintf(pc_port, sizeof(pc_port), "%d",port);
36     service = pc_port;
37   }
38   memset(&hints, 0, sizeof(hints));
39   hints.ai_socktype = SOCK_DGRAM;
40   hints.ai_family   = AF_UNSPEC;
41   hints.ai_flags    = 0;
42   if ((error = getaddrinfo(addr, service, &hints, &res)))
43   {
44     res = NULL;
45     err_msg = strerror(errno);
46     printf("ERR_MSG: getaddrinfo, %s\n", err_msg);
47     return FAILED;
48   }
49   memcpy(&p_ctx->udp_addr,res->ai_addr, res->ai_addrlen);
50   p_ctx->udp_addr_len = res->ai_addrlen;
51
52   p_ctx->is_udp_multicast = 
53     IN_MULTICAST(ntohl(((struct sockaddr_in *)&(p_ctx->udp_addr))->sin_addr.s_addr));
54
55   if (!(p_ctx->is_udp_multicast))
56   {
57     printf("ERR_MSG: Address %s:%d is not udp-multicast\n",addr, port);
58     return FAILED;
59   }
60
61   /* create socket  */
62   memset(&hints, 0, sizeof(hints));
63   hints.ai_socktype = SOCK_DGRAM;
64   hints.ai_family   = AF_UNSPEC;
65   hints.ai_flags    = AI_PASSIVE;
66   if ((error = getaddrinfo(0, service, &hints, &res)))
67   {
68     res = NULL;
69     err_msg = strerror(errno);
70     printf("ERR_MSG: getaddrinfo, %s\n", err_msg);
71     return FAILED;
72   }
73   memcpy(&p_ctx->my_addr,res->ai_addr, res->ai_addrlen);
74   p_ctx->my_addr_len = res->ai_addrlen;
75
76   udp_fd = socket(res->ai_family, SOCK_DGRAM, 0);
77   if (udp_fd < 0)
78     goto fail;
79
80   if (p_ctx->reuse_socket || p_ctx->is_udp_multicast)
81   {
82     p_ctx->reuse_socket = 1;
83     if (setsockopt(udp_fd, SOL_SOCKET, SO_REUSEADDR, &(p_ctx->reuse_socket),
84             sizeof(p_ctx->reuse_socket)) != 0)
85       goto fail;
86   }
87
88   if (p_ctx->is_udp_multicast)
89   {
90     ret = bind(udp_fd, (struct sockaddr*)&(p_ctx->udp_addr), res->ai_addrlen); 
91   }
92
93   length = sizeof(p_ctx->my_addr);
94   getsockname(udp_fd, (struct sockaddr*)&(p_ctx->my_addr), &length);
95
96   /* join udp-multicast group */
97   if (p_ctx->is_udp_multicast)
98   {
99     struct ip_mreq mreq;
100    if (((struct sockaddr*)&(p_ctx->udp_addr))->sa_family == AF_INET)
101    {
102      mreq.imr_multiaddr.s_addr = 
103        (((struct sockaddr_in*)&(p_ctx->udp_addr))->sin_addr.s_addr);
104      mreq.imr_interface.s_addr = inet_addr(host);
105    }
106
107    if (setsockopt(udp_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,(const void*)&mreq,
108        sizeof(mreq)) < 0)
109    {
110      err_msg = strerror(errno);
111      printf("ERR_MSG: setsockopt(IP_ADD_MEMBERSHIP), %s\n", err_msg);
112      return FAILED; 
113    }
114  }
115
116  tmp = p_ctx->pkt_buf_size;
117  if (setsockopt(udp_fd, SOL_SOCKET, SO_RCVBUF, &tmp, sizeof(tmp)) < 0)
118  {
119    err_msg = strerror(errno);
120    printf("ERR_MSG: setsockopt(IP_ADD_MEMBERSHIP), %s\n", err_msg);
121    return FAILED; 
122  }
123
124  fcntl(udp_fd, F_SETFL, fcntl(udp_fd, F_GETFL) | O_NONBLOCK);
125  p_ctx->fd = udp_fd;
126
127  /* 申请FIFO,并启动接收子线程 */
128  p_ctx->p_fifo = udp_fifo_buf_alloc(p_ctx->cir_buf_size);
129  if (pthread_create(&(p_ctx->thread_cir_buf), NULL, udp_fifo_buf_circular_read, p_ctx))
130  {
131    printf("pthread_create failed.\n");
132    goto fail;
133  }
134
135  return SUCCEED;
136
137fail:
138  if (p_ctx->fd >= 0)
139    close(p_ctx->fd);
140
141  udp_fifo_buf_free(p_ctx->p_fifo);
142  udp_free(p_ctx);
143
144  return FAILED;
145}

Line4~14:
变量声明;

Line16~23:
分配UDP上下文数据结构的存储空间;

Line25~31:
依据参数进行初始化;

Line9:
struct addrinfo结构体的定义如下:
struct addrinfo {
     int ai_flags;             /* customize behavior */
     int ai_family;            /* address family */
     int ai_socktype;          /* socket type */
     int ai_protocol;          /* protocol */
     socklen_t ai_addrlen;     /* length in bytes of address */
     struct sockaddr *ai_addr; /* address */
     char *ai_canonname;       /* canonical name of host */
     struct addrinfo *ai_next; /* next in list */
     .
     .
     .
   };
ai_family指定地址族,可取值如下: 
AF_INET              2            IPv4 
AF_INET6            23            IPv6 
AF_UNSPEC        0            协议无关


ai_socktype指定套接字的类型 
SOCK_STREAM       1            流 
SOCK_DGRAM        2            数据报
在AF_INET通信域中套接字类型SOCK_STREAM的默认协议是TCP(传输控制协议)
在AF_INET通信域中套接字类型SOCK_DGRAM的默认协议是UDP(用户数据报协议)


ai_protocol指定协议类型。可取的值取决于ai_address和ai_socktype的值
取值                       值             说明
IPPROTO_IP          0              IP协议
IPPROTO_IPV4      4              IPv4
IPPROTO_IPV6      41             IPv6
IPPROTO_UDP       17             UDP
IPPROTO_TCP       6              TCP


ai_flags指定了如何来处理地址和名字,可取值如下:
取值                         值             说明
AI_PASSIVE              1              被动的,用于bind,通常用于server socket
AI_CANONNAME       2
AI_NUMERICHOST    4              地址为数字串
ai_flags值的说明
AI_NUMERICHOST    AI_CANONNAME   AI_PASSIVE
         0/1                                    0/1            0/1
如上表所示,ai_flagsde值的范围为0~7,取决于程序如何设置3个标志位,
比如设置ai_flags为“AI_PASSIVE|AI_CANONNAME”,ai_flags值就为3。
三个参数的含义分别为:
(1)AI_PASSIVE当此标志置位时,
   表示调用者将在bind()函数调用中使用返回的地址结构。
   当此标志不置位时,表示将在connect()函数调用中使用。
   当节点名为NULL,且此标志置位,  则返回的地址将是通配地址。
   如果节点名NULL,且此标志不置位,则返回的地址将是回环地址。
(2)AI_CANNONAME当此标志置位时,
   在函数所返回的第一个addrinfo结构中的ai_cannoname成员中,
   应该包含一个以空字符结尾的字符串,字符串的内容是节点名的正规名。
(3)AI_NUMERICHOST当此标志置位时
   此标志表示调用中的节点名必须是一个数字地址字符串

ai_protocol:
取值              值             说明
IPPROTO_IP        0              IP协议
IPPROTO_IPV4      4              IPv4
IPPROTO_IPV6      41             IPv6
IPPROTO_UDP       17             UDP
IPPROTO_TCP       6              TCP

Line33~50,62~74
getaddrinfo函数
将一个主机名字和服务名字映射到一个地址。

它能够处理名字到地址以及服务到端口这两种转换,
返回的是一个sockaddr结构的链表而不是一个地址清单。
这些sockaddr结构随后可由套接口函数直接使用。如此一来,
getaddrinfo函数把协议相关性安全隐藏在这个库函数内部。

定义及需要的头文件如下:
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *restrict hostname,
                const char *restrict service,
                const struct addrinfo *restrict hint,
                struct addrinfo **restrict result);

void freeaddrinfo(struct addrinfo *ai);


参数说明
hostname: 一个主机名或者地址串(IPv4的点分十进制串或者IPv6的16进制串)
service:  服务名可以是十进制的端口号,也可以是已定义的服务名称,如ftp、http等
hints:    可以是一个空指针,也可以是一个指向某个addrinfo结构体的指针,
          调用者在这个结构中填入关于期望返回的信息类型的暗示。
          举例来说:
            如果指定的服务既支持TCP也支持UDP,
            那么调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM,
            使得返回的仅仅是适用于数据报套接口的信息。
result:   本函数通过result指针参数返回一个指向addrinfo结构体链表的指针。
返回值:
0——成功,非0——出错


实际常用设置
一般情况下,client/server编程中,
  server端调用bind(如果面向连接的还需要listen),
  client则不用调用bind函数,解析地址后直接connect(面向连接)或直接发送数据(无连接)。
因此,比较常见的情况有
(1) 通常服务器端在调用getaddrinfo之前,
      ai_flags设置AI_PASSIVE,用于bind;
      主机名nodename通常会设置为NULL,返回通配地址[::]。
(2) 客户端调用getaddrinfo时,
      ai_flags一般不设置AI_PASSIVE,
      但是主机名nodename和服务名servname(更愿意称之为端口)则应该不为空。
(3) 当然,即使不设置AI_PASSIVE,取出的地址也并非不可以被bind,
      很多程序中ai_flags直接设置为0,即3个标志位都不设置,
      这种情况下只要hostname和servname设置的没有问题就可以正确bind。

Line52~53:
ntohl()函数:将一个无符号长整形数从网络字节顺序转换为主机字节顺序。
根据internet NIC关于IP地址的规定,IP地址共分为A-E 共5类,
  A-C类 目前应用的普通IP地址,
  E 类地址保留为将来使用,
  D 类地址即为组播地址,其网络号为固定的1110(第0~3位),
    第4~31位定义了某一特殊的组播地址,范围为 224.0.0.0~239.255.255.255,共有228个约27亿个地址.
所以用:
#define IN_MULTICAST(a) ((((uint32_t)(a)) & 0xf0000000) == 0xe0000000)
来判断输入的地址是否为组播地址。

Line76:
创建本机用来接收组播数据的套接字。
socket函数:
头文件
#include <sys/types.h>
#include <sys/socket.h>
函数原型:
 int socket(int domain, int type, int protocol);
参数说明:
第一个参数指定应用程序使用的通信协议的协议族,
  对于TCP/IP协议族,该参数置AF_INET;
第二个参数指定要创建的套接字类型,
  流套接字类型为SOCK_STREAM、
  数据报套接字类型为SOCK_DGRAM、
  原始套接字SOCK_RAW(WinSock接口并不适用某种特定的协议去封装它,而是由程序自行处理数据包以及协议首部);
第三个参数指定应用程序所使用的通信协议。
  此参数可以指定单个协议系列中的不同传输协议。
  在Internet通讯域中,此参数一般取值为0,系统会根据套接字的类型决定应使用的传输层协议。
返回结果:
该函数如果调用成功就返回新创建的套接字的描述符,
如果失败就返回INVALID_SOCKET。(Linux下失败返回-1)
套接字描述符是一个整数类型的值。
每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。
该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,
因此根据套接字描述符就可以找到其对应的套接字数据结构。
每个进程在自己的进程空间里都有一个套接字描述符表, 但是套接字数据结构都是在操作系统的内核缓冲里。


Line80~86, 94, 116~122:
设置套接字udp_fd的选项,继续重用该socket。
具体含义可见:
http://blog.chinaunix.net/uid-26000296-id-3667810.html

Line88~91:
将UPD组播地址与本机的套接口udp_fd捆绑。
本函数适用于未连接的数据报或流类套接口,在connect()或listen()调用前使用。
当用socket()创建套接口后,它便存在于一个名字空间(地址族)中,但并未赋名。
bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)。

应用程序可在bind()后用getsockname()来获知所分配的地址,
但必需注意的是,getsockname()只有在套接口连接成功后才会填写Internet地址,
这是由于在多种主机环境下若干种Internet地址都是有效的.


Line96~114:
组播的编程 
组播的程序设计使用setsockopt()函数和getsockopt()函数来实现,组播的选项是IP层的其选项值和含义参见11.5所示。
表11.5 多播相关的选项:
getsockopt()/setsockopt()的选项                   含 义 
IP_MULTICAST_TTL                                 设置多播组数据的TTL值 
IP_ADD_MEMBERSHIP                                在指定接口上加入组播组 
IP_DROP_MEMBERSHIP                               退出组播组 
IP_MULTICAST_IF                                  获取默认接口或设置接口 
IP_MULTICAST_LOOP                                禁止组播数据回送 

1. 选项IP_MULTICASE_TTL 
   选项IP_MULTICAST_TTL允许设置超时TTL, 范围为0255之间的任何值.
   例如:
      unsigned char ttl=255; 
      setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl)); 

2. 选项IP_MULTICAST_IF 
选项IP_MULTICAST_IF用于设置组播的默认网络接口, 会从给定的网络接口发送,另一个网络接口会忽略此数据。
例如:
   struct in_addr addr; 
   setsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,&addr,sizeof(addr)); 
参数addr是希望多播输出接口的IP地址,使用INADDR_ANY地址回送到默认接口。 
默认情况下,当本机发送组播数据到某个网络接口时,在IP层,数据会回送到本地的回环接口
3. 选项IP_MULTICAST_LOOP用于控制数据是否回送到本地的回环接口。
例如:
unsigned char loop; 
setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop)); 
参数loop设置为0禁止回送,设置为1允许回送。

4. 选项IP_ADD_MEMBERSHIP和IP_DROP_MEMBERSHIP 加入或者退出一个组播组。
通过选项IP_ADD_MEMBERSHIP和IP_DROP_MEMBERSHIP,对一个结构struct ip_mreq类型的变量进行控制。
struct ip_mreq原型如下:
struct ip_mreq  { 
 struct in_addr imn_multiaddr; /*加入或者退出的广播组IP地址*/ 
 struct in_addr imr_interface; /*加入或者退出的网络接口IP地址*/ 
}; 
选项IP_ADD_MEMBERSHIP用于加入某个广播组,之后就可以向这个广播组发送数据或者从广播组接收数据。
此选项的值为mreq结构,
成员imn_multiaddr是需要加入的广播组IP地址,
成员imr_interface是本机需要加入广播组的网络接口IP地址。
例如:
struct ip_mreq mreq; setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)); 

Line124:
针对(文件)描述符提供控制.
函数类型

定义函数 int fcntl(int fd, int cmd); 
  int fcntl(int fd, int cmd, long arg); 
  int fcntl(int fd, int cmd, struct flock *lock);


参数fd  代表欲设置的文件描述词。
参数cmd 代表打算操作的指令。
有以下几种情况:
F_DUPFD 用来查找大于或等于参数arg的最小且仍未使用的文件描述词,并且复制参数fd的文件描述词。
        执行成功则返回新复制的文件描述词。
        新描述符与fd共享同一文件表项,但是新描述符有它自己的一套文件描述符标志,
        其中FD_CLOEXEC文件描述符标志被清除。请参考dup2()。
F_GETFD 取得close-on-exec旗标。若此旗标的FD_CLOEXEC位为0,
        代表在调用exec()相关函数时文件将不会关闭。
F_SETFD 设置close-on-exec 旗标。
        该旗标以参数arg 的FD_CLOEXEC位决定。
F_GETFL 取得文件描述词状态旗标,此旗标为open()的参数flags。
F_SETFL 设置文件描述词状态旗标,参数arg为新旗标,
        但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。
F_GETLK 取得文件锁定的状态。
F_SETLK 设置文件锁定的状态。
        此时flcok 结构的l_type 值必须是F_RDLCK、F_WRLCK或F_UNLCK。
        如果无法建立锁定,则返回-1,错误代码为EACCES 或EAGAIN。
F_SETLKW F_SETLK 作用相同,
        但是无法建立锁定时,此调用会一直等到锁定动作成功为止。
        若在等待锁定的过程中被信号中断时,会立即返回-1,错误代码为EINTR。
参数lock指针为flock 结构指针,
定义如下
struct flock
{
  short int l_type;
  short int l_whence;
  off_t l_start;
  off_t l_len;
  pid_t l_pid;
};


l_type 有三种状态:
F_RDLCK 建立一个供读取用的锁定
F_WRLCK 建立一个供写入用的锁定
F_UNLCK 删除之前建立的锁定


l_whence 也有三种方式:
SEEK_SET 以文件开头为锁定的起始位置。
SEEK_CUR 以目前文件读写位置为锁定的起始位置
SEEK_END 以文件结尾为锁定的起始位置。
返回值 成功则返回0,若有错误则返回-1,错误原因存于errno.

功能介绍
fcntl()用来操作文件描述符的一些特性。fcntl 不仅可以施加建议性锁,还可以施加强制锁。
同时,fcntl还能对文件的某一记录进行上锁,也就是记录锁。

3.3 接收UDP组播数据到环形FIFO中
1  void *udp_fifo_buf_circular_read(void *ptr)
2  {
3    UDPContext *p_ctx = ptr;
4    fd_set readfds;
5    struct timeval time_val;
6
7    for (;;)
8    {
9      int left, len;
10     int ret;
11
12     FD_ZERO(&readfds);
13     FD_SET(p_ctx->fd, &readfds);
14
15     time_val.tv_sec = 1;
16     time_val.tv_usec= 0;
17     ret = select(p_ctx->fd+1, &readfds, NULL, NULL, &time_val);
18     if (ret < 0)
19     {
20       if (udp_neterrno() == AVERROR(EINTR))
21         continue;
22
23       p_ctx->cir_buf_err = EIO;
24       return NULL;
25     }
26
27     if (!(ret>0 && FD_ISSET(p_ctx->fd, &readfds)))
28       continue;
29
30     left = udp_fifo_buf_space(p_ctx->p_fifo);
31     left = UDPMIN(left, (p_ctx->p_fifo->ptr_tail - p_ctx->p_fifo->ptr_write));
32
33     if (!left)
34     {
35       printf("circular buffer: OVERRUN\n");
36       p_ctx->cir_buf_err = EIO;
37       return NULL;
38     }
39
40     len = recv(p_ctx->fd, p_ctx->p_fifo->ptr_write, left, 0);
41     //printf("RECV : %d bytes\n", len);
42
43     if (len < 0)
44     {
45       if (udp_neterrno() != AVERROR(EAGAIN) && 
46           udp_neterrno() != AVERROR(EINTR))
47       {
48         p_ctx->cir_buf_err = EIO;
49         return NULL;
50       }
51     }
52
53     p_ctx->p_fifo->ptr_write += len;
54     if (p_ctx->p_fifo->ptr_write >= p_ctx->p_fifo->ptr_tail)
55     {
56       p_ctx->p_fifo->ptr_write = p_ctx->p_fifo->buffer;
57     }
58     p_ctx->p_fifo->idx_write += len;
59   }
60
61   return NULL;
62 }
Line17:
确定一个或多个套接口的状态,如需要则等待。
int select( int nfds, 
            fd_set FAR* readfds, 
            fd_set FAR* writefds, 
            fd_set FAR* exceptfds, 
            const struct timeval FAR* timeout);
nfds:       是一个整数值,是指集合中所有文件描述符的范围,
             即所有文件描述符的最大值加1,不能错!
readfds:  (可选)指针,指向一组等待可读性检查的套接口。
writefds: (可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针,指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对阻塞操作则为NULL。

注释
本函数用于确定一个或多个套接口的状态,
对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息,
用fd_set结构来表示一组等待检查的套接口,
在调用返回时,这个结构存有满足一定条件的套接口组的子集,
并且select()返回满足条件的套接口的数目。
有一组宏可用于对fd_set的操作,这些宏与Berkeley Unix软件中的兼容,但内部的表达是完全不同的。


readfds参数 标识等待可读性检查的套接口。
  如果该套接口正处于监听listen()状态,则若有连接请求到达,
  该套接口便被标识为可读,这样一个accept()调用保证可以无阻塞完成,
  对其他套接口而言,可读性意味着有排队数据供读取。
  或者对于SOCK_STREAM类型套接口来说,
  相对于该套接口的虚套接口已关闭,
  于是recv()或recvfrom()操作均能无阻塞完成,writefds参数标识等待可写性检查的套接口。
  如果一个套接口正在connect()连接(非阻塞),可写性意味着连接顺利建立。
  如果套接口并未处于connect()调用中,可写性意味着send()和sendto()调用将无阻塞完成。
〔但并未指出这个保证在多长时间内有效,特别是在多线程环境中〕。


exceptfds参数 标识等待带外数据存在性或意味错误条件检查的套接口,
  请注意如果设置了SO_OOBINLINE选项为假FALSE,则只能用这种方法来检查带外数据的存在与否,
  对于SO_STREAM类型套接口,远端造成的连接中止和KEEPALIVE错误都将被作为意味出错。
  如果套接口正在进行连接connect()(非阻塞方式),则连接试图的失败将会表现在exceptfds参数中。


如果对readfds、writefds或exceptfds中任一个组类不感兴趣,可将它置为空NULL。


timeout参数控制select完成的时间。
  若timeout参数为空指针,则select将一直阻塞到有一个描述字满足条件,
  否则的话,timeout指向一个timeval结构,其中指定了select调用在返回前等待多长时间。
  如果timeval为{0,0},则select立即返回,这可用于探询所选套接口的状态,
  如果处于这种状态,则select调用可认为是非阻塞的,且一切适用于非阻塞调用的假设都适用于它,




返回值
select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;如果超时则返回0;
否则的话,返回SOCKET_ERROR错误,
当返回位-1时,所有描述符集清0。
当返回为0时,超时不修改任何描述符集。
当返回为非0时,在3个描述符集里,依旧是1的位就是准备好的描述符。
这也就是为什么,每次用select后都要用FD_ISSET的原因。

3.4. 从FIFO中读取数据到buf
1  int read_udp(UDPContext *p_ctx, char *p_buf, int size)
2  {
3    int ret, avail;
4    fd_set readfds;
5    struct timeval time_val;
6  
7    if (p_ctx->p_fifo)
8    {
9      do {
10       avail = udp_fifo_buf_size(p_ctx->p_fifo);
11
12       if (avail)
13       {
14         size = UDPMIN(avail, size);
15         udp_fifo_buf_generic_read(p_ctx->p_fifo, p_buf, size);
16         return size;
17       }
18       else
19       {
20         FD_ZERO(&readfds);
21         FD_SET(p_ctx->fd, &readfds);
22
23         time_val.tv_sec = 1;
24         time_val.tv_usec= 0;
25         ret = select(p_ctx->fd+1, &readfds, NULL, NULL, &time_val);
26
27         if (ret < 0)
28           return ret;
29       }
30
31     }while (1);
32   }
33
34   return 0;
35 }


1  int udp_fifo_buf_generic_read(UDPFifoBuf *f, void *dst, int buf_size)
2  {
3    do{
4      int len = UDPMIN((f->ptr_tail - f->ptr_read), buf_size);
5
6      memcpy(dst, f->ptr_read, len);
7      dst = (uint8_t*)dst + len;
8
9      udp_fifo_buf_drain(f, len);
10     buf_size -= len;
11   }while (buf_size > 0);
12
13   return 0;
14 }


3.5 关闭UDP组播数据接收并回收资源
1  int close_udp(UDPContext **pp_ctx, char *host)
2  {
3    if (((struct sockaddr*)&((*pp_ctx)->udp_addr))->sa_family == AF_INET)
4    {
5      struct ip_mreq mreq;

7      mreq.imr_multiaddr.s_addr = 
8        ((struct sockaddr_in*)&((*pp_ctx)->udp_addr))->sin_addr.s_addr;
9      mreq.imr_interface.s_addr = inet_addr(host);
10
11     if (setsockopt((*pp_ctx)->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
12             (const void*)&mreq, sizeof(mreq)) < 0)
13     {
14       printf("setsockopt(IP_DROP_MEMBERSHIP): %s\n", strerror(errno));
15       return -1;
16     }
17   }
18  
19   close((*pp_ctx)->fd);
20   udp_fifo_buf_free((*pp_ctx)->p_fifo);
21   udp_free(*pp_ctx);
22
23   return 0;
24 }


四、makefile程序
#
# \File
#   makefile
# \Descript
#   Build RecUdp2File
#
OBJECTS = main.o udp.o
CC = gcc
CFLAGS = -D_REENTRANT -lpthread -g -Wall 


RecUpd2File : $(OBJECTS)
  $(CC) $(CFLAGS) -o RecUdp2File $(OBJECTS)


main.o: udp.h
udp.o: udp.h


.PHONY: clean
clean:
  rm RecUdp2File $(OBJECTS)


完整的代码开以在这里下载:
http://download.csdn.net/detail/fireroll/5348914