Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

WeJr

C++ Socket UDP Broadcast

Recommended Posts

Estou criando um programa que deve se comunicar através de broadcast já que não haverá um servidor definido. Tentei criar uma classe para a conexão, mas só consegui resultados com TCP.

 

Em Python consegui consegui fazer. Aprendi o básico da linguagem e através de alguns tutoriais criei a classe para a conexão, mas por enquanto quero fazer em C++.

 

Fazendo o mesmo que o script Python meu código em C++ parece enviar e receber algo, mas ele só recebe em uma segunda instância do programa e mesmo assim não exibe nada.

Funciona somente se o endereço de broadcast for de uma interface especifica (192.168.2.255). Não sei porque isso acontece, mas a interface é do VMWare.

 

Código C++:

#pragma once
#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")

#define DEFAULT_PORT "6881"
#define MAX_PACKET_SIZE 1000000

SOCKET NetworkSocket;

int _tmain(int argc, _TCHAR* argv[])
{
	//Criar socket UDP {

	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2), &wsaData);
	NetworkSocket = INVALID_SOCKET;
	NetworkSocket = socket(AF_INET, SOCK_DGRAM, 0);
	setsockopt(NetworkSocket, SOL_SOCKET, SO_REUSEADDR, (char*)1, sizeof((char*)1));
	setsockopt(NetworkSocket, SOL_SOCKET, SO_BROADCAST, (char*)1, sizeof((char*)1));
	struct addrinfo* result = NULL, hints;
	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_protocol = 0;
	getaddrinfo("", DEFAULT_PORT, &hints, &result);
	bind(NetworkSocket, result->ai_addr, (int)result->ai_addrlen);

	// } addrinfo broadcast {

	result = NULL;
	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_DGRAM;
	getaddrinfo("255.255.255.255", DEFAULT_PORT, &hints, &result);

	// } Enviar {

	char* data = "Ola";
	sendto(NetworkSocket, data, sizeof(data), 0, result->ai_addr, (int)result->ai_addrlen);
	printf("Send: |%s|\n", data);

	// } Receber {

	char network_data[MAX_PACKET_SIZE];
	recv(NetworkSocket, network_data, MAX_PACKET_SIZE, 0);
	printf("Receive: |%s|\n", network_data);

	// }

	while(true){
	}
	return 0;
}

 

Código Python:

import socket

class Network:

    DEFAULT_PORT = 6881
    _MAX_PACKET_SIZE = 1000000

    def __init__(self):
        self.NetworkSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.NetworkSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.NetworkSocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.NetworkSocket.bind(('',self.DEFAULT_PORT))

    def send(self, data, address = False, ip = False):
        if address == False:
            if ip == False:
                address = ('<broadcast>', self.DEFAULT_PORT)
            else:
                address = (ip, self.DEFAULT_PORT)
        self.NetworkSocket.sendto(data, address)
        return True

    def recv(self):
        return self.NetworkSocket.recvfrom(self._MAX_PACKET_SIZE)

 

Para realizar alguns testes da classe em Python criei o main para receber alguns argumentos e realizar as operações:

from network import Network
import sys
import getopt

def main(argv):
    try:
       opts, args = getopt.getopt(argv,"hrs",["help","receive","send","times=","string=","ipaddress="])
    except getopt.GetoptError:
        print('test.py (-r [--times=<TIMES>] | -s --string=<STRING> [--ipaddress=<IPADDRESS>])')
        sys.exit(2)

    times = None
    str  = None
    IPaddr = None
    if   ('-h', '') in opts or ('--help','') in opts:
        print('test.py (-r [--times=<TIMES>] | -s --string=<STRING> [--ipaddress=<IPADDRESS>])')
    elif ('-r', '') in opts or ('--receive','') in opts:
        Net = Network()
        for opt, arg in opts:
            if opt == '--times':
                times = arg
        if times != None:
            if times == 'ever':
                while True:
                    message, address = Net.recv()
                    print("Message:", message.decode(), "Address:", address[0])
            else:
                times = int(times)
                while times != 0:
                    message, address = Net.recv()
                    print("Message:", message.decode(), "Address:", address[0])
                    times -= 1
        else:
            message, address = Net.recv()
            print("Message:", message.decode(), "Address:", address[0])
    elif ('-s', '') in opts or ('--send','') in opts:
        for opt, arg in opts:
            if opt == '--string':
                str = arg
            elif opt == '--ipaddress':
                IPaddr = arg
        if str != None:
            if IPaddr != None:
                Net = Network()
                Net.send(bytes(str,"UTF-8"),ip=IPaddr)
            else:
                Net = Network()
                Net.send(bytes(str,"UTF-8"))
        else:
            print('test.py (-r [--times=<TIMES>] | -s --string=<STRING> [--ipaddress=<IPADDRESS>])')
    else:
        print('test.py (-r [--times=<TIMES>] | -s --string=<STRING> [--ipaddress=<IPADDRESS>])')
    sys.exit()

if __name__ == "__main__":
   main(sys.argv[1:])

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

tentou colocar um analisador de protocolo tipo Wireshark para ver a comunicação?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Sim.

Com o programa em C++, consigo somente observar alguma comunicação se o endereço de broadcast for 192.168.2.255, mas em Python está funcionando para todos.

 

Agora desativei as interfaces de rede, com exceção de uma, e tentei novamente.

Em C++ só funcionou com o endereço de broadcast da interface que ficou ativa.

 

Acho que o programa em C++ está enviando somente se o endereço de broadcast for o da interface ativa com maior prioridade ou algo assim.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Os dois utilizam a mesma biblioteca de sockets do windows.

Pesquisou na documentação MDSN se tem alguma configuração pra fazer?

 

http://msdn.microsoft.com/pt-br/library/aa916134.aspx

Compartilhar este post


Link para o post
Compartilhar em outros sites

Já pesquisei muito nesta documentação, mas achei poucas respostas.

 

Parece que por algum motivo o broadcast para 255.255.255.255 não funciona em C++.

Dessa forma eu teria que criar um socket para cada rede em que o computador estiver conectado e teria que executar cada operação de envio ou recebimento em todos, se tornando algo complicado.

 

Se o endereço da rede na qual o socket está associado for 192.168.1.1 o endereço broadcast para envio deve ser o dessa rede, como, por exemplo, 192.168.1.255. Não funciona enviar para 255.255.255.255 ou receber de todas interfaces.

 

Código C++:

(só fiz algumas mudanças na forma como o endereço é configurado)

#pragma once
#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")

#define DEFAULT_PORT 6881
#define MAX_PACKET_SIZE 1000000

SOCKET NetworkSocket;

int _tmain(int argc, _TCHAR* argv[])
{
	//Criar socket UDP {

	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2), &wsaData);

	NetworkSocket = INVALID_SOCKET;
	NetworkSocket = socket(AF_INET, SOCK_DGRAM, 0);

	setsockopt(NetworkSocket, SOL_SOCKET, SO_REUSEADDR, (char*)1, sizeof((char*)1));
	setsockopt(NetworkSocket, SOL_SOCKET, SO_BROADCAST, (char*)1, sizeof((char*)1));

	struct sockaddr_in localAddr;
	ZeroMemory(&localAddr, sizeof(localAddr));
	localAddr.sin_family = AF_INET;
	localAddr.sin_port = htons(DEFAULT_PORT);
	localAddr.sin_addr.s_addr  = inet_addr("192.168.1.1");

	bind(NetworkSocket, (sockaddr*)&localAddr, sizeof(localAddr));

	// } sockaddr_in broadcast {

	struct sockaddr_in broadcastAddr;
	ZeroMemory(&broadcastAddr, sizeof(broadcastAddr));
	broadcastAddr.sin_family = AF_INET;
	broadcastAddr.sin_port = htons(DEFAULT_PORT);
	broadcastAddr.sin_addr.s_addr  = inet_addr("192.168.1.255");

	// } Enviar {

	char* data = "Ola";
	sendto(NetworkSocket, data, sizeof(data), 0, (sockaddr*)&broadcastAddr, sizeof(broadcastAddr));
	printf("Send: |%s|\n", data);

	// } Receber {

	char network_data[MAX_PACKET_SIZE];
	recv(NetworkSocket, network_data, MAX_PACKET_SIZE, 0);
	printf("Receive: |%s|\n", network_data);

	// }

	while(true){
	}
	return 0;
}

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Achei um pequeno erro que impedia o envio de broadcast para 255.255.255.255, mas mesmo assim a mensagem só vai para uma rede.

 

Stack Overflow - UDP-Broadcast on all interfaces: http://stackoverflow.com/questions/683624/udp-broadcast-on-all-interfaces

Se eu quiser enviar para todas as redes terei mesmo que enviar separadamente para cada uma delas.

 

Solução: Vou estudar multicast para ver se pode resolver meu problema, se não, vou implementar o envio por broadcast para cada uma das redes em que o computador estiver conectado.

 

 

Erros no código:

setsockopt(NetworkSocket, SOL_SOCKET, SO_REUSEADDR, (char*)1, sizeof((char*)1));
setsockopt(NetworkSocket, SOL_SOCKET, SO_BROADCAST, (char*)1, sizeof((char*)1));

O certo é assim:

int yes = 1;
setsockopt(NetworkSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
setsockopt(NetworkSocket, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));

 

Código C++ com a correção:

#pragma once
#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")

#define DEFAULT_PORT 6881
#define MAX_PACKET_SIZE 1000000

SOCKET NetworkSocket;

int _tmain(int argc, _TCHAR* argv[])
{
	//Criar socket UDP {

	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2), &wsaData);

	NetworkSocket = INVALID_SOCKET;
	NetworkSocket = socket(AF_INET, SOCK_DGRAM, 0);
	
	int yes = 1;
	setsockopt(NetworkSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
	setsockopt(NetworkSocket, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));

	struct sockaddr_in localAddr;
	ZeroMemory(&localAddr, sizeof(localAddr));
	localAddr.sin_family = AF_INET;
	localAddr.sin_port = htons(DEFAULT_PORT);
	localAddr.sin_addr.s_addr  = INADDR_ANY;

	bind(NetworkSocket, (sockaddr*)&localAddr, sizeof(localAddr));

	// } sockaddr_in broadcast {

	struct sockaddr_in broadcastAddr;
	ZeroMemory(&broadcastAddr, sizeof(broadcastAddr));
	broadcastAddr.sin_family = AF_INET;
	broadcastAddr.sin_port = htons(DEFAULT_PORT);
	broadcastAddr.sin_addr.s_addr  = INADDR_BROADCAST;

	// } Enviar {

	char* data = "Ola";
	sendto(NetworkSocket, data, sizeof(data), 0, (sockaddr*)&broadcastAddr, sizeof(broadcastAddr));
	printf("Send: |%s|\n", data);

	// } Receber {

	char network_data[MAX_PACKET_SIZE];
	recv(NetworkSocket, network_data, MAX_PACKET_SIZE, 0);
	printf("Receive: |%s|\n", network_data);

	// }

	while(true){
	}
	return 0;
}

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.