通过广播获取ip地址的实现

引言

在进行网络程序的设计时,有时候我们不想要手动指定接收方的ip尤其是在做聊天小程序的时候,因为这个时候对方的ip不是固定的,那么我们该怎么办呢?一个方法就是接收方创建一个socket监听一个端口,发送方通过广播的方式给所有局域网内的主机发送一个“hello”包,当然这个包是发往接收方监听的端口的,接收方接收到“hello”包后获取自己的ip地址然后封装在数据报里再发回给发送方,这样发送方就能获得局域网内所有在线的接收方的信息了!


但这个过程有几个问题要考虑。第一点,如果接收方有多个网卡,我怎么知道“hello”包是通过哪个网卡接收的呢?第二点,在接收方我可以用监听那个广播端口的socket来发送消息嘛?

对于第一点,也是苦恼了我很久的问题,最终找到了方法:getsockname,这个函数可以根据addr来得到ip地址,用C语言学习网络编程的都知道接收函数是有一个sockaddr_in结构体来接收发送方的ip和端口信息的,而这个函数就是根据这个结构体来解析的,但是这个函数有一个条件是一定要在连接的情况下才能获取本地ip地址,不过也对,连接了,网卡一定也就确定了。既然要连接那反正udp套接字也可以connect,我们多写一点就是了。

对于第二点,答案是不可以,因为我也尝试了这样的做法,在发送方接收到的ip消息会出现乱码的情况。所以在发送方和接收方都要多创建一个用于传送该消息的socket。总体来说就是,发送方和接收方都要有一个socket用来广播"hello"包和接收"hello"包,并且还都要有一个udp socket来发送udp消息和接收udp消息。


话不多说,上代码

//发送方--广播“hello”包然后接收接收方发送的ip消息
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <WinSock2.h>
#pragma comment (lib,"ws2_32.lib")
 
#define BUF_SIZE 100
void error_handling(const char* message);
 
int main(int argc,char* argv[])
{
	WSADATA wsadata;
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
		error_handling("wsastartup error!");
 
	if (argc != 2) {
		printf("usage: %s <port>\n", argv[0]);
		exit(1);
	}
 
	SOCKET send_sock,recv_sock;
	SOCKADDR_IN broad_adr;
	char send_msg[BUF_SIZE]="hello,world",rec_msg[BUF_SIZE];;
	int so_brd = 1;
	//创建send_sock并与广播地址进行关联
	send_sock = socket(PF_INET,SOCK_DGRAM,0);
	if (send_sock == INVALID_SOCKET)
		error_handling("send_sock error!");
	memset(&broad_adr,0,sizeof(broad_adr));
	broad_adr.sin_family = AF_INET;
	broad_adr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
	broad_adr.sin_port = htons(atoi(argv[1]));
	//设置广播选项
	int getinfo = setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(const char*)&so_brd,sizeof(so_brd));
	if (getinfo == SOCKET_ERROR) 
		error_handling("setsockopt error!");
	printf("setsockopt has done\n");
 
	//创建recv_sock并与本地地址进行绑定
	recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
	if (recv_sock == INVALID_SOCKET)
		error_handling("recv_sock error!");
	struct sockaddr_in myadr, youradr;
	memset(&myadr, 0, sizeof(myadr));
	myadr.sin_family = AF_INET;
	myadr.sin_addr.s_addr = htonl(INADDR_ANY);
	myadr.sin_port = htons(10244);
	if (bind(recv_sock, (struct sockaddr*)&myadr, sizeof(myadr)) == SOCKET_ERROR)
		error_handling("bind error!");
	else printf("bind has done\n");
	
	//发送helloworld消息
	int send_len;
	send_len = sendto(send_sock,send_msg,strlen(send_msg),0,(SOCKADDR*)&broad_adr,sizeof(broad_adr));
	if(send_len == -1)
		error_handling("sendto error!");
	else printf("sendto has done\n");
	closesocket(send_sock); //发送完后关闭发送的socket
	
	//接收来自receive.cpp运行端的ip消息并输出ip
	int yadr_sz = sizeof(youradr);
 
	int recv_len = recvfrom(recv_sock, rec_msg, BUF_SIZE, 0, (SOCKADDR*)&youradr, &yadr_sz);
	if (recv_len == -1)
		error_handling("recvfrom error!");
	else {
		printf("%s\n", rec_msg);
	}
 
	closesocket(recv_sock);
 
	WSACleanup();
	return 0;
} 
 
void error_handling(const char* message)
{
	printf("%s\n", message);
	exit(1);
}
//发送方接收到“hello”包后获取本机ip地址然后封装到udp数据报里发送
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment (lib,"ws2_32.lib")
#define BUF_SIZE 100
void error_handling(const char* message);
 
int main(int argc, char* argv[])
{
	WSADATA wsadata;
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
		error_handling("wsastartup error!");
 
	if (argc != 2) {
		printf("usage: %s <port>\n", argv[0]);
		exit(1);
	}
 
	char 	rec_msg[BUF_SIZE],send_msg[BUF_SIZE] = "the receiver ip is ";
	SOCKET send_sock,recv_sock;
 
	send_sock = socket(PF_INET, SOCK_DGRAM, 0);
	if (send_sock == INVALID_SOCKET)
		error_handling("send_sock error!");
 
	recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
	if(recv_sock == INVALID_SOCKET)
		error_handling("recv_sock error!");
		
	SOCKADDR_IN adr,youradr;
	//set adr
	memset(&adr, 0, sizeof(adr));
	adr.sin_family = AF_INET;
	adr.sin_addr.s_addr = htonl(INADDR_ANY);
	adr.sin_port = htons(atoi(argv[1]));
	//bind socket and adr
	if (bind(recv_sock,(SOCKADDR*)&adr,sizeof(adr)) == -1)
		error_handling("bind error!");
	else printf("bind has done\n");
	//receive message
	int your_adr_sz = sizeof(youradr);
	int strlen = recvfrom(recv_sock, rec_msg, BUF_SIZE, 0, (SOCKADDR*)&youradr, &your_adr_sz);
	if (strlen == -1)
		error_handling("recvfrom error!");
	else {
		//输出接收到的消息和发送端的ip地址
		char* ip = inet_ntoa(youradr.sin_addr);
		printf("the message from sender: %s\n", rec_msg);
		printf("the sender ip is : %s\n", ip);
		
		//与发送端建立连接,通过getsockname得到自己的ip地址,将自己的ip地址发送给发送端
		//getsockname函数要在连接时才能得到本地ip地址,所以要先connect
		//另外要注意在发送端的接收socket的端口设定在10244,所以在这里也要改下
		youradr.sin_port = htons(10244);
		int con = connect(send_sock, (SOCKADDR*)&youradr, your_adr_sz);
		if (con == 0)
			printf("connect has done\n");
		else error_handling("connect error!");
		SOCKADDR_IN myaddr;
		int sz = sizeof(myaddr);
		int ret = getsockname(send_sock, (SOCKADDR*)&myaddr, &sz); //得到我自己的IP地址
		if (ret != 0)
			error_handling("getsockname error!");
		printf("my ip is %s", inet_ntoa(myaddr.sin_addr));
 
		char temp[BUF_SIZE];
		char *myip = inet_ntoa(myaddr.sin_addr);
		strcpy(temp, myip);
		strncat(send_msg, temp, ::strlen(temp)); //这里strlen前面加::是因为我前面有一个strlen变量重名了
		int sendmsg_sz = ::strlen(send_msg);
		send_msg[sendmsg_sz] = '\0';
		sendmsg_sz++;
		//printf("send_msg:%s\n", send_msg);
		int sendlen = send(send_sock,send_msg,sendmsg_sz,0);
		if (sendlen == -1)
			error_handling("sendto error!");
		else printf("send has done\n");
	}
	closesocket(send_sock);
	closesocket(recv_sock);
	WSACleanup();
	return 0;
}
 
void error_handling(const char* message)
{
	printf("%s\n", message);
	exit(1);
}

以下是在虚拟机上调试的结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值