IPv6与IPv4的UDP组播地址转换
这个东西可能对某些老师有用处,作者
交大饮水水源BBS ID:
rogerfd (Roger·璇) 共上站 1344 次 网龄792天 [巨蟹座]
上 次 在: [2009年03月20日08:59:33 星期五] 从 [211.144.112.64] 到本站一游。
目前在线:[讯息器:(打开) 呼叫器:(打开)] 生命力:[364] 文章:[297] 信箱:[ ]
Roger的小程序系列(19)IPv6与IPv4的UDP组播地址转换
工作中有时候需要把一路或者多路IPv6的UDP组播转换为IPv4的组播;有时候则相反,要把IPv4的转换为IPv6,于是写了一个小工具来统一完成这类的事情,Roger把它叫做IPv5,哈哈。
IPv6的组播接收可以参考Roger之前的文章《Roger的小程序系列(2)IPv6组播工具》,发送也是比较简单的,每收到一个包就用sendto发出去就可以了。需要注意的事情是,这类的转发通常都是使用不同的网卡,一收一发,所以接收和发送都必须指明网络接口和IP地址。IPv6的接收和发送必须绑定IPv6的地址,IPv4的接受和发送则必须绑定IPv4的地址,不能混用。
所以首先就是要遍历本机的所有网络接口和网络地址,这事情依赖API GetAdaptersAddresses,大致的代码是这样的
ULONG uLen = 0;
m_pInfo = NULL;
//得到该结构的大小
GetAdaptersAddresses (AF_UNSPEC, 0, 0, m_pInfo, &uLen);
if (uLen <= 0)
{
return FALSE;
}
m_pInfo = (IP_ADAPTER_ADDRESSES*) malloc (uLen);
if (!m_pInfo)
{
return FALSE;
}
if(ERROR_SUCCESS != GetAdaptersAddresses (AF_UNSPEC, 0, 0, m_pInfo, &uLen))
{
free(m_pInfo);
return FALSE;
}
m_pInfo是 IP_ADAPTER_ADDRESSES 类型的指针,所有的网卡信息构成一个链表,下面这段代码来获取所有网卡的描述信息
void CNewDlg::FillInf(CComboBox* pCombo)
{
IP_ADAPTER_ADDRESSES *q = NULL;
pCombo->ResetContent ();
for(q = m_pInfo; q ; q = q->Next)
{
CString str = q->Description;
pCombo->AddString (str);
}
return;
}
以下的代码来遍历一个网络接口的所有地址,我们只关心单播的UnicastAddress
void CNewDlg::FillAddr(int nIndex, CComboBox* pCombo)
{
IP_ADAPTER_ADDRESSES *q = NULL;
int i = 0;
pCombo->ResetContent ();
char addr[256];
for(q = m_pInfo; q; q = q->Next, ++i)
{
if (i == nIndex)
{
for (PIP_ADAPTER_UNICAST_ADDRESS pip = q->FirstUnicastAddress; pip ;
pip = pip->Next)
{
if(!getnameinfo(pip->Address.lpSockaddr,pip->Address.iSockaddrLength,
addr, sizeof addr,
0,0,NI_NUMERICHOST ))
{
pCombo->AddString (addr);
}
}
break;
}
}
return;
}
转换使用后台线程来干活,每个转换搞一个线程,转换的参数和状态信息放在一个全局变量里边。
/* stdafx.h */
enum ipv5_status
{
status_stop = 0,
status_starting,
status_running,
status_error,
status_stopping,
};
typedef struct _Convert_Info
{
char src_mc_addr [64];
char src_mc_port [8];
char src_inf_name[256];
char src_inf_addr[64];
char dst_mc_addr [64];
char dst_mc_port [8];
char dst_inf_name[256];
char dst_inf_addr[64];
bool cut_rtp_header;
bool is_rtp;
int send_ttl;
ipv5_status status;
SOCKET recv_socket;
SOCKET send_socket;
HANDLE hThread;
double recvd_packets;
double recvd_bytes;
double err_packets;
double packet_rate;
double bit_rate;
double err_rate;
}Convert_Info;
#define MAX_THREAD 16
/* ipv5.cpp */
Convert_Info g_arryInfo[MAX_THREAD];
/* WorkTHread.cpp */
#include “StdAfx.h”
#include “ipv5.h”
bool IsV6 (const char* addr)
{
return (strstr (addr, “::”) != NULL);
}
DWORD WINAPI ThreadProc (LPVOID Index)
{
ADDRINFO s1,*s2,s3,*s4;
ADDRINFO s5,*s6,s7,*s8;
WSABUF stWSABuf;
SOCKADDR_IN6 addrFrom;
DWORD dwIndex, dwTime1, dwTime2, nRet, dwParam, cbRet, iLen, iLost;
USHORT index, oldindex = 0;
char buf[64*1024];
double last_second_packets = 0, last_second_err_packets = 0, last_second_bytes = 0;
dwIndex = (DWORD) Index;
g_arryInfo[dwIndex].status = status_starting;
bool bFirstPkt;
int ttl = g_arryInfo[dwIndex].send_ttl;
bool SrcV6 = IsV6 (g_arryInfo[dwIndex].src_inf_addr);
bool DstV6 = IsV6 (g_arryInfo[dwIndex].dst_inf_addr);
memset (&s1, 0, sizeof(s1));
s1.ai_family = SrcV6 ? AF_INET6 : AF_INET;
s1.ai_socktype = SOCK_DGRAM;
s1.ai_protocol = IPPROTO_UDP;
s1.ai_flags = AI_NUMERICHOST;
nRet = getaddrinfo (g_arryInfo[dwIndex].src_mc_addr,
g_arryInfo[dwIndex].src_mc_port,
&s1,
&s2);
if (nRet != 0)
{
goto end;
}
memset (&s3, 0, sizeof(s3));
s3.ai_family = SrcV6 ? AF_INET6 : AF_INET;
s3.ai_socktype = SOCK_DGRAM;
s3.ai_protocol = IPPROTO_UDP;
s3.ai_flags = AI_PASSIVE;
nRet = getaddrinfo (g_arryInfo[dwIndex].src_inf_addr,
g_arryInfo[dwIndex].src_mc_port,
&s3,
&s4);
if (nRet != 0)
{
goto end;
}
g_arryInfo[dwIndex].recv_socket =
WSASocket ( SrcV6 ? AF_INET6 : AF_INET,
SOCK_DGRAM,
IPPROTO_UDP,
(LPWSAPROTOCOL_INFO)NULL,
0,
WSA_FLAG_OVERLAPPED |
WSA_FLAG_MULTIPOINT_C_LEAF |
WSA_FLAG_MULTIPOINT_D_LEAF);
if (g_arryInfo[dwIndex].recv_socket == INVALID_SOCKET)
{
goto end;
}
dwParam = TRUE;
nRet = setsockopt (
g_arryInfo[dwIndex].recv_socket,
SOL_SOCKET,
SO_REUSEADDR,
(char *)&dwParam,
sizeof (dwParam));
if (nRet == SOCKET_ERROR)
{
goto end;
}
if (SOCKET_ERROR == bind (g_arryInfo[dwIndex].recv_socket,
s4->ai_addr,
s4->ai_addrlen))
{
goto end;
}
dwParam = TRUE;
nRet = WSAIoctl (g_arryInfo[dwIndex].recv_socket,
SIO_MULTIPOINT_LOOPBACK,
&dwParam,
sizeof (dwParam),
NULL,
0,
&cbRet,
NULL,
NULL);
if (nRet)
{
goto end;
}
g_arryInfo[dwIndex].recv_socket =
WSAJoinLeaf (g_arryInfo[dwIndex].recv_socket, s2->ai_addr,
s2->ai_addrlen,
NULL,
NULL,
NULL,
NULL,
JL_RECEIVER_ONLY);
if (g_arryInfo[dwIndex].recv_socket == INVALID_SOCKET)
{
goto end;
}
dwParam = 2000;
if(SOCKET_ERROR == setsockopt (g_arryInfo[dwIndex].recv_socket,
SOL_SOCKET, SO_RCVTIMEO, (const char*)&dwParam,sizeof(dwParam)))
{
goto end;
}
dwParam = FALSE; //TRUE is Non-Blocking
nRet = WSAIoctl (g_arryInfo[dwIndex].recv_socket,
FIONBIO,
&dwParam,
sizeof dwParam,
NULL,
0,
&cbRet,
0,
0);
dwParam = 256*1024;
nRet = setsockopt (g_arryInfo[dwIndex].recv_socket,
SOL_SOCKET,
SO_RCVBUF,
(char *)&dwParam,
sizeof (dwParam));
if (nRet == SOCKET_ERROR)
{
goto end;
}
memset (&s5, 0, sizeof(s5));
s5.ai_family = DstV6 ? AF_INET6 : AF_INET;
s5.ai_socktype = SOCK_DGRAM;
s5.ai_protocol = IPPROTO_UDP;
s5.ai_flags = AI_NUMERICHOST;
nRet = getaddrinfo (g_arryInfo[dwIndex].dst_mc_addr,
g_arryInfo[dwIndex].dst_mc_port,
&s5,
&s6);
if (nRet != 0)
{
goto end;
}
memset (&s7, 0, sizeof(s7));
s7.ai_family = DstV6 ? AF_INET6 : AF_INET;
s7.ai_socktype = SOCK_DGRAM;
s7.ai_protocol = IPPROTO_UDP;
s7.ai_flags = AI_PASSIVE;
nRet = getaddrinfo (g_arryInfo[dwIndex].dst_inf_addr,
g_arryInfo[dwIndex].dst_mc_port,
&s7,
&s8);
if (nRet != 0)
{
goto end;
}
g_arryInfo[dwIndex].send_socket = socket (DstV6 ? AF_INET6 : AF_INET,
SOCK_DGRAM,
IPPROTO_UDP);
if (g_arryInfo[dwIndex].send_socket == INVALID_SOCKET)
{
goto end;
}
if (SOCKET_ERROR == setsockopt (g_arryInfo[dwIndex].send_socket,
DstV6 ? IPPROTO_IPV6 : IPPROTO_IP,
IP_MULTICAST_TTL,
(const char*)&ttl,
sizeof(int) ) )
{
int i = WSAGetLastError();
goto end;
}
if (SOCKET_ERROR == bind (g_arryInfo[dwIndex].send_socket,
s8->ai_addr,
s8->ai_addrlen))
{
goto end;
}
dwTime1 = GetTickCount();
bFirstPkt = TRUE;
iLost = 0;
g_arryInfo[dwIndex].is_rtp = false;
g_arryInfo[dwIndex].status = status_running;
g_arryInfo[dwIndex].err_packets = g_arryInfo[dwIndex].recvd_packets = g_arryInfo[dwIndex].recvd_bytes = 0;
g_arryInfo[dwIndex].bit_rate = g_arryInfo[dwIndex].err_rate = g_arryInfo[dwIndex].packet_rate = 0;
while (g_arryInfo[dwIndex].status == status_running)
{
stWSABuf.buf = buf;
stWSABuf.len = sizeof buf;
cbRet = 0;
dwParam = 0;
iLen = sizeof (addrFrom);
nRet = WSARecvFrom (g_arryInfo[dwIndex].recv_socket,
&stWSABuf,
1,
&cbRet,
&dwParam,
(SOCKADDR*)&addrFrom,
(int*)&iLen,
NULL,
NULL);
if (nRet == SOCKET_ERROR )
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
continue;
}
else
{
break;
}
}
if (bFirstPkt)
{
if (cbRet >= 188*3 &&
stWSABuf.buf[0] == 0×47 &&
stWSABuf.buf[188] == 0×47 &&
stWSABuf.buf[188*2] == 0×47)
{
g_arryInfo[dwIndex].is_rtp = false;
}
else
{
g_arryInfo[dwIndex].is_rtp = true;
}
}
g_arryInfo[dwIndex].recvd_packets += 1;
if(g_arryInfo[dwIndex].is_rtp && cbRet < 12)
{
g_arryInfo[dwIndex].err_packets += 1;
continue;
}
if (g_arryInfo[dwIndex].is_rtp)
{
//统计丢包
index = ntohs (*((USHORT*)(stWSABuf.buf+2)));
if (++oldindex != index)
{
TRACE (”RTP Error! Excepet %d but got %d\n”, oldindex, index);
if (!bFirstPkt)
{
iLost = (index-oldindex) > 0 ? index-oldindex : oldindex-index;
g_arryInfo[dwIndex].err_packets += iLost;
}
oldindex = index;
}
}
if (g_arryInfo[dwIndex].cut_rtp_header && g_arryInfo[dwIndex].is_rtp)
{
if (SOCKET_ERROR ==
sendto (g_arryInfo[dwIndex].send_socket,
buf + 12,
cbRet - 12,
0,
(const struct sockaddr FAR *)s6->ai_addr,
s6->ai_addrlen))
{
goto end;
}
}
else
{
if (SOCKET_ERROR ==
sendto (g_arryInfo[dwIndex].send_socket,
buf,
cbRet,
0,
(const struct sockaddr FAR *)s6->ai_addr,
s6->ai_addrlen))
{
goto end;
}
}
if(bFirstPkt)
{
bFirstPkt = FALSE;
}
g_arryInfo[dwIndex].recvd_bytes += cbRet;
dwTime2 = GetTickCount();
if(dwTime2 - dwTime1 >= 1000)
{
dwTime1 = dwTime2;
g_arryInfo[dwIndex].bit_rate = (g_arryInfo[dwIndex].recvd_bytes -
last_second_bytes) / 128;
last_second_bytes = g_arryInfo[dwIndex].recvd_bytes;
g_arryInfo[dwIndex].packet_rate = g_arryInfo[dwIndex].recvd_packets -
last_second_packets;
last_second_packets = g_arryInfo[dwIndex].recvd_packets;
if (g_arryInfo[dwIndex].packet_rate > 1)
{
g_arryInfo[dwIndex].err_rate = 100.0* (g_arryInfo[dwIndex].err_packets -
last_second_err_packets) / g_arryInfo[dwIndex].packet_rate;
}
else
{
g_arryInfo[dwIndex].err_rate = 0.0;
}
last_second_err_packets = g_arryInfo[dwIndex].err_packets;
}
}
end:
if (g_arryInfo[dwIndex].status != status_stopping)
{
// error exit
g_arryInfo[dwIndex].status = status_error;
}
else
{
g_arryInfo[dwIndex].status = status_stop;
}
g_arryInfo[dwIndex].err_packets = g_arryInfo[dwIndex].recvd_packets = g_arryInfo[dwIndex].recvd_bytes = 0;
g_arryInfo[dwIndex].bit_rate = g_arryInfo[dwIndex].err_rate = g_arryInfo[dwIndex].packet_rate = 0;
closesocket (g_arryInfo[dwIndex].recv_socket);
closesocket (g_arryInfo[dwIndex].send_socket);
return 0;
}
运行的样子
完整代码就不给出来了,这里有可执行程序的下载: ipv5
————————————————————————
作者: roger
Blog: http://rogerfd.cn
Email:roger99707@163.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

