/* * NetMessage.cpp: implementation for sending BMessages across the net * * This is unsupported demo code. Use at your own risk. */ #include "NetMessage.h" #include #include #include #include #include #include #include static const char TRANSACTION_ID[] = "!TransactionID!"; NetClient::NetClient(const char *hostname, int port) { struct hostent *h; h = gethostbyname(hostname); if (h == NULL) { address = 0; } else { memcpy(&this->address, h->h_addr, sizeof(this->address)); } this->port = port; this->tid = fmod(system_time(), (double)UINT_MAX + 1); this->timeout = 10; /* 10 second total timeout */ this->retrans = 4; /* with 4 retransmissions, 1 packet every 2 seconds */ this->sock = socket(AF_INET, SOCK_DGRAM, 0); this->replydata = (char *)malloc(B_UDP_MAX_SIZE); } NetClient::~NetClient(void) { if (sock >= 0) { closesocket(sock); sock = -1; if (replydata) { free(replydata); replydata = NULL; } } } static int waitpacket( int s, double to ) { struct timeval tv; struct fd_set fds; tv.tv_sec = (long)to / 1000000; tv.tv_usec = (long)to % 1000000; FD_ZERO(&fds); FD_SET(s, &fds); return (select(s + 1, &fds, NULL, NULL, &tv) == 1); } long NetClient::SendMessage( BMessage *request, BMessage **replyp ) { char *data; long size; struct sockaddr_in sin; int i; int trans; BMessage *reply; int res; double to; struct sockaddr_in from; int fromlen; unsigned long replytid; unsigned long mytid; double this_to; double then; double now; request->AddLong(TRANSACTION_ID, (long)(mytid = tid++)); request->Flatten(&data, &size); sin.sin_family = AF_INET; sin.sin_port = port; sin.sin_addr.s_addr = address; memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); trans = retrans + 1; to = (timeout * 1000000.0) / trans; for (i = 0; i < trans; i++) { res = sendto(this->sock, data, size, 0, (struct sockaddr *)&sin, sizeof(sin)); if (res < 0) { free(data); return (NET_CANT_SEND); } this_to = to; then = system_time(); while (this_to > 0 && waitpacket(this->sock, this_to)) { fromlen = sizeof(from); res = recvfrom(this->sock, replydata, B_UDP_MAX_SIZE, 0, (struct sockaddr *)&from, &fromlen); if (res < 0) { free(data); return (NET_CANT_RECV); } reply = new BMessage(); reply->Unflatten(replydata); if (!reply->HasLong(TRANSACTION_ID)) { /* bogus packet: ignore */ } else { replytid = reply->FindLong(TRANSACTION_ID); if (replytid == mytid) { *replyp = reply; free(data); return (B_NO_ERROR); } else { /* bogus packet: ignore */ } } delete reply; /* * Update our timeout and wait again * This way, a bogus packet doesn't screw up * our timeouts */ now = system_time(); this_to -= (now - then); then = now; } } free(data); return (B_TIMED_OUT); } NetServer::NetServer(int port) { struct sockaddr_in sin; int res; sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock >= 0) { sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; sin.sin_port = htons(port); memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); res = bind(sock, (struct sockaddr *)&sin, sizeof(sin)); if (res < 0) { requestdata = NULL; return; } requestdata = (char *)malloc(B_UDP_MAX_SIZE); return; } requestdata = NULL; return; } NetServer::~NetServer(void) { if (sock >= 0) { closesocket(sock); sock = -1; if (requestdata) { free(requestdata); requestdata = NULL; } } } long NetServer::ReceiveMessage(BMessage **messagep) { int len; BMessage *message; struct sockaddr_in from; int fromlen; for (;;) { fromlen = sizeof(from); len = recvfrom(sock, requestdata, B_UDP_MAX_SIZE, 0, (struct sockaddr *)&from, &fromlen); if (len < 0) { return (NET_CANT_RECV); } message = new BMessage(); message->Unflatten(requestdata); *messagep = message; if (message->HasLong(TRANSACTION_ID)) { tid = message->FindLong(TRANSACTION_ID); address = from; return (B_NO_ERROR); } } } long NetServer::SendReply( BMessage *request ) { char *data; long size; int res; request->AddLong(TRANSACTION_ID, tid); request->Flatten(&data, &size); res = sendto(this->sock, data, size, 0, (struct sockaddr *)&address, sizeof(address)); if (res < 0) { free(data); return (NET_CANT_SEND); } free(data); return (B_NO_ERROR); }