Linux网络编程 之 UDP 广播 .

在网上搜了很多资料 ,其中周立发的博客对我的学习参考作用最大,谢谢前辈。

这个倒让我花的时间最多了,主要一直脑残的在自己的虚拟机上调试不成功,认为只要开几个终端就可以当作服务器了,这种想法是错误的。因为ip地址会被占用的,所以后来我和同事测试一下,就OK了。

这个广播代码逻辑其实挺有用的,可以用来寻找所在局域网内设备

逻辑就是:客户端一直发广播,当服务器收到这个广播,则向客户端回个确认,这样客户端收到确认后就知道有所要的服务器了

  直接上代码吧,一个很简单udp 广播通信的例子,分别为服务器端和客户端的代码。


1、服务器代码 (bcast_server.c)

/*********************************************************************
 * Filename: bcast_server.c
 * Description: 广播服务器端代码
 * Author: Eric([email protected])
 * Date: 2012-9-14
 ********************************************************************/
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;
}


2、客户端代码(bcast_clinet.c)

 

/*********************************************************************
 * Filename: bcast_client.c
 * Description:广播客户端代码
 * Author: Eric([email protected])
 * Date: 2012-9-14
 ********************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include 

#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)

运行效果如图:

Linux网络编程 之 UDP 广播 ._第1张图片


linux网络编程源代码下载:http://download.csdn.net/detail/yanyuanfen2011/6595113


如有问题欢迎留言交流!

你可能感兴趣的:(网络编程)