Linux 35 网络编程多播socket选项实践

SOCKET选项用法:int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);实例IP多播模型(VRRP)...
摘要由CSDN通过智能技术生成

SOCKET选项

用法:

int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

实例

IP多播模型(VRRP):

#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <linux/tcp.h>
#include <linux/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <asm-generic/ioctls.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>

/* 192.168.220.128 */
#define LOCAL_ADDR		0xC0A8DC80

/* 192.168.220.129 */
#define PEER_ADDR		0xC0A8DC81

#define	IPPROTO_VRRP	0x70

/* 224.0.0.18 */
#define MCAST_ADDR		0xe0000012

#define MODE_LOCAL		0
#define MODE_PEER 		1

#define BUFFER_SIZE		128

extern int errno;
int mode = 0;	/* local or peer */
int sockFd = 0;
long int lastTime = 0;

int socketInit()
{
	int on = 0;
	int ret = 0;
	struct ip_mreq mreq = {0};

	if((sockFd = socket(AF_INET, SOCK_RAW, IPPROTO_VRRP)) < 0)
	{
		perror("socket");
		exit(1);
	}

	/* 设置非阻塞模式 */
	fcntl(sockFd, F_SETFL, O_NONBLOCK);

	/* 设置ttl */
	on = 255;
	if ((ret = setsockopt (sockFd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&on, sizeof(int))) < 0)
	{
		perror("setsockopt IP_MULTICAST_TTL");
		exit(1);
	}

	/* 禁止组播包环回 */
	on = 0;
	if ((ret = setsockopt (sockFd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&on, sizeof(int))) < 0)
	{
		perror("setsockopt IP_MULTICAST_LOOP");
		exit(1);
	}

	/* 加入多播组,用于收包 */
	mreq.imr_multiaddr.s_addr = htonl(MCAST_ADDR);
	#if 0
	mreq.imr_interface.s_addr = mode == MODE_LOCAL ? htonl(LOCAL_ADDR) : htonl(PEER_ADDR);
	#else
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);
	#endif
	if ((ret = setsockopt(sockFd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq))) < 0)
	{
		perror("setsockopt IP_ADD_MEMBERSHIP");
		exit(1);
	}

	/* 携带接口信息 */
	on = 1;
	if ((ret = setsockopt(sockFd, IPPROTO_IP, IP_PKTINFO, (char *)&on, sizeof(int))) < 0)
	{
		perror("setsockopt IP_PKTINFO");
		exit(1);
	}

	/* 设置自定义IP头,否则系统会自己封装IP头 */
	on = 1;
	if ((ret = setsockopt(sockFd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(int))) < 0)
	{
		perror("setsockopt IP_HDRINCL");
		exit(1);
	}

	return 0;
}

int socketRcv(int fd)
{
	char recvBuf[BUFFER_SIZE] = {0};
	int recvLen = 0;
	time_t rawtime;

	time(&rawtime);

	printf("%s",ctime(&rawtime));

	/* 这里简单的用recv收ip包,还可以使用recvmsg将控制信息(接口索引和地址)带进来,前面设置了IP_PKTINFO */

/*
struct in_pktinfo {
	int		ipi_ifindex;
	struct in_addr	ipi_spec_dst;
	struct in_addr	ipi_addr;
};*/

	recvLen = recv(fd, recvBuf, BUFFER_SIZE, 0);
	if (recvLen <= 0)
	{
		perror("recv");
		exit(1);
	}

	recvBuf[recvLen] = 0;	/* 结束符 */
	printf("recv %d bytes from peer:%s\n",recvLen,recvBuf+20);	/* RAW socket收上来的包带20字节的IP头 */

	return 0;
}

int socketSend(int fd)
{
	struct sockaddr_in dst_addr = {0};
	struct msghdr msg = {0};
	struct iovec iov[2] = {0};
	struct ip_mreq mreq = {0};
	struct iphdr ip_header;
	struct in_addr addr = {0};
	char vrrp_pkt[32] = "VRRP PKT TEST ONLY!!!!!!!!!!!!!";
	char mac[6] = {0x00,0x00,0x5e,0x00,0x01,0x01};
	int ret = 0;

	if (fd <= 0)
	{
		return 0;
	}

	/* 设置IP头部 */
	memset(&ip_header, 0, sizeof(struct iphdr));
	ip_header.version = 4;
	ip_header.ihl = (sizeof(struct iphdr) >> 2) & 0xf;
	ip_header.tos = IPTOS_PREC_INTERNETCONTROL;
	ip_header.id = 0;
	ip_header.frag_off = 0;
	ip_header.ttl = 255;
	ip_header.protocol = IPPROTO_VRRP;
	ip_header.check = 0;
	ip_header.tot_len = htons(sizeof(struct iphdr) + sizeof(vrrp_pkt));
	ip_header.saddr = mode == MODE_LOCAL ? htonl(LOCAL_ADDR) : htonl(PEER_ADDR);
	ip_header.daddr = htonl(MCAST_ADDR);


	/* 设置目的地址 */
	dst_addr.sin_family = AF_INET;
	dst_addr.sin_addr.s_addr = htonl(MCAST_ADDR);

	/* 设置msg */
	msg.msg_name = &dst_addr;
	msg.msg_namelen = sizeof (dst_addr); 
	msg.msg_iov = iov;
	msg.msg_iovlen = 2;
	
	/* 设置缓冲区 */
	iov[0].iov_base = (char *)&ip_header;
	iov[0].iov_len = sizeof(struct iphdr);
	iov[1].iov_base = (char *)vrrp_pkt;
	iov[1].iov_len = sizeof(vrrp_pkt);

	/* 设置多播出接口 */
	addr.s_addr = mode == MODE_LOCAL ? htonl(LOCAL_ADDR) : htonl(PEER_ADDR);
	if ((ret = setsockopt(sockFd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addr, sizeof(addr))) < 0)
	{
		perror("setsockopt IP_MULTICAST_IF");
		exit(1);
	}

	#if 0
	/* 设置发包源MAC */
	if ((ret = setsockopt(sockFd, SOL_SOCKET, SO_SNDSRCMAC, (char *)&mac, sizeof(mac))) < 0)
	{
		perror("setsockopt SO_SNDSRCMAC");
		exit(1);
	}
	#endif

	if ((ret = sendmsg(sockFd, &msg, MSG_DONTWAIT)) < 0)
	{
		perror("sendmsg");
		exit(1);
	}

	return 0;
}

int send_timer()
{
	long int currTime = time(NULL);

	/* 每隔1s发送一次 */
	if (currTime - lastTime >= 1)
	{
		socketSend(sockFd);

		/* 更新上次发送时间 */
		lastTime = currTime;
	}

	return 0;
}

int main(int argc,char **argv)
{
	fd_set rbits;
	struct timeval t;
	int n = 0;		/* 读写可用的socket个数 */

	if (argc != 2)
	{
		printf("usage: ./ip_mc_test local|peer\n");
		return 0;
	}

	/* 启动local或者peer,local和peer主要用于在两台机器上(ip地址不同)测试 */
	if (memcmp(argv[1], "local", strlen("local")) == 0)
	{
		mode = MODE_LOCAL;
	}
	else if (memcmp(argv[1], "peer", strlen("peer")) == 0)
	{
		mode = MODE_PEER;
	}
	else
	{
		printf("usage: ./ip_mc_test local|peer\n");
		return 0;
	}

	socketInit();

	/* 主进程循环 */
	while(1)
	{
		FD_ZERO(&rbits);

		t.tv_sec = 1;
		t.tv_usec = 0;

		if (sockFd > 0)
			FD_SET(sockFd, &rbits);

		/* 检查套接字可读可写性,n为返回的可读和可写的套接字个数 */
		n = select(sockFd + 1, &rbits, NULL, NULL, &t);
		if (n > 0)
		{
			/* 可读,即有数据或者请求到达(serverFd只负责accept连接请求) */
			if (FD_ISSET(sockFd, &rbits))
			{
				socketRcv(sockFd);
			}
		}

		/* 发包定时器 */
		send_timer();
	}

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值