在网上搜了很多资料 ,其中周立发的博客对我的学习参考作用最大,谢谢前辈。
这个倒让我花的时间最多了,主要一直脑残的在自己的虚拟机上调试不成功,认为只要开几个终端就可以当作服务器了,这种想法是错误的。因为ip地址会被占用的,所以后来我和同事测试一下,就OK了。
这个广播代码逻辑其实挺有用的,可以用来寻找所在局域网内设备。
逻辑就是:客户端一直发广播,当服务器收到这个广播,则向客户端回个确认,这样客户端收到确认后就知道有所要的服务器了。
直接上代码吧,一个很简单udp 广播通信的例子,分别为服务器端和客户端的代码。
1、服务器代码 (bcast_server.c)
- /*********************************************************************
- * 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",
- (charchar *)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(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, (charchar *) &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, (charchar *) 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, (charchar *)&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网络编程源代码下载:http://download.youkuaiyun.com/detail/yanyuanfen2011/6595113
如有问题欢迎留言交流!
本文介绍了一个简单的UDP广播通信实例,包括服务器端和客户端代码。通过客户端发送广播消息,服务器端收到后回复确认消息来实现局域网内的设备查找。
1048

被折叠的 条评论
为什么被折叠?



