190 lines
4.6 KiB
C
190 lines
4.6 KiB
C
|
#ifndef __SOCKET_UDP_INCOMING_H__
|
||
|
#define __SOCKET_UDP_INCOMING_H__
|
||
|
|
||
|
#include "pandabase.h"
|
||
|
#include "socket_ip.h"
|
||
|
|
||
|
/**
|
||
|
* Base functionality for a UDP Reader
|
||
|
*/
|
||
|
class EXPCL_PANDA_NATIVENET Socket_UDP_Incoming : public Socket_IP {
|
||
|
PUBLISHED:
|
||
|
inline Socket_UDP_Incoming() {}
|
||
|
|
||
|
inline bool OpenForInput(unsigned short port);
|
||
|
inline bool OpenForInput(const Socket_Address &address);
|
||
|
inline bool OpenForInputMCast(const Socket_Address &address);
|
||
|
inline bool GetPacket(char *data, int *max_len, Socket_Address &address);
|
||
|
inline bool SendTo(const char *data, int len, const Socket_Address &address);
|
||
|
inline bool InitNoAddress();
|
||
|
inline bool SetToBroadCast();
|
||
|
|
||
|
public:
|
||
|
static TypeHandle get_class_type() {
|
||
|
return _type_handle;
|
||
|
}
|
||
|
static void init_type() {
|
||
|
Socket_IP::init_type();
|
||
|
register_type(_type_handle, "Socket_UDP_Incoming",
|
||
|
Socket_IP::get_class_type());
|
||
|
}
|
||
|
virtual TypeHandle get_type() const {
|
||
|
return get_class_type();
|
||
|
}
|
||
|
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||
|
|
||
|
private:
|
||
|
static TypeHandle _type_handle;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Flips the OS bits that allow for brodcast packets to come in on this port.
|
||
|
*/
|
||
|
inline bool Socket_UDP_Incoming::
|
||
|
SetToBroadCast() {
|
||
|
int optval = 1;
|
||
|
|
||
|
if (setsockopt(_socket, SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval)) != 0) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set this socket to work without a bound external address.
|
||
|
*/
|
||
|
inline bool Socket_UDP_Incoming::
|
||
|
InitNoAddress() {
|
||
|
Close();
|
||
|
|
||
|
if (support_ipv6) {
|
||
|
// Create a socket supporting both IPv4 and IPv6.
|
||
|
_socket = DO_NEWUDP(AF_INET6);
|
||
|
SetV6Only(false);
|
||
|
} else {
|
||
|
_socket = DO_NEWUDP(AF_INET);
|
||
|
}
|
||
|
|
||
|
return (_socket != BAD_SOCKET);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts a UDP socket listening on a port
|
||
|
*/
|
||
|
inline bool Socket_UDP_Incoming::
|
||
|
OpenForInput(unsigned short port) {
|
||
|
Close();
|
||
|
|
||
|
Socket_Address address;
|
||
|
if (support_ipv6) {
|
||
|
// Create a socket supporting both IPv4 and IPv6.
|
||
|
address.set_any_IPv6(port);
|
||
|
_socket = DO_NEWUDP(AF_INET6);
|
||
|
SetV6Only(false);
|
||
|
} else {
|
||
|
address.set_any_IP(port);
|
||
|
_socket = DO_NEWUDP(AF_INET);
|
||
|
}
|
||
|
|
||
|
if (_socket == BAD_SOCKET) {
|
||
|
return ErrorClose();
|
||
|
}
|
||
|
|
||
|
if (DO_BIND(_socket, &address.GetAddressInfo()) != 0) {
|
||
|
return ErrorClose();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts a UDP socket listening on a port
|
||
|
*/
|
||
|
inline bool Socket_UDP_Incoming::
|
||
|
OpenForInput(const Socket_Address &address) {
|
||
|
Close();
|
||
|
_socket = DO_NEWUDP(address.get_family());
|
||
|
if (_socket == BAD_SOCKET) {
|
||
|
return ErrorClose();
|
||
|
}
|
||
|
|
||
|
if (DO_BIND(_socket, &address.GetAddressInfo()) != 0) {
|
||
|
return ErrorClose();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts a UDP socket listening on a port
|
||
|
*/
|
||
|
inline bool Socket_UDP_Incoming::
|
||
|
OpenForInputMCast(const Socket_Address &address) {
|
||
|
Close();
|
||
|
_socket = DO_NEWUDP(address.get_family());
|
||
|
if (_socket == BAD_SOCKET) {
|
||
|
return ErrorClose();
|
||
|
}
|
||
|
|
||
|
Socket_Address wa1(address.get_port());
|
||
|
if (DO_BIND(_socket, &wa1.GetAddressInfo()) != 0) {
|
||
|
return ErrorClose();
|
||
|
}
|
||
|
|
||
|
int status = -1;
|
||
|
|
||
|
const sockaddr *addr = &address.GetAddressInfo();
|
||
|
if (addr->sa_family == AF_INET) {
|
||
|
struct ip_mreq imreq;
|
||
|
memset(&imreq, 0, sizeof(imreq));
|
||
|
imreq.imr_multiaddr.s_addr = ((sockaddr_in*)addr)->sin_addr.s_addr;
|
||
|
imreq.imr_interface.s_addr = INADDR_ANY; // use DEFAULT interface
|
||
|
|
||
|
status = setsockopt(GetSocket(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||
|
(const char *)&imreq, sizeof(imreq));
|
||
|
|
||
|
} else if (addr->sa_family == AF_INET6) {
|
||
|
struct ipv6_mreq imreq;
|
||
|
memcpy(&imreq.ipv6mr_multiaddr,
|
||
|
&(((struct sockaddr_in6 *)addr)->sin6_addr),
|
||
|
sizeof(struct in6_addr));
|
||
|
imreq.ipv6mr_interface = 0; // use DEFAULT interface
|
||
|
|
||
|
status = setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_JOIN_GROUP,
|
||
|
(const char *)&imreq, sizeof(imreq));
|
||
|
}
|
||
|
|
||
|
return (status == 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Grabs a dataset off the listening UDP socket and fills in the source
|
||
|
* address information
|
||
|
*
|
||
|
*/
|
||
|
inline bool Socket_UDP_Incoming::
|
||
|
GetPacket(char *data, int *max_len, Socket_Address &address) {
|
||
|
int val = DO_RECV_FROM(_socket, data, *max_len, &address.GetAddressInfo());
|
||
|
*max_len = 0;
|
||
|
|
||
|
if (val <= 0) {
|
||
|
if (GetLastError() != LOCAL_BLOCKING_ERROR) { // im treating a blocking error as a 0 lenght read
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
*max_len = val;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Send data to specified address
|
||
|
*/
|
||
|
inline bool Socket_UDP_Incoming::
|
||
|
SendTo(const char *data, int len, const Socket_Address &address) {
|
||
|
return (DO_SOCKET_WRITE_TO(_socket, data, len, &address.GetAddressInfo()) == len);
|
||
|
}
|
||
|
|
||
|
#endif //__SOCKET_UDP_INCOMING_H__
|