UDP to UDP 数据转发

目的
希望读者在阅读本文之前,已经读过了《『黑客编程』一、TCP to TCP 数据转发》,UDP to UDP可以用作QQ通过转发代理聊天的功能。
应用机器A,提供UDP端口的服务,转发代理机器B,提供UDP转发端口服务,客户机C通过UDP把数据发送到B,B再转发给A,并把A发送的数据转发
给C。

设计
//主程序
Main()
{
beginthread(u2uMainThread());
}

//主线程
u2uMainThread(Param)
{
while(true)
{
创建"本地接收数据SOCK",用来接收并转发客户端C发送过来的数据
while(read>0)
{
等待并接收客户端C数据
等到后,查找"目标SOCK"
if(没有找到)
{
创建一个新的"目标SOCK"
"目标SOCK"创建成功后,创建"Wait Thread",
}
用"目标SOCK"发送数据到应用A
}
}
}
//Wait 线程
Wait Thread(Param)
{
创建"目标SOCK"->"连接SOCK"数据转发Thread
等待这个线程结束
释放SOCK资源
}
//TCP2TCP数据转发线程
UDP2UDP Thread(Param)
{
SOCK FROM,TO
while(read >0)
{
readfrom(From,Buff);
Sendto(To,Buff);
}
}

参考代码
参数结构
struct AGENTTCPUDPGATEWAYINFO
{
char TargetIP[40];
int TargetPort;
int LocalPort;
int LocalTmpPort;
};

//主线程
DWORD WINAPI u2umainthread(LPVOID lpvoid)
{
//获取参数
AGENTTCPUDPGATEWAYINFO *pasi = (AGENTTCPUDPGATEWAYINFO*)lpvoid;
AGENTTCPUDPGATEWAYINFO asi;
memcpy(&asi,pasi,sizeof(AGENTTCPUDPGATEWAYINFO));
delete pasi; pasi = NULL;
//-b

int iRet;
struct sockaddr_in UDPLocal,Target;
SOCKET s[2];//s[0]=>UDP socket s[1]=>TCP socket
HANDLE hThread[2]={NULL,NULL};

printf("\nOK!Work mode is udp2udp.");

//监听本地UDP port的地址结构
UDPLocal.sin_family=AF_INET;
UDPLocal.sin_addr.s_addr=INADDR_ANY;
UDPLocal.sin_port=htons(asi.LocalPort);
//目标地址结构
Target.sin_family=AF_INET;
Target.sin_addr.s_addr=inet_addr(asi.TargetIP);
Target.sin_port=htons(asi.TargetPort);
//开始循环
while(1)
{
printf("\n\n************Udp2Udp Start new**************\n\n");
_DelSock(s[0]);
//创建一个UDP socket
s[0]=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(s[0]==INVALID_SOCKET)
{
ShowError("\nUdp2Udp:Create UDP socket");
Sleep(5000);
continue;
}
_AddSock(s[0]);
//bind UDP socket
iRet=bind(s[0],(struct sockaddr *)&UDPLocal,sizeof(UDPLocal));
if(iRet == SOCKET_ERROR)
{
printf("\nUdp2Udp:Bind UDP port %d failed.",asi.LocalPort);
Sleep(5000);
continue;
}
else
printf("\nUdp2Udp:Bind UDP port %d ok.",asi.LocalPort);

// DWORD threadid=GetCurrentThreadId();
struct sockaddr_in from;
int dwSize=sizeof(from);
char incomingbuff[maxsize];
int read = 1;
while(read >0)
{
read=recvfrom(s[0],incomingbuff,maxsize,0,(SOCKADDR *)&from,&dwSize);
if(read <=0 ) break;
s[1] = udpsvrGetSock1(from) ;
if(s[1] == 0)
{
//创建一个UDP socket
s[1]=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(s[1]==INVALID_SOCKET)
{
ShowError("\nTcp2Udp:Create UDP socket");
Sleep(5000);
continue;
}
BOOL bindok = FALSE;
int LocalTmpPort = asi.LocalTmpPort;
if(LocalTmpPort<1) LocalTmpPort = 4000;
while(LocalTmpPort <65535)
{
//监听本地UDP port的地址结构
UDPLocal.sin_family=AF_INET;
UDPLocal.sin_addr.s_addr=INADDR_ANY;
UDPLocal.sin_port=htons(LocalTmpPort);
//bind UDP socket
iRet=bind(s[1],(struct sockaddr *)&UDPLocal,sizeof(UDPLocal));
if(iRet == SOCKET_ERROR)
{
printf("\nTcp2Udp:Bind UDP port %d failed.",LocalTmpPort);
LocalTmpPort ++ ;
Sleep(50);
continue;
}
else
{
printf("\nTcp2Udp:Bind UDP port %d ok.",LocalTmpPort);
bindok = TRUE;
break;
}
}
if(!bindok)
{
_DelSock(s[0]);
_DelSock(s[1]);
Sleep(5000);
continue;
}

DATAREDIRDPARAM* ptdataredird = new DATAREDIRDPARAM;
ptdataredird->sock[0] = s[0];//udp
ptdataredird->sock[1] = s[1];//udp
memcpy(&ptdataredird->sinudp ,&from,sizeof(sockaddr_in));
ptdataredird->sinudpisfilled = TRUE;
udpsvrAddDataRedird(ptdataredird);
DATAREDIRDPARAM* ptdataredird2 = new DATAREDIRDPARAM;
memcpy(ptdataredird2,ptdataredird,sizeof(DATAREDIRDPARAM));
DWORD threadid;
HANDLE htcp2udpThread = CreateThread(NULL,0,u2uwaitthread,(LPVOID)ptdataredird2,NULL,&threadid);
CloseHandle(htcp2udpThread);
}
printf("\ntUdp2Tcp: recvfrom %s:%d %d bytes.",inet_ntoa(from.sin_addr),ntohs(from.sin_port),read);
int iUDPRepeat = 3;
int npos=0;
while(npos < read)
{
int nsendcount=sendto(s[1],incomingbuff+npos,read-npos,0,(SOCKADDR *)&Target,sizeof(Target));
if(nsendcount == SOCKET_ERROR)
{
ShowError("sendto");
//重复发送次数自减一
if((iUDPRepeat--)>0)
{
printf("\nUDP2UDP Try %d times to send.",iUDPRepeat);
continue;
}
}
if(nsendcount <= 0 ) return 0;
printf("\nUDP2UDP:sendto %s:%d %d bytes.",inet_ntoa(Target.sin_addr),ntohs(Target.sin_port),nsendcount);
npos += nsendcount;
}
}
udpsvrDelAllDataRedird(s[0]);
printf("\n\n**************OK!Udp2Tcp do next******************\n\n");
}//end of while
return 0;
}
//监护线程
DWORD WINAPI u2uwaitthread(LPVOID lpvoid)
{
DATAREDIRDPARAM* pdrp = (DATAREDIRDPARAM*)lpvoid;
HANDLE hThread;
DWORD id;
hThread = CreateThread(NULL,0,udp2udp,(LPVOID)pdrp,NULL,&id);
_AddThread(hThread);
DWORD waiter;
waiter = WaitForSingleObject(hThread,INFINITE);
udpsvrDelDataRedird(pdrp->sock[1]);
_DelThread(hThread);
printf("\nUdp2Tcp: Socket closed.\n");
delete pdrp;
ExitThread(0);
return 0;
}
//
//UDP socket数据转发函数,从s[0]接收数据,转发到s[1]
//
DWORD WINAPI udp2udp(LPVOID lpvoid)
{
DATAREDIRDPARAM *tudp = (DATAREDIRDPARAM*)lpvoid;
SOCKET *s = tudp->sock;

DWORD threadid=GetCurrentThreadId();
printf("\n***New UDP2UDP Thread %d recvfrom %d sendto %d.\n",threadid,s[1],s[0]);

struct sockaddr_in from;
int dwSize=sizeof(from);
int iUDPRepeat=3;//UDP发送失败后重复的次数

char incomingbuff[maxsize];
int read = 1;
while(read >0)
{
read=recvfrom(s[1],incomingbuff,maxsize,0,(SOCKADDR *)&from,&dwSize);
if(read <=0 ) break;
printf("\nUDP2UDP[%d] recvfrom %s:%d %d bytes.",threadid,inet_ntoa(from.sin_addr),ntohs(from.sin_port),read);
iUDPRepeat = 3;
int npos=0;
while(npos < read)
{
int nsendcount=sendto(s[0],incomingbuff+npos,read-npos,0,(SOCKADDR *)&tudp->sinudp,sizeof(tudp->sinudp));
if(nsendcount == SOCKET_ERROR)
{
ShowError("sendto");
//重复发送次数自减一
if((iUDPRepeat--)>0)
{
printf("\nUDP2UDP Try %d times to send.",iUDPRepeat);
continue;
}
}
if(nsendcount <= 0 ) return 0;
printf("\nUDP2UDP[%d] sendto %s:%d %d bytes.",threadid,inet_ntoa(tudp->sinudp.sin_addr),ntohs(tudp->sinudp.sin_port),nsendcount);
npos += nsendcount;
}
}
printf("\nUDP2UDP Thread %d LastError %d",threadid,GetLastError());
return 0;
}
话题订阅和UDP数据转发是两个不同的功能,需要分别实现。 1. 实现话题订阅 话题订阅是指客户端订阅某个主题,当主题有更新时,服务器会向客户端发送更新信息。以下是一个简单的话题订阅实现示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #define SERVER_IP "127.0.0.1" #define SERVER_PORT 8888 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in server_addr; char topic[256]; // 创建socket sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket error"); exit(EXIT_FAILURE); } // 设置服务器地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); server_addr.sin_port = htons(SERVER_PORT); // 连接服务器 if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("connect error"); exit(EXIT_FAILURE); } // 输入要订阅的主题 printf("Please enter the topic you want to subscribe: "); fgets(topic, sizeof(topic), stdin); topic[strlen(topic) - 1] = '\0'; // 去掉换行符 // 发送订阅请求 if (write(sockfd, topic, strlen(topic)) < 0) { perror("write error"); exit(EXIT_FAILURE); } // 接收更新信息 char buf[1024]; while (1) { memset(buf, 0, sizeof(buf)); if (read(sockfd, buf, sizeof(buf)) < 0) { perror("read error"); exit(EXIT_FAILURE); } printf("Received update: %s\n", buf); } // 关闭socket close(sockfd); return 0; } ``` 2. 实现UDP数据转发 UDP数据转发是指客户端将UDP数据包发送给服务器,服务器再将数据转发给其他客户端。以下是一个简单的UDP数据转发实现示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #define SERVER_IP "127.0.0.1" #define SERVER_PORT 8888 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in server_addr, client_addr; char buf[1024]; socklen_t addr_len = sizeof(client_addr); // 创建socket sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket error"); exit(EXIT_FAILURE); } // 设置服务器地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); server_addr.sin_port = htons(SERVER_PORT); // 绑定socket到本地地址 if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind error"); exit(EXIT_FAILURE); } // 循环接收数据转发 while (1) { memset(buf, 0, sizeof(buf)); if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &addr_len) < 0) { perror("recvfrom error"); exit(EXIT_FAILURE); } printf("Received data from %s:%d: %s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buf); // 转发数据给其他客户端 // ... // 这里只是简单地将数据包原样发送给客户端 if (sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0) { perror("sendto error"); exit(EXIT_FAILURE); } } // 关闭socket close(sockfd); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值