C语音组播收发
C语音组播收发
- recv_multi
- CMakeLists.txt
- recv_multi_msg.c
- send_multi
- CMakeLists.txt
- send_multi_msg.c
- CMakeLists.txt
recv_multi
CMakeLists.txt
#version require
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 14)
#project name
set(PROJ recv_multi)
project(${PROJ})
#SET(CMAKE_BUILD_TYPE Release)
#SET(CMAKE_RELEASE_POSTFIX "_r")
#set(CMAKE_DEBUG_POSTFIX "_d")
#binary output pathes
SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin/)
#compiler flags
set(CMAKE_C_FLAGS " -Wall")
#include pathes
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/./inc/)
#source files
file(GLOB_RECURSE SRC ${CMAKE_CURRENT_SOURCE_DIR}/./*.c*)
#compile binary
add_executable(${PROJECT_NAME}
${SRC}
)
target_link_libraries(${PROJ} PRIVATE
-lpthread
-lgthread-2.0
)
#link libraries
target_link_libraries(${PROJ} PRIVATE)
recv_multi_msg.c
#include <stdatomic.h>
#include <pthread.h>
#include <stdio.h>
#include <stdint.h> // 包含 uint32_t 的定义
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// 全局退出标志
atomic_int exit_flag = ATOMIC_VAR_INIT(0);
struct addr_info
{
uint32_t _myip;
uint16_t _myport;
uint32_t _myMulIp;
};
struct addr_info gMyAddrInfo;
#define RTPUDPV4TRANS_MAXPACKSIZE 65535
#define RTPUDPV4TRANS_RTPTRANSMITBUFFER 32768
#define RTPSOCKLENTYPE unsigned int
pthread_t thTest;
//mySelect实现非阻塞
static int mySelect(int sockets, int8_t *readflags)
{
struct pollfd fds;
fds.fd = sockets;
fds.events = POLLIN;
fds.revents = 0;
*readflags = 0;
int status = poll(&fds, 1, 0);
if (status < 0)
{
if (errno == EINTR)
return 0;
return status;
}
if (status > 0)
{
if (fds.revents)
*readflags = 1;
}
return status;
}
void* testRev(void* argv) {
int sockfd;
struct sockaddr_in local_addr;
char packetbuffer[RTPUDPV4TRANS_MAXPACKSIZE] = {0};
// 创建UDP套接字
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 绑定到本地地址和端口
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(gMyAddrInfo._myport);
local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //或htonl(gMyAddrInfo._myMulIp);
if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
perror("bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
// 加入组播组
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = htonl(gMyAddrInfo._myMulIp);
mreq.imr_interface.s_addr = htonl(gMyAddrInfo._myip);
if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
perror("setsockopt IP_ADD_MEMBERSHIP failed");
close(sockfd);
exit(EXIT_FAILURE);
}
// 接收组播消息
while (!atomic_load(&exit_flag)) {
int8_t dataavailable = 0;
if (mySelect(sockfd, &dataavailable) < 0)
continue;
if (dataavailable)
{
socklen_t addr_len = sizeof(local_addr);
int n = recvfrom(sockfd, packetbuffer, RTPUDPV4TRANS_MAXPACKSIZE - 1, 0, (struct sockaddr *)&local_addr, &addr_len);
if (n > 0) {
printf("Received message: %s\n", packetbuffer);
}
}
// 延时 0.02 秒
struct timespec ts;
ts.tv_sec = 0; // 秒部分
ts.tv_nsec = 1e9*0.02; // 纳秒部分
nanosleep(&ts, NULL);
}
// 离开组播组
if (setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
perror("setsockopt IP_DROP_MEMBERSHIP failed");
}
close(sockfd);
return NULL;
}
void delRevGroupAddr()
{
// 设置退出标志
atomic_store(&exit_flag, 1);
// 等待线程退出
pthread_join(thTest, NULL);
}
void usage(const char *progname) {
fprintf(stderr, "Usage: %s <interface> <port> <multicast_address>\n", progname);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
// if (argc != 4) {
// usage(argv[0]);
// }
// gMyAddrInfo._myip = ntohl(inet_addr(argv[1])); //192.168.1.130
// gMyAddrInfo._myport = atoi(argv[2]);
// gMyAddrInfo._myMulIp = ntohl(inet_addr(argv[3])); //224.0.1.1
//gMyAddrInfo._myip = INADDR_ANY;//ntohl(inet_addr("192.168.1.130")); //192.168.1.130
gMyAddrInfo._myip = ntohl(inet_addr("192.168.1.130")); //192.168.1.130
gMyAddrInfo._myport = atoi("14000");
gMyAddrInfo._myMulIp = ntohl(inet_addr("224.0.1.1")); //224.0.1.1
atomic_store(&exit_flag, 0);
pthread_create(&thTest, NULL, testRev, NULL);
int exit = 0;
scanf("%d", &exit);
delRevGroupAddr();
return 0;
}
send_multi
CMakeLists.txt
#version require
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 14)
#project name
set(PROJ send_multi)
project(${PROJ})
#SET(CMAKE_BUILD_TYPE Release)
#SET(CMAKE_RELEASE_POSTFIX "_r")
#set(CMAKE_DEBUG_POSTFIX "_d")
#binary output pathes
SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin/)
#compiler flags
set(CMAKE_C_FLAGS " -Wall")
#include pathes
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/./inc/)
#source files
file(GLOB_RECURSE SRC ${CMAKE_CURRENT_SOURCE_DIR}/./*.c*)
#compile binary
add_executable(${PROJECT_NAME}
${SRC}
)
target_link_libraries(${PROJ} PRIVATE
-lpthread
-lgthread-2.0
)
#link libraries
target_link_libraries(${PROJ} PRIVATE)
send_multi_msg.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void usage(const char *progname) {
fprintf(stderr, "Usage: %s <multicast_address> <port> <message> <interval> <interface>\n", progname);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
if (argc != 6) {
usage(argv[0]);
}
const char *multicast_address = argv[1];
int port = atoi(argv[2]);
const char *message = argv[3];
int interval = atoi(argv[4]);
const char *interface = argv[5];
int sock;
struct sockaddr_in multicast_addr;
int msg_len = strlen(message);
// 创建套接字
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 绑定到指定的网卡接口
struct in_addr local_interface;
local_interface.s_addr = inet_addr(interface);
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&local_interface, sizeof(local_interface)) < 0) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
// 设置组播地址和端口
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET;
multicast_addr.sin_addr.s_addr = inet_addr(multicast_address);
multicast_addr.sin_port = htons(port);
while (1) {
// 发送组播消息
if (sendto(sock, message, msg_len, 0, (struct sockaddr *)&multicast_addr, sizeof(multicast_addr)) < 0) {
perror("sendto");
exit(EXIT_FAILURE);
}
printf("Sent: %s\n", message);
// 等待指定的间隔时间
sleep(interval);
}
// 关闭套接字
close(sock);
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(CS_LOCATION_HA)
# 包含子项目
add_subdirectory(recv_multi)
add_subdirectory(send_multi)
注释:
一:接收端:在组播通信中,接收端使用 bind 函数时,sin.sin_addr.s_addr 通常设置为 INADDR_ANY(即 0.0.0.0),而不是指定本机的具体 IP 地址。这是由组播通信的特性和网络协议栈的工作机制决定的。以下详细解释原因:
- 组播通信的特性
组播数据包是发送到一个组播组地址(例如 224.0.1.1),而不是发送到某个特定的主机 IP 地址。
组播数据包会被网络中的路由器或交换机转发到所有加入该组播组的主机。
接收端的主机需要监听组播地址,而不是监听自己的单播 IP 地址。 - 为什么不能指定本机 IP 地址
如果 sin.sin_addr.s_addr 设置为本机的具体 IP 地址(例如 192.168.1.130),那么套接字只会接收发送到该 IP 地址的数据包。
然而,组播数据包的目标地址是组播地址(例如 224.0.1.1),而不是本机的单播 IP 地址。因此,如果绑定到本机 IP 地址,套接字将无法接收到组播数据包。