************************************************************************
IMPLEMENTATION NOTE:
man socket states that SIGPIPE will be raised if a process writes to a
broken pipe (when the other side has hung up). So for robustness, this
signal has to be handled.
ANOTHER NOTE:
UDP is a connectionless protocol so a very tiny hack has to be used: if the
listen-connection receives a message, it _must_ be a connect-message, since
it will be lost. For each message that arrives at the listen-connection, a
new data-connection will be created which will receive all further messages
from the client, but the message will be lost to the data-connection.
Therefore, the first message sent to the server _must_ be a connect-message,
so a data-connection is created at the serverside.
**************************************************************************/
const char connect_string[] = "CS++:Connect";
//---------------------------- udpsocket implementation -------------------
int udpsocket::_got_sigpipe = 0;
int udpsocket::_handler_installed = 0;
void (*udpsocket::_old_sigpipe_handler)(int) = NULL;
void udpsocket::sigpipe_handler(int signr)
{
signal(SIGPIPE, sigpipe_handler);
_got_sigpipe = 1;
if (_old_sigpipe_handler != NULL)
(*_old_sigpipe_handler)(signr);
if (cs_trace)
printf("udpsocket::sigpipe_handler(%d)\n", signr);
}
udpsocket::udpsocket()
{
if (!_handler_installed)
{
_old_sigpipe_handler = signal(SIGPIPE, sigpipe_handler);
_handler_installed = 1;
}
_portnr = -1;
_name.sin_family = 0;
_name.sin_port = 0;
_name.sin_addr.s_addr = 0;
_sd = -1;
}
int udpsocket::portnr() const
{
struct sockaddr addr;
int addrlen;
// hack to keep this function 'const'
udpsocket* uptr = (udpsocket*) this;
if (_portnr != -1)
return _portnr; // portnumber already known
addrlen = sizeof(addr);
if (getsockname(_sd, &addr, &addrlen) < 0)
{
warn("udp_listenconn::portnr : getsockname failed (%s)",
strerror(errno));
return -1;
}
struct sockaddr_in* ptr = (struct sockaddr_in*) &addr;
uptr -> _portnr = ntohs(ptr -> sin_port);
return uptr -> _portnr;
}
void udpsocket::close()
{
::close(_sd);
_sd = -1;
}
//--------------------------- udp_dataconn implementation --------------------
// constructor for client-side
udp_dataconn::udp_dataconn(const csaddress* addr)
:udpsocket(), data_connection(addr)
{
char buf[128];
sockaddr_in dst;
int dstlen = sizeof(dst);
if (addr == NULL)
{
warn("udp_dataconn::udp_dataconn : addr = NULL");
return;
}
// make socket connection
struct hostent* hp;
inet_address* a = (inet_address*) addr;
char* hname = (char*) a -> hostname();
if (cs_trace)
printf("hname = %s, portnr = %d\n", a -> hostname(), a -> portnr());
if ((_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
warn("udp_dataconn::udp_dataconn : couldn't create socket (%s)",
strerror(errno));
return;
}
_name.sin_family = AF_INET;
_name.sin_port = htons(a -> portnr());
if ((hname == NULL) || (strlen(hname) == 0))
{
warn("udp_dataconn::udp_dataconn : hostname = NULL or \"\"");
::close(_fd);
return;
}
else
{
hp = gethostbyname(hname);
if (hp == NULL)
hp = gethostbyaddr(hname, strlen(hname), AF_INET);
if (hp == NULL)
{
warn("udp_dataconn::udp_dataconn : unknown host %s", hname);
::close(_fd);
return;
}
memcpy(&_name.sin_addr.s_addr, hp -> h_addr, hp -> h_length);
}
if (sendto(_fd, connect_string, strlen(connect_string)+1, 0,
(struct sockaddr*) &_name, sizeof(_name)) < 0)
{
perror("INET Domain sendto");
exit(2);
}
// wait for reply from server
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(_fd, &readfds);
timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
int got_data = 0;
if (cs_trace)
printf("waiting for reply...\n");
do
{
if (select(_fd+1, &readfds, NULL, NULL, &tv) > 0)
got_data = 1;
}
while ((tv.tv_usec != 0) || (tv.tv_sec != 0));
if (!got_data)
{
warn("udp_dataconn::udp_dataconn : didn't get connection ACK");
::close(_fd);
return;
}
recvfrom(_fd, buf, sizeof(buf), 0, (sockaddr*) &dst, &dstlen);
if (cs_trace > 1)
printf("received: %s\n", buf);
if (connect(_fd, (struct sockaddr*) &dst, dstlen) < 0)
{
warn("udp_dataconn::udp_dataconn : couldn't connect (%s)",
strerror(errno));
::close(_fd);
return;
}
// set socketdescriptor
_sd = _fd;
_connected = 1;
}
// constructor for server-side
udp_dataconn::udp_dataconn(int fd)
:udpsocket(), data_connection()
{
_fd = fd;
_sd = fd;
// since this constructor will be called by the receiving side of a
// connection, the connection is already a fact
_connected = 1;
}
udp_dataconn::~udp_dataconn()
{
close();
}
int udp_dataconn::readmsg(char* buf, int nrbytes)
{
// return read(buf, nrbytes);
return recv(_fd, buf, nrbytes, 0);
}
int udp_dataconn::writemsg(const char* buf, int nrbytes)
{
// return write(buf, nrbytes);
return send(_fd, buf, nrbytes, 0);
}
//------------------------ udp_listenconn implementation --------------------
udp_listenconn::udp_listenconn(const csaddress* addr)
:udpsocket(), listen_connection(addr)
{
/* DEBUG
// make socket connection
struct hostent* hp;
inet_address* a = (inet_address*) _address; // this should be tested!!!
char* hname = NULL;
if ((_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
warn("udp_listenconn::udp_listenconn : couldn't create socket (%s)",
strerror(errno));
return;
}
_name.sin_family = AF_INET;
if (a != NULL)
{
hname = (char*) a -> hostname();
_name.sin_port = htons(a -> portnr());
}
else
{
hname = NULL;
_name.sin_port = 0;
}
if (hname == NULL)
_name.sin_addr.s_addr = INADDR_ANY;
else
{
hp = gethostbyname(hname);
if (hp == NULL)
hp = gethostbyaddr(hname, strlen(hname), AF_INET);
if (hp == NULL)
{
warn("udp_listenconn::udp_listenconn : unknown host %s", hname);
::close(_fd);
return;
}
memcpy(&_name.sin_addr.s_addr, hp -> h_addr, hp -> h_length);
}
if (bind(_fd, (struct sockaddr*) &_name, sizeof(_name)) < 0)
{
warn("udp_listenconn::udp_listenconn : couldn't bind socket (%s)",
strerror(errno));
::close(_fd);
return;
}
// set socketdescriptor
_sd = _fd;
_connected = 1; // we're on the air now
*/
if (cs_trace)
printf("udp_listenconn::udp_listenconn\n");
struct sockaddr_in name;
int ld;
if ((ld = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
perror("socket");
exit(1);
}
name.sin_family = AF_INET;
name.sin_port = 0;
name.sin_addr.s_addr = INADDR_ANY;
if ( bind( ld, (sockaddr*) &name, sizeof( name ) ) < 0 )
{
perror("INET Domain bind");
exit(2);
}
_sd = ld;
_fd = ld;
_connected = 1;
}
udp_listenconn::~udp_listenconn()
{
close();
}
data_connection* udp_listenconn::waitconnect()
{
int new_sd;
data_connection* new_conn = NULL;
sockaddr from;
int fromlen;
char buf[256];
if (cs_trace)
printf("waitconnect\n");
// every packet that arrives will come from a new client
fromlen = sizeof(from);
// message can't be forwarded to correct socket, so just read it (no peek)
if (recvfrom(_fd, buf, sizeof(buf), 0, &from, &fromlen) < 0)
{
warn("udp_listenconn::waitconnect : could not receive (%s)",
strerror(errno));
return NULL;
}
if (strncmp(buf, connect_string, sizeof(connect_string)) != 0)
{
warn("udp_listenconn::waitconnect : received data message (%s), "
"ignoring", buf);
}
else
warn("udp_listenconn:waitconnect : got connect string");
if ((new_sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
warn("udp_listenconn::waitconnect : couldn't create new socket (%s)",
strerror(errno));
return NULL;
}
if (connect(new_sd, &from, fromlen) != 0)
{
warn("udp_listenconn::waitconnect : couldn't connect (%s)",
strerror(errno));
return NULL;
}
if (sendto(new_sd, buf, strlen(buf)+1, 0, &from, fromlen) == -1)
perror("sendto");
else
{
if (cs_trace)
printf("sent\n");
}
if (!(new_conn = new udp_dataconn(new_sd)))
{
warn("udp_listenconn::waitconnect : couldn't create new "
"data_connection");
return NULL;
}
// some questions here: has the data now moved from the listen-socket to
// the data-socket? NO!!!
// if so, will binding a handler to the data-socket
// immediately cause the handler to be called as is
// wanted? NO!!!
// solution: always send a connection-message to make yourself known
return new_conn;
}
//------------------------ udp_client implementation ---------------------
udp_client::udp_client()
:csclient(_udpimp = new udp_clientimp())
{
_inetaddr = NULL;
}
udp_client::udp_client(const char* hostname, int portnr)
:csclient(_udpimp = new udp_clientimp(),
_inetaddr = new inet_address(hostname, portnr))
{
}
udp_client::~udp_client()
{
if (_inetaddr != NULL)
delete _inetaddr;
delete _udpimp;
}
int udp_client::connect(const char* hostname, int portnr)
{
if (_inetaddr != NULL)
delete _inetaddr;
_inetaddr = new inet_address(hostname, portnr);
write_to_log("udp_client::connect with %s at port %d", hostname, portnr);
return csclient::connect(_inetaddr);
}
//------------------------ udp_server implementation ---------------------
udp_server::udp_server()
:csserver(_udpimp = new udp_serverimp())
{
}
udp_server::~udp_server()
{
delete _udpimp;
}
int udp_server::portnr() const
{
udp_listenconn* tlc = (udp_listenconn*) listenconn();
if (tlc != NULL)
return tlc -> portnr();
else
return -1;
}
//--------------------- udp_clientimp implementation -----------------------
udp_clientimp::udp_clientimp()
{
}
data_connection* udp_clientimp::createdata(const csaddress* addr) const
{
return new udp_dataconn(addr);
}
//------------------------- udp_serverimp ------------------------------
udp_serverimp::udp_serverimp()
{
}
listen_connection* udp_serverimp::createlisten(const csaddress* addr) const
{
return new udp_listenconn(addr);
}
[.]
Papers
Tutorials
Examples
Manuals
Interfaces
Sources
Packages
Resources
?