http://hi.baidu.com/tianhaixin8315/blog/item/1815e0cfec87ba5f0eb34569.html
最近需要编写一个vxWorks下的UDP通信程序,要求Server端广播发送,几个Client端接收数据,同时进行拆包处理。
在网上找了不少UDP广播发送的程序,不是讲的不太清楚,就是功能实现上有问题,最后参考VC下的UDP broadcast 例程实现了功能。
在这里和大家分享一下。
/* udpServer.c - UDP server example */
/*
DESCRIPTION
This file contains the server-side of the VxWorks UDP example code.
The example code demonstrates the useage of several BSD 4.4-style
socket routine calls.
*/
/* includes */
#include "vxWorks.h"
#include "sockLib.h"
#include "inetLib.h"
#include "stdioLib.h"
#include "strLib.h"
#include "ioLib.h"
#include "fioLib.h"
#include "Define.h"
#include "taskLib.h"
#include "string.h "
#include "vxudpServer.h"
#include "fsfb_main.h"
#include "logLib.h"
/*********************************************************************
*
* udpServer - read from UDP socket and display client's message if requested
*
* Example of VxWorks UDP server:
* -> sp udpServer
* task spawned: id = 0x3a1f6c, name = t2
* value = 3809132 = 0x3a1f6c
* -> MESSAGE FROM CLIENT (Internet Address 150.12.0.11, port 1028):
* Greetings from UDP client
*
* RETURNS: Never, or ERROR if a resources could not be allocated.
***********************************************************************/
int udpInitialize(void);
int StartUdpRecv(void);
void OnReceiveThread(void);
int udpSendData(FSFB_MSG *fsfb_msg_send, int MSG_LEN,char * chNetAddr,int nPort);
struct sockaddr_in serverAddr; /* server's socket address */
struct sockaddr_in clientAddr; /* client's socket address */
int sockAddrSize; /* size of socket address structure */
int sFd; /* socket file descriptor */
char inetAddr[INET_ADDR_LEN]; /* buffer for client's inet addr */
BYTE nDataAddress = 0;
BYTE nDataType = 0;
int nSendDataLen = 0;
BYTE bQueInRslt = 0;
char pBuf[1024]; //Recv data use
char strSendData[4096]; //send data use
int udpInitialize(void)
{
/* set up the local address */
sockAddrSize = sizeof (struct sockaddr_in);
bzero ((char *) &serverAddr, sockAddrSize);
serverAddr.sin_len = (u_char) sockAddrSize;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons (SERVER_PORT_NUM);
serverAddr.sin_addr.s_addr = htonl (INADDR_ANY); //INADDR_BROADCAST,INADDR_ANY
/* create a UDP-based socket */
if ((sFd = socket (AF_INET, SOCK_DGRAM, 0)) == ERROR)
{
perror ("socket");
return (ERROR);
}
//设置允许地址复用
int flag = 1,ret;
ret = setsockopt(sFd,SOL_SOCKET,SO_BROADCAST,(char *)&flag,sizeof(flag));
if(ret!=0)
{
logMsg("setsockopt failed\n",0,0,0,0,0,0);
close(sFd);
return FALSE;
}
/* bind socket to local address */
if (bind (sFd, (struct sockaddr *) &serverAddr, sockAddrSize) == ERROR)
{
perror ("bind");
close (sFd);
return (ERROR);
}
return TRUE;
}
int StartUdpRecv(void)
{
int udpNet=taskSpawn("udpNet",JUDGECOMPRIO,VX_NO_STACK_FILL,0x1000,(FUNCPTR)OnReceiveThread,0,0,0,0,0,0,0,0,0,0);
if (udpNet==ERROR)
{
if (taskIdVerify(udpNet) == TRUE)
{
taskDelete(udpNet);
}
return FALSE;
}
}
/* read data from a socket and satisfy requests */
void OnReceiveThread(void)
{
memset(pBuf,0,sizeof pBuf);
FOREVER
{
if (recvfrom (sFd, pBuf, sizeof pBuf, 0,
(struct sockaddr *) &clientAddr, &sockAddrSize) == ERROR)
{
perror ("recvfrom");
close (sFd);
return;
}
/* if client requested that message be displayed, print it */
{
/* convert inet address to dot notation */
inet_ntoa_b (clientAddr.sin_addr, inetAddr);
nDataAddress = pBuf[2];
nDataType = (BYTE)pBuf[1];
logMsg("nDataType = 0x%x\n",nDataType,0,0,0,0,0);
logMsg("nDataAddress = 0x%x\n",nDataAddress,0,0,0,0,0);
if (nDataAddress == 0x10)
{
continue;
}
switch(nDataAddress) //判断数据来源:由IP地址判断改成数据源地址判断
{
case CTCMSGAddress:
logMsg("CTC que come in\n",0,0,0,0,0,0);
if (nDataType == FSFB_MSG_ID_SSE)
{
bQueInRslt = InsertData(&(ctc_cbi_2_tcc_sse.SSEINQUE),(BYTE *)pBuf);
logMsg("CTC sse come in : %d\n",bQueInRslt,0,0,0,0,0);
}
else if (nDataType == FSFB_MSG_ID_SSR)
{
bQueInRslt = InsertData(&(ctc_cbi_2_tcc_ssr.SSRINQUE),(BYTE *)pBuf);
logMsg("CTC ssr come in : %d\n",bQueInRslt,0,0,0,0,0);
}
else if (nDataType == FSFB_MSG_ID_DATA)
{
bQueInRslt = InsertData(&(ctc_2_tcc_data.CTCINQUE),(BYTE *)pBuf);
logMsg("CTC data come in : %d\n",bQueInRslt,0,0,0,0,0);
}
break;
case CBIMSGAddress:
if (nDataType == FSFB_MSG_ID_SSE)
{
bQueInRslt = InsertData(&(ctc_cbi_2_tcc_sse.SSEINQUE),(BYTE *)pBuf);
logMsg("CBI sse come in : %d\n",bQueInRslt,0,0,0,0,0);
}
else if (nDataType == FSFB_MSG_ID_SSR)
{
bQueInRslt = InsertData(&(ctc_cbi_2_tcc_ssr.SSRINQUE),(BYTE *)pBuf);
logMsg("CBI ssr come in : %d\n",bQueInRslt,0,0,0,0,0);
}
else if (nDataType == FSFB_MSG_ID_DATA)
{
bQueInRslt = InsertData(&(cbi_2_tcc_data.CBIINQUE),(BYTE *)pBuf);
logMsg("CBI data come in : %d\n",bQueInRslt,0,0,0,0,0);
}
break;
default:
break;
}
}
}
}
int udpSendData(FSFB_MSG *fsfb_msg_send, int MSG_LEN,char * chNetAddr,int nPort)
{
memset(strSendData,0,sizeof strSendData);
memcpy(strSendData,fsfb_msg_send,MSG_LEN+2);
serverAddr.sin_addr.s_addr = inet_addr(chNetAddr);
if ((nSendDataLen = sendto (sFd, strSendData, MSG_LEN+2, 0,
(struct sockaddr *) &serverAddr, sockAddrSize)) == ERROR)
{
perror ("sendto");
return (FALSE);
}
if(nSendDataLen != MSG_LEN+2)
return -1;
return (TRUE);
}
广播发送的关键是设置socket的选项,允许地址复用,即给多个ip地址发送数据,而发送端只需发送一包数据。
int flag = 1,ret;
ret = setsockopt(sFd,SOL_SOCKET,SO_BROADCAST,(char *)&flag,sizeof(flag));
if(ret!=0)
{
logMsg("setsockopt failed\n",0,0,0,0,0,0);
close(sFd);
return FALSE;
}
在发送数据时,将chNetAddr填写为 x.x.x.255,则x.x.x.*网段中的所有地址(1~254)都将接收到广播数据。