如果目的地址是multicast, 可以使用 socket 选项 IP_MULTICAST_IF,它指定多播报文是哪个网络接口发出去:
struct Ip_in_addr in_addr; /* for IP_IP_MULTICAST_IF */
struct sockaddr_in myAddr; /* for bind */
/* create client's socket */
if((sFd = socket(AF_INET, SOCK_DGRAM,0)) == ERROR ) {
perror ("socket");
return (ERROR);
}
/* test IP_IP_MULTICAST_IF */
in_addr.s_addr = inet_addr(nic); /* interface address */
if (ipcom_setsockopt(sFd, IP_IPPROTO_IP, IP_IP_MULTICAST_IF, &in_addr, sizeof(in_addr)) < 0) {
printf ("ipcom_setsockopt IP_IP_MULTICAST_IF failed, errno = %d\n", errno);
}
这是我写的一个udp client 程序,在vxworks 下通过。 修改一下头文件,应该也可以在其它操作系统下编译和运行。
/* test if udp broadcast is OK */
/* Dai Yuwen, December 2011 */
#include <vxWorks.h>
#include <sockLib.h>
#include <inetLib.h>
#include <hostLib.h>
#include <string.h>
#include <stdio.h>
#include <ipcom_sock.h>
#define SERVER_PORT_NUM 9
#define REQUEST_MSG_SIZE 1024
struct request
{
int display;
char message[REQUEST_MSG_SIZE];
};
/* usage: udpClient serverIP, interfaceIP */
STATUS udpClient(char *serverName, char* nic)
{
struct request myRequest;
struct sockaddr_in serverAddr;
int sockAddrSize;
int sFd;
int mlen;
int addr;
struct Ip_in_addr in_addr; /* for IP_IP_MULTICAST_IF */
struct sockaddr_in myAddr; /* for bind */
/* create client's socket */
if((sFd = socket(AF_INET, SOCK_DGRAM,0)) == ERROR ) {
perror ("socket");
return (ERROR);
}
/* test IP_IP_MULTICAST_IF */
in_addr.s_addr = inet_addr(nic); /* interface address */
if (ipcom_setsockopt(sFd, IP_IPPROTO_IP, IP_IP_MULTICAST_IF, &in_addr, sizeof(in_addr)) < 0) {
printf ("ipcom_setsockopt IP_IP_MULTICAST_IF failed, errno = %d\n", errno);
}
/* bind not required - port is dynamic */
#if 1
bzero(&myAddr, sizeof(myAddr));
myAddr.sin_family = AF_INET;
myAddr.sin_addr.s_addr = inet_addr(nic); /* interface address */
/*myAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* interface address */
myAddr.sin_port = 0; /* let kernel choose port */
if(bind(sFd, (struct sockaddr *)&myAddr, sizeof(myAddr)) < 0) {
printf ("bind failed, errno = %d s_addr = %x\n", errno, myAddr.sin_addr.s_addr);
}
#endif
/* bind server socket address */
sockAddrSize = sizeof(struct sockaddr_in);
bzero((char*)&serverAddr, sockAddrSize);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(SERVER_PORT_NUM);
#if 0
if(((serverAddr.sin_addr.s_addr = inet_addr(serverName)) == ERROR )&&
((serverAddr.sin_addr.s_addr = hostGetByName(serverName)) == ERROR)) {
perror ("unknown server name");
close(sFd);
return (ERROR);
}
serverAddr.sin_addr.s_addr = hostGetByName(serverName);
if (serverAddr.sin_addr.s_addr == ERROR){
perror ("unknown server name");
close(sFd);
return (ERROR);
}
#endif
addr = inet_addr(serverName);
if (serverAddr.sin_addr.s_addr == ERROR){
perror ("unknown server name");
close(sFd);
return (ERROR);
}
serverAddr.sin_addr.s_addr = addr;
memcpy (myRequest.message, "greeting from vxworks", 21);
if(sendto(sFd, (caddr_t)&myRequest, 0 /*sizeof(myRequest)*/, 0,
(struct sockaddr *)&serverAddr, sockAddrSize) == ERROR) {
perror ("sendto");
close(sFd);
return(ERROR);
}
close(sFd);
return(OK);
}
用法是这样的:
-> udpClient "224.0.0.1","192.168.20.1"
-> udpClient "224.0.0.1","172.25.52.31"
udpClient 的第二个参数指定的是网口地址,表示要从该网口出去。 在PC上抓
包,验证了这点。
另外,我还验证了调用bind是没有用的,虽然源IP地址按要求设置了, 但UDP包并不从
指定IP地址的那个口出去,这是符合预期的,因为到底从哪个口出去,由路由决
定,而查找路由是根据目的IP地址,跟源IP无关。
还有,有人想把广播包从指定网口发出去,用这个选项是没有用的。 只能加路由解决。