Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
364 lines (299 sloc) 7.67 KB
#ifndef com_sleepless_udpsocket_h
#define com_sleepless_udpsocket_h
// Copyright 2002-2004
// Sleepless Software Inc.
// All Rights Reserved
#include "socket.cpp"
#include "str.cpp"
struct UDPSocket
{
bool ready;
SOCKET sock;
int localPort;
char remoteHost[128];
int remotePort;
unsigned long bytesIn;
unsigned long bytesOut;
unsigned long pktsIn;
unsigned long pktsOut;
// Constructs a udp socket and binds it to any available port
// on the local host machine. The socket is then connected to
// remote host and port specified.
UDPSocket(const char *rh, int rp)
{
create(0, rh, rp);
}
// Constructs a udp socket and binds it to specified port
// on the local host machine. The socket remains unconnected.
UDPSocket(int lp)
{
create(lp, 0, 0);
}
void create(int lp, const char *rh, int rp)
{
bytesIn = 0;
bytesOut = 0;
pktsIn = 0;
pktsOut = 0;
// Initialize winsock (if we're running on windows)
if(!socketInitSockets())
return;
ready = false;
sock = INVALID_SOCKET;
localPort = 0;
remoteHost[0] = 0;
remotePort = 0;
// Create a new UDP socket
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sock == INVALID_SOCKET)
return;
// Non-blocking I/O
// unsigned long noblock = 1;
// ioctl(s, FIONBIO, &noblock);
// Bind the socket to local port 'lp'.
// If 'lp' is 0, it will be bound to
// any available port number.
struct sockaddr_in sa_u;
sa_u.sin_family = AF_INET;
sa_u.sin_addr.s_addr = htonl(INADDR_ANY);
sa_u.sin_port = htons(lp);
if(bind(sock, (struct sockaddr *)&sa_u, sizeof (sa_u)) == SOCKET_ERROR)
{
closesocket(sock);
sock = INVALID_SOCKET;
return;
}
// Extract port number actually bound to
socklen_t ulen = sizeof(sa_u);
if(getsockname(sock, (struct sockaddr *)&sa_u, &ulen) == SOCKET_ERROR)
{
closesocket(sock);
sock = INVALID_SOCKET;
return;
}
localPort = ntohs(sa_u.sin_port);
if((rh != 0) && (rp != 0))
{
// Connect socket to specified remote host:port
struct hostent *he = gethostbyname(rh); // Lookup IP from hostname
if(he == NULL)
{
localPort = 0;
closesocket(sock);
sock = INVALID_SOCKET;
return;
}
// Connect the socket
struct sockaddr_in sa_ucon;
sa_ucon.sin_family = PF_INET;
sa_ucon.sin_port = htons(rp);
sa_ucon.sin_addr = *((struct in_addr *)he->h_addr);
if(connect(sock, (SOCKADDR *)&sa_ucon, sizeof(sa_ucon)) == SOCKET_ERROR)
{
localPort = 0;
closesocket(sock);
sock = INVALID_SOCKET;
return;
}
remotePort = rp;
Str::copy(remoteHost, rh, sizeof(remoteHost));
}
ready = true;
}
~UDPSocket()
{
//if(sock != INVALID_SOCKET)
// closesocket(sock);
//ready = false;
disconnect();
}
void disconnect()
{
if(sock != INVALID_SOCKET)
closesocket(sock);
ready = false;
}
/* Returns non-zero of there is 1 or more pkts available to read
on the socket */
int available()
{
return socketAvailable(sock);
}
// Returns 0 if socket is unconnected
int getRemotePort()
{
return remotePort;
}
// Returns 0 if socket is unconnected
const char *getRemoteHost()
{
return remoteHost;
}
int getLocalPort()
{
return localPort;
}
/* Receive a datagram.
buf buffer to receive the data
len size of 'buf'
fromiptxt buffer that receives remote host IP as ascii string
must be at least 32 bytes long.
fromport pointer to an int to receive remote port #
fromip pointer to an int to IP of remote host
Returns the # of bytes read into 'buf'
*/
int receive(char *buf, int len, char *fromiptxt, int *fromport, int *fromip)
{
struct sockaddr_in sa;
sa.sin_family = AF_INET;
socklen_t sas = sizeof(sa);
int r = recvfrom(sock, buf, len, 0, (sockaddr *)&sa, &sas);
unsigned int ip = ntohl(sa.sin_addr.s_addr);
if(fromiptxt)
sprintf(fromiptxt, "%d.%d.%d.%d", 0xff&(ip>>24), 0xff&(ip>>16), 0xff&(ip>>8), 0xff&ip);
if(fromport)
*fromport = ntohs(sa.sin_port);
if(fromip)
*fromip = ip;
bytesIn += r;
pktsIn++;
return r;
}
// Send a datagram to a specified host:port.
int transmit(char *buf, int len, unsigned long int toip, int toport)
{
//printf("udp transmit %lu %d\n", toip, toport);
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(toport);
sa.sin_addr.s_addr = htonl(toip);
int r = sendto(sock, buf, len, 0, (sockaddr *)&sa, sizeof(sa));
bytesOut += r;
pktsOut++;
return r;
}
// Send a datagram to a specified host:port.
// NOTE: this method takes an ASCII string for the hostname, which can be
// a symbolic domain name. Resolving this name to an IP addr can
// be relatively slow and time consuming. If you know the IP of
// the host to which you want to send your packet, use the
// transmit(char *, int, int, int) version if you can.
int transmit(char *buf, int len, char *tohost, int toport)
{
struct hostent *he = gethostbyname(tohost);
if(he == NULL)
return SOCKET_ERROR;
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(toport);
sa.sin_addr = *((struct in_addr *)he->h_addr);
int r = sendto(sock, buf, len, 0, (sockaddr *)&sa, sizeof(sa));
bytesOut += r;
pktsOut++;
return r;
}
// Send a datagram to the host:port to which this socket is connected.
// If the socket is not connected, -1 is returned.
int transmit(char *buf, int len)
{
int r = send(sock, buf, len, 0);
bytesOut += r;
pktsOut++;
return r;
}
};
#ifdef TEST
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
if(argc == 2)
{
printf("Server mode\n");
int localport = atoi(argv[1]);
UDPSocket udps(localport);
if(!udps.ready)
{
printf("Couldn't create UDP socket on port %d\n", localport);
return 1;
}
printf("socket bound to port %d\n", udps.getLocalPort());
printf("socket remote host is %s\n", udps.getRemoteHost());
printf("socket remote port is %d\n", udps.getRemotePort());
char buf[256];
char fromhost[100];
int fromport;
int r;
while(true)
{
r = udps.receive(buf, sizeof(buf), fromhost, &fromport, 0);
if(r < 1)
{
printf("RECV ERROR: r = %d\n", r);
}
else
{
printf("%d byte UDP packet received from %s:%d\n", r, fromhost, fromport);
printf("received %s", buf);
if(Str::equals(buf, ".\n"))
break;
printf("echoing packet back ...\n");
r = udps.transmit(buf, r, fromhost, fromport);
if(r < 1)
printf("XMIT ERROR: r = %d\n", r);
printf("sent %s", buf);
}
}
printf("Done.\n");
}
else
if(argc == 3)
{
printf("Client mode\n");
char *remotehost = argv[1];
int remoteport = atoi(argv[2]);
UDPSocket udps(remotehost, remoteport);
if(!udps.ready)
{
printf("Couldn't create UDP socket for %s:%d\n", remotehost, remoteport);
return 1;
}
printf("socket bound to port %d\n", udps.getLocalPort());
printf("socket remote host is %s\n", udps.getRemoteHost());
printf("socket remote port is %d\n", udps.getRemotePort());
char buf[256];
char fromhost[100];
int fromport;
int r;
while(fgets(buf, sizeof(buf), stdin))
{
if(Str::equals(buf, ".\n"))
break;
r = udps.transmit(buf, strlen(buf) + 1);
if(r != (int)strlen(buf) + 1)
{
printf("XMIT ERROR: r = %d\n", r);
}
else
{
printf("sent %d bytes: %s", r, buf);
r = udps.receive(buf, sizeof(buf), fromhost, &fromport, 0);
if(r < 1)
printf("RECV ERROR: r = %d\n", r);
else
printf("received %d bytes: %s", r, buf);
}
}
udps.transmit(".\n", 3, remotehost, remoteport);
printf("Done.\n");
}
else
{
printf("Server mode usage: %s localport\n", argv[0]);
printf("Client mode usage: %s remotehost remoteport\n", argv[0]);
return 1;
}
return 0;
}
#endif
#endif // com_sleepless_udpsocket_h