您的位置:首页 > 理论基础 > 计算机网络

linux网络编程之广播

2012-09-14 18:56 274 查看
Linux网络编程之广播

作者:Eric(wongpz@foxmail.com)
时间:2012-09-14

常见的TCP和UDP通信大都使用的是点对点的单播方式,这种方式可以很方便进行交互处理,在多个交互同时进行时,网络带宽占的比较多。广播是由一个主机向一个网络上所有主机发送消息的方式,需要的网络带宽相对单播来说,降低很多。
广播的地址是将IP地址中主机部分全部置为1,即xxx.xxx.xxx.255。255.255.255.255这一特殊的广播地址可以向全世界进行广播,但一般路由器都会屏蔽。
广播发送的目的MAC地址为FF:FF:FF:FF:FF:FF。
一般发送广播步骤如下:
(1)确定广播接口名字,如eth0;
(2)确定广播地址,通过ioctl函数,请求码为SIOCGIFBRDADDR,从而得到广播地址;
(3)使用广播地址进行广播;

由于TCP协议是端到端的协议,通信之前必须建立三次握手才能发送数据,而广播是一种一对多的通信方式,所以TCP不支持广播。一般在局域网中,广播用来探测服务器地址或客户端的发现。

编程实现:
服务器端:

/*********************************************************************
* Filename: bcast_server.c
* Description: 广播服务器端代码
* Author: Eric(wongpz@foxmail.com)
* Date: 2012-9-14
********************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <linux/in.h>
#include <stdlib.h>

#define IP_FOUND "IP_FOUND"
#define IP_FOUND_ACK "IP_FOUND_ACK"
#define PORT 9999

int main(int argc, char*argv[])
{
int ret = -1;
int sock;
struct sockaddr_in server_addr; //服务器端地址
struct sockaddr_in from_addr; //客户端地址
int from_len = sizeof(struct sockaddr_in);
int count = -1;
fd_set readfd; //读文件描述符集合
char buffer[1024];
struct timeval timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 0;

sock = socket(AF_INET, SOCK_DGRAM, 0); //建立数据报套接字
if (sock < 0)
{
perror("sock error");
return -1;
}

memset((void*) &server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY );
server_addr.sin_port = htons(PORT);

//将地址结构绑定到套接字上
ret = bind(sock, (struct sockaddr*) &server_addr, sizeof(server_addr));
if (ret < 0)
{
perror("bind error");
return -1;
}

/**
* 循环等待客户端
*/
while (1)
{
timeout.tv_sec = 100;
timeout.tv_usec = 0;

//文件描述符集合清0
FD_ZERO(&readfd);

//将套接字描述符加入到文件描述符集合
FD_SET(sock, &readfd);

//select侦听是否有数据到来
ret = select(sock + 1, &readfd, NULL, NULL, &timeout); //侦听是否可读
switch (ret)
{
case -1: //发生错误
perror("select error:");
break;
case 0: //超时
printf("select timeout\n");
break;
default:
if (FD_ISSET(sock,&readfd))
{
count = recvfrom(sock, buffer, 1024, 0,
(struct sockaddr*)&from_addr, &from_len); //接收客户端发送的数据

//from_addr保存客户端的地址结构
if (strstr(buffer, IP_FOUND))
{
//响应客户端请求
//打印客户端的IP地址和端口号
printf("\nClient connection information:\n\t IP: %s, Port: %d\n",
(char *)inet_ntoa(from_addr.sin_addr),
ntohs(from_addr.sin_port));

//将数据发送给客户端
memcpy(buffer, IP_FOUND_ACK, strlen(IP_FOUND_ACK) + 1);
count = sendto(sock, buffer, strlen(buffer), 0,
(struct sockaddr*) &from_addr, from_len);
}
}
break;
}
}
return 0;
}


客户端:

/*********************************************************************
* Filename: bcast_client.c
* Description:广播客户端代码
* Author: Eric(wongpz@foxmail.com)
* Date: 2012-9-14
********************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>

#define IP_FOUND "IP_FOUND"
#define IP_FOUND_ACK "IP_FOUND_ACK"
#define IFNAME "eth0"
#define MCAST_PORT 9999

int main(int argc, char*argv[])
{
int ret = -1;
int sock = -1;
int j = -1;
int so_broadcast = 1;
struct ifreq *ifr;
struct ifconf ifc;
struct sockaddr_in broadcast_addr; //广播地址
struct sockaddr_in from_addr; //服务端地址
int from_len = sizeof(from_addr);
int count = -1;
fd_set readfd; //读文件描述符集合
char buffer[1024];
struct timeval timeout;
timeout.tv_sec = 2; //超时时间为2秒
timeout.tv_usec = 0;

//建立数据报套接字
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
{
perror("create socket failed:");
return -1;
}

// 获取所有套接字接口
ifc.ifc_len = sizeof(buffer);
ifc.ifc_buf = buffer;
if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0)
{
perror("ioctl-conf:");
return -1;
}
ifr = ifc.ifc_req;
for (j = ifc.ifc_len / sizeof(struct ifreq); --j >= 0; ifr++)
{
if (!strcmp(ifr->ifr_name, "eth0"))
{
if (ioctl(sock, SIOCGIFFLAGS, (char *) ifr) < 0)
{
perror("ioctl-get flag failed:");
}
break;
}
}

//将使用的网络接口名字复制到ifr.ifr_name中,由于不同的网卡接口的广播地址是不一样的,因此指定网卡接口
//strncpy(ifr.ifr_name, IFNAME, strlen(IFNAME));
//发送命令,获得网络接口的广播地址
if (ioctl(sock, SIOCGIFBRDADDR, ifr) == -1)
{
perror("ioctl error");
return -1;
}
//将获得的广播地址复制到broadcast_addr
memcpy(&broadcast_addr, (char *)&ifr->ifr_broadaddr, sizeof(struct sockaddr_in));
//设置广播端口号
printf("\nBroadcast-IP: %s\n", inet_ntoa(broadcast_addr.sin_addr));
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_port = htons(MCAST_PORT);

//默认的套接字描述符sock是不支持广播,必须设置套接字描述符以支持广播
ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &so_broadcast,
sizeof(so_broadcast));

//发送多次广播,看网络上是否有服务器存在
int times = 10;
int i = 0;
for (i = 0; i < times; i++)
{
//一共发送10次广播,每次等待2秒是否有回应
//广播发送服务器地址请求
timeout.tv_sec = 2;  //超时时间为2秒
timeout.tv_usec = 0;
ret = sendto(sock, IP_FOUND, strlen(IP_FOUND), 0,
(struct sockaddr*) &broadcast_addr, sizeof(broadcast_addr));
if (ret < 0)
{
continue;
}

//文件描述符清0
FD_ZERO(&readfd);
//将套接字文件描述符加入到文件描述符集合中
FD_SET(sock, &readfd);
//select侦听是否有数据到来
ret = select(sock + 1, &readfd, NULL, NULL, &timeout);
switch (ret)
{
case -1:
break;
case 0:
perror("select timeout\n");
break;
default:
//接收到数据
if (FD_ISSET(sock,&readfd))
{
count = recvfrom(sock, buffer, 1024, 0,
(struct sockaddr*) &from_addr, &from_len); //from_addr为服务器端地址
printf("\trecvmsg is %s\n", buffer);
if (strstr(buffer, IP_FOUND_ACK))
{
printf("\tfound server IP is %s, Port is %d\n",
inet_ntoa(from_addr.sin_addr),
htons(from_addr.sin_port));
}
return -1;

}
break;

}
}
return 0;
}


Makefile文件:

OBJS_SERVER = bcast_server.o
OBJS_CLIENT = bcast_client.o
LIBS_SERVER =
LIBS_CLIENT =

CFLAGS = -c
CC = gcc

PROS = bcast_client bcast_server

all: $(PROS)

.c.o:
$(CC) $(CFLAGS) $<

bcast_client: $(OBJS_CLIENT)
$(CC) -o $@ $^ $(LIBS_SERVER)

bcast_server: $(OBJS_SERVER)
$(CC) -o $@ $^ $(LIBS_CLIENT)

clean:
rm -rf $(PROS) $(OBJS_CLIENT) $(OBJS_SERVER)


运行截图:





备注:
以上代码参考于http://blog.csdn.net/chenjin_zhong/article/details/7270213,获取网络接口部分有改动。

具体代码及文档可以在如下地址下载:
http://download.csdn.net/detail/ericdev/4570901
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: