/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /******************************************************************* ** udpsrc.c -- Test basic function of UDP server ** ** udpsrv operates on the same machine with program udpclt. ** udpsrv is the server side of a udp sockets application. ** udpclt is the client side of a udp sockets application. ** ** The test is designed to assist developers in porting/debugging ** the UDP socket functions of NSPR. ** ** This test is not a stress test. ** ** main() starts two threads: UDP_Server() and UDP_Client(); ** main() uses PR_JoinThread() to wait for the threads to complete. ** ** UDP_Server() does repeated recvfrom()s from a socket. ** He detects an EOF condition set by UDP_Client(). For each ** packet received by UDP_Server(), he checks its content for ** expected content, then sends the packet back to UDP_Client(). ** ** UDP_Client() sends packets to UDP_Server() using sendto() ** he recieves packets back from the server via recvfrom(). ** After he sends enough packets containing UDP_AMOUNT_TO_WRITE ** bytes of data, he sends an EOF message. ** ** The test issues a pass/fail message at end. ** ** Notes: ** The variable "_debug_on" can be set to 1 to cause diagnostic ** messages related to client/server synchronization. Useful when ** the test hangs. ** ** Error messages are written to stdout. ** ******************************************************************** */ /* --- include files --- */ #include "nspr.h" #include "prpriv.h" #include "plgetopt.h" #include "prttools.h" #include #include #include #include #ifdef XP_PC # define mode_t int #endif #define UDP_BUF_SIZE 4096 #define UDP_DGRAM_SIZE 128 #define UDP_AMOUNT_TO_WRITE (PRInt32)((UDP_DGRAM_SIZE * 1000l) + 1) #define NUM_UDP_CLIENTS 1 #define NUM_UDP_DATAGRAMS_PER_CLIENT 5 #define UDP_SERVER_PORT 9050 #define UDP_CLIENT_PORT 9053 #define MY_INADDR PR_INADDR_ANY #define PEER_INADDR PR_INADDR_LOOPBACK #define UDP_TIMEOUT 400000 /* #define UDP_TIMEOUT PR_INTERVAL_NO_TIMEOUT */ /* --- static data --- */ static PRIntn _debug_on = 0; static PRBool passed = PR_TRUE; static PRUint32 cltBytesRead = 0; static PRUint32 srvBytesRead = 0; static PRFileDesc* output = NULL; /* --- static function declarations --- */ #define DPRINTF(arg) \ if (_debug_on) PR_fprintf(output, arg) /******************************************************************* ** ListNetAddr() -- Display the Net Address on stdout ** ** Description: displays the component parts of a PRNetAddr struct ** ** Arguments: address of PRNetAddr structure to display ** ** Returns: void ** ** Notes: ** ******************************************************************** */ void ListNetAddr(char* msg, PRNetAddr* na) { char mbuf[256]; sprintf(mbuf, "ListNetAddr: %s family: %d, port: %d, ip: %8.8X\n", msg, na->inet.family, PR_ntohs(na->inet.port), PR_ntohl(na->inet.ip)); #if 0 DPRINTF( mbuf ); #endif } /* --- end ListNetAddr() --- */ /******************************************************************** ** UDP_Server() -- Test a UDP server application ** ** Description: The Server side of a UDP Client/Server application. ** ** Arguments: none ** ** Returns: void ** ** Notes: ** ** ******************************************************************** */ static void PR_CALLBACK UDP_Server(void* arg) { static char svrBuf[UDP_BUF_SIZE]; PRFileDesc* svrSock; PRInt32 rv; PRNetAddr netaddr; PRBool bound = PR_FALSE; PRBool endOfInput = PR_FALSE; PRInt32 numBytes = UDP_DGRAM_SIZE; DPRINTF("udpsrv: UDP_Server(): starting\n"); /* --- Create the socket --- */ DPRINTF("udpsrv: UDP_Server(): Creating UDP Socket\n"); svrSock = PR_NewUDPSocket(); if (svrSock == NULL) { passed = PR_FALSE; if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): PR_NewUDPSocket() returned NULL\n"); return; } /* --- Initialize the sockaddr_in structure --- */ memset(&netaddr, 0, sizeof(netaddr)); netaddr.inet.family = PR_AF_INET; netaddr.inet.port = PR_htons(UDP_SERVER_PORT); netaddr.inet.ip = PR_htonl(MY_INADDR); /* --- Bind the socket --- */ while (!bound) { DPRINTF("udpsrv: UDP_Server(): Binding socket\n"); rv = PR_Bind(svrSock, &netaddr); if (rv < 0) { if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \ PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n"); PR_Sleep(PR_MillisecondsToInterval(2000)); continue; } else { passed = PR_FALSE; if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \ PR_Bind(): failed: %ld with error: %ld\n", rv, PR_GetError()); PR_Close(svrSock); return; } } else { bound = PR_TRUE; } } ListNetAddr("UDP_Server: after bind", &netaddr); /* --- Recv the socket --- */ while (!endOfInput) { DPRINTF("udpsrv: UDP_Server(): RecvFrom() socket\n"); rv = PR_RecvFrom(svrSock, svrBuf, numBytes, 0, &netaddr, UDP_TIMEOUT); if (rv == -1) { passed = PR_FALSE; if (debug_mode) PR_fprintf( output, "udpsrv: UDP_Server(): PR_RecvFrom(): failed with error: %ld\n", PR_GetError()); PR_Close(svrSock); return; } ListNetAddr("UDP_Server after RecvFrom", &netaddr); srvBytesRead += rv; if (svrBuf[0] == 'E') { DPRINTF("udpsrv: UDP_Server(): EOF on input detected\n"); endOfInput = PR_TRUE; } /* --- Send the socket --- */ DPRINTF("udpsrv: UDP_Server(): SendTo(): socket\n"); rv = PR_SendTo(svrSock, svrBuf, rv, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT); if (rv == -1) { passed = PR_FALSE; if (debug_mode) PR_fprintf( output, "udpsrv: UDP_Server(): PR_SendTo(): failed with error: %ld\n", PR_GetError()); PR_Close(svrSock); return; } ListNetAddr("UDP_Server after SendTo", &netaddr); } /* --- Close the socket --- */ DPRINTF("udpsrv: UDP_Server(): Closing socket\n"); rv = PR_Close(svrSock); if (rv != PR_SUCCESS) { passed = PR_FALSE; if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): PR_Close(): failed to close socket\n"); return; } DPRINTF("udpsrv: UDP_Server(): Normal end\n"); } /* --- end UDP_Server() --- */ static char cltBuf[UDP_BUF_SIZE]; static char cltBufin[UDP_BUF_SIZE]; /******************************************************************** ** UDP_Client() -- Test a UDP client application ** ** Description: ** ** Arguments: ** ** ** Returns: ** 0 -- Successful execution ** 1 -- Test failed. ** ** Notes: ** ** ******************************************************************** */ static void PR_CALLBACK UDP_Client(void* arg) { PRFileDesc* cltSock; PRInt32 rv; PRBool bound = PR_FALSE; PRNetAddr netaddr; PRNetAddr netaddrx; PRBool endOfInput = PR_FALSE; PRInt32 numBytes = UDP_DGRAM_SIZE; PRInt32 writeThisMany = UDP_AMOUNT_TO_WRITE; int i; DPRINTF("udpsrv: UDP_Client(): starting\n"); /* --- Create the socket --- */ cltSock = PR_NewUDPSocket(); if (cltSock == NULL) { passed = PR_FALSE; if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Client(): PR_NewUDPSocket() returned NULL\n"); return; } /* --- Initialize the sockaddr_in structure --- */ memset(&netaddr, 0, sizeof(netaddr)); netaddr.inet.family = PR_AF_INET; netaddr.inet.ip = PR_htonl(MY_INADDR); netaddr.inet.port = PR_htons(UDP_CLIENT_PORT); /* --- Initialize the write buffer --- */ for (i = 0; i < UDP_BUF_SIZE; i++) { cltBuf[i] = i; } /* --- Bind the socket --- */ while (!bound) { DPRINTF("udpsrv: UDP_Client(): Binding socket\n"); rv = PR_Bind(cltSock, &netaddr); if (rv < 0) { if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Client(): PR_Bind(): reports: " "PR_ADDRESS_IN_USE_ERROR\n"); PR_Sleep(PR_MillisecondsToInterval(2000)); continue; } else { passed = PR_FALSE; if (debug_mode) PR_fprintf( output, "udpsrv: UDP_Client(): PR_Bind(): failed: %ld with error: %ld\n", rv, PR_GetError()); PR_Close(cltSock); return; } } else { bound = PR_TRUE; } } ListNetAddr("UDP_Client after Bind", &netaddr); /* --- Initialize the sockaddr_in structure --- */ memset(&netaddr, 0, sizeof(netaddr)); netaddr.inet.family = PR_AF_INET; netaddr.inet.ip = PR_htonl(PEER_INADDR); netaddr.inet.port = PR_htons(UDP_SERVER_PORT); /* --- send and receive packets until no more data left */ while (!endOfInput) { /* ** Signal EOF in the data stream on the last packet */ if (writeThisMany <= UDP_DGRAM_SIZE) { DPRINTF("udpsrv: UDP_Client(): Send EOF packet\n"); cltBuf[0] = 'E'; endOfInput = PR_TRUE; } /* --- SendTo the socket --- */ if (writeThisMany > UDP_DGRAM_SIZE) { numBytes = UDP_DGRAM_SIZE; } else { numBytes = writeThisMany; } writeThisMany -= numBytes; { char mbuf[256]; sprintf(mbuf, "udpsrv: UDP_Client(): write_this_many: %d, numbytes: %d\n", writeThisMany, numBytes); DPRINTF(mbuf); } DPRINTF("udpsrv: UDP_Client(): SendTo(): socket\n"); rv = PR_SendTo(cltSock, cltBuf, numBytes, 0, &netaddr, UDP_TIMEOUT); if (rv == -1) { passed = PR_FALSE; if (debug_mode) PR_fprintf( output, "udpsrv: UDP_Client(): PR_SendTo(): failed with error: %ld\n", PR_GetError()); PR_Close(cltSock); return; } ListNetAddr("UDP_Client after SendTo", &netaddr); /* --- RecvFrom the socket --- */ memset(cltBufin, 0, UDP_BUF_SIZE); DPRINTF("udpsrv: UDP_Client(): RecvFrom(): socket\n"); rv = PR_RecvFrom(cltSock, cltBufin, numBytes, 0, &netaddrx, UDP_TIMEOUT); if (rv == -1) { passed = PR_FALSE; if (debug_mode) PR_fprintf( output, "udpsrv: UDP_Client(): PR_RecvFrom(): failed with error: %ld\n", PR_GetError()); PR_Close(cltSock); return; } ListNetAddr("UDP_Client after RecvFrom()", &netaddr); cltBytesRead += rv; /* --- verify buffer --- */ for (i = 0; i < rv; i++) { if (cltBufin[i] != i) { /* --- special case, end of input --- */ if (endOfInput && i == 0 && cltBufin[0] == 'E') { continue; } passed = PR_FALSE; if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Client(): return data mismatch\n"); PR_Close(cltSock); return; } } if (debug_mode) { PR_fprintf(output, "."); } } /* --- Close the socket --- */ DPRINTF("udpsrv: UDP_Server(): Closing socket\n"); rv = PR_Close(cltSock); if (rv != PR_SUCCESS) { passed = PR_FALSE; if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Client(): PR_Close(): failed to close socket\n"); return; } DPRINTF("udpsrv: UDP_Client(): ending\n"); } /* --- end UDP_Client() --- */ /******************************************************************** ** main() -- udpsrv ** ** arguments: ** ** Returns: ** 0 -- Successful execution ** 1 -- Test failed. ** ** Description: ** ** Standard test case setup. ** ** Calls the function UDP_Server() ** ******************************************************************** */ int main(int argc, char** argv) { PRThread *srv, *clt; /* The command line argument: -d is used to determine if the test is being run in debug mode. The regress tool requires only one line output:PASS or FAIL. All of the printfs associated with this test has been handled with a if (debug_mode) test. Usage: test_name -d -v */ PLOptStatus os; PLOptState* opt = PL_CreateOptState(argc, argv, "dv"); while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { if (PL_OPT_BAD == os) { continue; } switch (opt->option) { case 'd': /* debug mode */ debug_mode = 1; break; case 'v': /* verbose mode */ _debug_on = 1; break; default: break; } } PL_DestroyOptState(opt); PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); PR_STDIO_INIT(); output = PR_STDERR; PR_SetConcurrency(4); /* ** Create the Server thread */ DPRINTF("udpsrv: Creating Server Thread\n"); srv = PR_CreateThread(PR_USER_THREAD, UDP_Server, (void*)0, PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); if (srv == NULL) { if (debug_mode) { PR_fprintf(output, "udpsrv: Cannot create server thread\n"); } passed = PR_FALSE; } /* ** Give the Server time to Start */ DPRINTF("udpsrv: Pausing to allow Server to start\n"); PR_Sleep(PR_MillisecondsToInterval(200)); /* ** Create the Client thread */ DPRINTF("udpsrv: Creating Client Thread\n"); clt = PR_CreateThread(PR_USER_THREAD, UDP_Client, (void*)0, PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); if (clt == NULL) { if (debug_mode) { PR_fprintf(output, "udpsrv: Cannot create server thread\n"); } passed = PR_FALSE; } /* ** */ DPRINTF("udpsrv: Waiting to join Server & Client Threads\n"); PR_JoinThread(srv); PR_JoinThread(clt); /* ** Evaluate test results */ if (debug_mode) PR_fprintf(output, "\n\nudpsrv: main(): cltBytesRead(%ld), \ srvBytesRead(%ld), expected(%ld)\n", cltBytesRead, srvBytesRead, UDP_AMOUNT_TO_WRITE); if (cltBytesRead != srvBytesRead || cltBytesRead != UDP_AMOUNT_TO_WRITE) { passed = PR_FALSE; } PR_Cleanup(); if (passed) { return 0; } else { return 1; } } /* --- end main() --- */