// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdnet.h"
#include "nel/net/udp_sock.h"
#include "nel/net/net_log.h"
#ifdef NL_OS_WINDOWS
# include
# define NOMINMAX
# include
# define socklen_t int
# define ERROR_NUM WSAGetLastError()
#elif defined NL_OS_UNIX
# include
# include
# include
# include
# include
# include
# include
# include
# include
//#include
# define SOCKET_ERROR -1
# define INVALID_SOCKET -1
# define ERROR_NUM errno
# define ERROR_MSG strerror(errno)
typedef int SOCKET;
#endif
using namespace NLMISC;
namespace NLNET {
/*
* Constructor
*/
CUdpSock::CUdpSock( bool logging ) :
CSock( logging ),
_Bound( false )
{
// Socket creation
createSocket( SOCK_DGRAM, IPPROTO_UDP );
}
/** Binds the socket to the specified port. Call bind() for an unreliable socket if the host acts as a server and waits for
* messages. If the host acts as a client, call sendTo(), there is no need to bind the socket.
*/
void CUdpSock::bind( uint16 port )
{
CInetAddress addr; // any IP address
addr.setPort( port );
bind( addr );
setLocalAddress(); // will not set the address if the host is multihomed, use bind(CInetAddress) instead
}
/*
* Same as bind(uint16) but binds on a specified address/port (useful when the host has several addresses)
*/
void CUdpSock::bind( const CInetAddress& addr )
{
#ifndef NL_OS_WINDOWS
// Set Reuse Address On (does not work on Win98 and is useless on Win2000)
int value = true;
if ( setsockopt( _Sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value) ) == SOCKET_ERROR )
{
throw ESocket( "ReuseAddr failed" );
}
#endif
_LocalAddr = addr;
// Bind the socket
if ( ::bind( _Sock, (sockaddr*)(_LocalAddr.sockAddr()), sizeof(sockaddr) ) == SOCKET_ERROR )
{
throw ESocket( "Bind failed" );
}
_Bound = true;
if ( _Logging )
{
LNETL0_DEBUG( "LNETL0: Socket %d bound at %s", _Sock, _LocalAddr.asString().c_str() );
}
}
/*
* Sends a message
*/
void CUdpSock::sendTo( const uint8 *buffer, uint len, const CInetAddress& addr )
{
// Send
if ( ::sendto( _Sock, (const char*)buffer, len, 0, (sockaddr*)(addr.sockAddr()), sizeof(sockaddr) ) != (sint32)len )
{
throw ESocket( "Unable to send datagram" );
}
_BytesSent += len;
if ( _Logging )
{
LNETL0_DEBUG( "LNETL0: Socket %d sent %d bytes to %s", _Sock, len, addr.asString().c_str() );
}
// If socket is unbound, retrieve local address
if ( ! _Bound )
{
setLocalAddress();
_Bound = true;
}
#ifdef NL_OS_WINDOWS
// temporary by ace to know size of SO_MAX_MSG_SIZE
static bool first = true;
if (first)
{
uint MMS, SB;
int size = sizeof (MMS);
getsockopt (_Sock, SOL_SOCKET, SO_SNDBUF, (char *)&SB, &size);
getsockopt (_Sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&MMS, &size);
LNETL0_INFO ("LNETL0: The udp SO_MAX_MSG_SIZE=%u, SO_SNDBUF=%u", MMS, SB);
first = false;
}
#endif
}
/*
* Receives data from the peer. (blocking function)
*/
bool CUdpSock::receive( uint8 *buffer, uint32& len, bool throw_exception )
{
nlassert( _Connected && (buffer!=NULL) );
// Receive incoming message
len = ::recv( _Sock, (char*)buffer, len , 0 );
// Check for errors (after setting the address)
if ( ((int)len) == SOCKET_ERROR )
{
if ( throw_exception )
throw ESocket( "Cannot receive data" );
return false;
}
_BytesReceived += len;
if ( _Logging )
{
LNETL0_DEBUG( "LNETL0: Socket %d received %d bytes from peer %s", _Sock, len, _RemoteAddr.asString().c_str() );
}
return true;
}
/*
* Receives data and say who the sender is. (blocking function)
*/
bool CUdpSock::receivedFrom( uint8 *buffer, uint& len, CInetAddress& addr, bool throw_exception )
{
// Receive incoming message
sockaddr_in saddr;
socklen_t saddrlen = sizeof(saddr);
len = ::recvfrom( _Sock, (char*)buffer, len , 0, (sockaddr*)&saddr, &saddrlen );
// If an error occurs, the saddr is not valid
// When the remote socket is closed, get sender's address to know who is quitting
addr.setSockAddr( &saddr );
// Check for errors (after setting the address)
if ( ((int)len) == SOCKET_ERROR )
{
if ( throw_exception )
throw ESocket( "Cannot receive data" );
return false;
}
_BytesReceived += len;
if ( _Logging )
{
LNETL0_DEBUG( "LNETL0: Socket %d received %d bytes from %s", _Sock, len, addr.asString().c_str() );
}
return true;
}
} // NLNET