使用广播查询服务器地址
21 Feb 2014流程
- 服务器端监听端口9999
- 客户端通过发送广播信息向网络查询需要的服务器地址
- 服务器端收到客户端的查询后把自己的地址信息发送给客户端
代码
公共头文件common.h
#ifndef __COMMON_H
#define __COMMON_H
#include <stdio.h>
#include <stdlib.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 QUERY_SERV "QUERY_SERV"
/*服务器应答消息*/
#define IAMSERV "IAMSERV"
#define PORT 9999
/*返回值*/
#define ERR 1
#define OK 0
#endif
服务器端serv.c
#include "common.h"
int main(int argc, char *argv[]) {
int ret = -1;
int sock = -1;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(struct sockaddr_in);
fd_set readfd;
char buffer[1024] = {0};
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock == -1) {
perror("sock error");
return ERR;
}
memset(&serv_addr, 0x0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
ret = bind(sock, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr));
if(ret == -1) {
perror("bind error");
return ERR;
}
while(1) {
FD_ZERO(&readfd);
FD_SET(sock, &readfd);
ret = select(sock + 1, &readfd, NULL, NULL, NULL);
switch(ret) {
case -1:
perror("select error");
break;
case 0:
fprintf(stderr, "select timeout\n");
break;
default:
if(FD_ISSET(sock, &readfd)) {
recvfrom(sock, buffer, 1024, 0, (struct sockaddr*)&cli_addr, &cli_len);
if(strstr(buffer, QUERY_SERV)) {
printf("Client IP: %s PORT: %d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
sendto(sock, IAMSERV, strlen(IAMSERV), 0, (struct sockaddr*)&cli_addr, cli_len);
}
}
break;
}
}
return OK;
}
客户端cli.c
#include "common.h"
int main(int argc, char *argv[]) {
int ret = -1;
struct sockaddr_in serv_addr;
socklen_t serv_len = sizeof(struct sockaddr_in);
fd_set readfd;
char buffer[1024] = {0};
char *netDev = NULL;
struct timeval timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
/*设置客户端的网口*/
if(argc == 2) {
netDev = argv[1];
} else {
/*默认使用eth0*/
netDev = "eth0";
}
int sock = -1;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock == -1) {
perror("socket error");
return ERR;
}
struct ifreq ifr;
memset(&ifr, 0x0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, netDev, strlen(netDev));
printf("use device: %s\n", ifr.ifr_name);
/*查询选定网口的广播地址*/
if(ioctl(sock, SIOCGIFBRDADDR, &ifr) == -1) {
perror("ioctl error");
return ERR;
}
int so_broadcast = 1;
struct sockaddr_in broadcast_addr;
memcpy(&broadcast_addr, &ifr.ifr_broadaddr, sizeof(struct sockaddr_in));
printf("broadcast IP is: %s\n", inet_ntoa(broadcast_addr.sin_addr));
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_port = htons(PORT);
ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &so_broadcast, sizeof(so_broadcast));
if(ret == -1) {
perror("setsockopt error");
return ERR;
}
/*广播查询服务器信息,如果没有回应,则重新发送,共重复10次*/
int times = 10;
int trys = 0;
for(trys = 0; trys < times; trys++) {
timeout.tv_sec = 2;
timeout.tv_usec = 0;
ret = sendto(sock, QUERY_SERV, strlen(QUERY_SERV), 0, (struct sockaddr*)&broadcast_addr, sizeof(struct sockaddr));
if(ret == -1) {
continue;
}
FD_ZERO(&readfd);
FD_SET(sock, &readfd);
ret = select(sock + 1, &readfd, NULL, NULL, &timeout);
switch(ret) {
case -1:
perror("select error");
break;
case 0:
fprintf(stderr, "select timeout\n");
break;
default:
if(FD_ISSET(sock, &readfd)) {
recvfrom(sock, buffer, 1024, 0, (struct sockaddr*)&serv_addr, &serv_len);
printf("recvmsg is: %s\n", buffer);
if(strstr(buffer, IAMSERV)) {
printf("Server found, IP: %s PORT: %d \n", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
}
return OK;
}
break;
}
}
if(trys == 10) {
printf("Sorry, cannot find the server\n");
}
return OK;
}
测试运行
-
打开服务器端:
./serv
-
打开客户端:
./cli
客户端参数:./cli [ethx]
参数说明:ethx是你要使用的网络端口,如eth0, eth1等,默认为eth0
运行输出
-
客户端输出: [
-
服务器端输出: [