| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* ***** BEGIN LICENSE BLOCK ***** |
| * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License Version |
| * 1.1 (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * for the specific language governing rights and limitations under the |
| * License. |
| * |
| * The Original Code is the Netscape Portable Runtime (NSPR). |
| * |
| * The Initial Developer of the Original Code is |
| * Netscape Communications Corporation. |
| * Portions created by the Initial Developer are Copyright (C) 1998-2000 |
| * the Initial Developer. All Rights Reserved. |
| * |
| * Contributor(s): |
| * |
| * Alternatively, the contents of this file may be used under the terms of |
| * either the GNU General Public License Version 2 or later (the "GPL"), or |
| * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
| * in which case the provisions of the GPL or the LGPL are applicable instead |
| * of those above. If you wish to allow use of your version of this file only |
| * under the terms of either the GPL or the LGPL, and not to allow others to |
| * use your version of this file under the terms of the MPL, indicate your |
| * decision by deleting the provisions above and replace them with the notice |
| * and other provisions required by the GPL or the LGPL. If you do not delete |
| * the provisions above, a recipient may use your version of this file under |
| * the terms of any one of the MPL, the GPL or the LGPL. |
| * |
| * ***** END LICENSE BLOCK ***** */ |
| |
| /*********************************************************************** |
| ** |
| ** Name: tmocon.c |
| ** |
| ** Description: test client socket connection. |
| ** |
| ** Modification History: |
| ** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. |
| ** The debug mode will print all of the printfs associated with this test. |
| ** The regress mode will be the default mode. Since the regress tool limits |
| ** the output to a one line status:PASS or FAIL,all of the printf statements |
| ** have been handled with an if (debug_mode) statement. |
| ***********************************************************************/ |
| |
| /*********************************************************************** |
| ** Includes |
| ***********************************************************************/ |
| /* Used to get the command line option */ |
| #include "plgetopt.h" |
| |
| #include "nspr.h" |
| #include "pprio.h" |
| |
| #include "plerror.h" |
| #include "plgetopt.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| /* for getcwd */ |
| #if defined(XP_UNIX) || defined (XP_OS2) || defined(XP_BEOS) |
| #include <unistd.h> |
| #elif defined(XP_PC) |
| #include <direct.h> |
| #endif |
| |
| #ifdef WINCE |
| #include <windows.h> |
| char *getcwd(char *buf, size_t size) |
| { |
| wchar_t wpath[MAX_PATH]; |
| _wgetcwd(wpath, MAX_PATH); |
| WideCharToMultiByte(CP_ACP, 0, wpath, -1, buf, size, 0, 0); |
| } |
| #endif |
| |
| #define BASE_PORT 9867 |
| |
| #define DEFAULT_DALLY 1 |
| #define DEFAULT_THREADS 1 |
| #define DEFAULT_TIMEOUT 10 |
| #define DEFAULT_MESSAGES 100 |
| #define DEFAULT_MESSAGESIZE 100 |
| |
| static PRFileDesc *debug_out = NULL; |
| |
| typedef struct Shared |
| { |
| PRBool random; |
| PRBool failed; |
| PRBool intermittant; |
| PRIntn debug; |
| PRInt32 messages; |
| PRIntervalTime dally; |
| PRIntervalTime timeout; |
| PRInt32 message_length; |
| PRNetAddr serverAddress; |
| } Shared; |
| |
| static PRIntervalTime Timeout(const Shared *shared) |
| { |
| PRIntervalTime timeout = shared->timeout; |
| if (shared->random) |
| { |
| PRIntervalTime quarter = timeout >> 2; /* one quarter of the interval */ |
| PRUint32 random = rand() % quarter; /* something in[0..timeout / 4) */ |
| timeout = (((3 * quarter) + random) >> 2) + quarter; /* [75..125)% */ |
| } |
| return timeout; |
| } /* Timeout */ |
| |
| static void CauseTimeout(const Shared *shared) |
| { |
| if (shared->intermittant) PR_Sleep(Timeout(shared)); |
| } /* CauseTimeout */ |
| |
| static PRStatus MakeReceiver(Shared *shared) |
| { |
| PRStatus rv = PR_FAILURE; |
| if (PR_IsNetAddrType(&shared->serverAddress, PR_IpAddrLoopback)) |
| { |
| char *argv[3]; |
| char path[1024 + sizeof("/tmoacc")]; |
| |
| getcwd(path, sizeof(path)); |
| |
| (void)strcat(path, "/tmoacc"); |
| #ifdef XP_PC |
| (void)strcat(path, ".exe"); |
| #endif |
| argv[0] = path; |
| if (shared->debug > 0) |
| { |
| argv[1] = "-d"; |
| argv[2] = NULL; |
| } |
| else argv[1] = NULL; |
| if (shared->debug > 1) |
| PR_fprintf(debug_out, " creating accept process %s ...", path); |
| fflush(stdout); |
| rv = PR_CreateProcessDetached(path, argv, NULL, NULL); |
| if (PR_SUCCESS == rv) |
| { |
| if (shared->debug > 1) |
| PR_fprintf(debug_out, " wait 5 seconds"); |
| if (shared->debug > 1) |
| PR_fprintf(debug_out, " before connecting to accept process ..."); |
| fflush(stdout); |
| PR_Sleep(PR_SecondsToInterval(5)); |
| return rv; |
| } |
| shared->failed = PR_TRUE; |
| if (shared->debug > 0) |
| PL_FPrintError(debug_out, "PR_CreateProcessDetached failed"); |
| } |
| return rv; |
| } /* MakeReceiver */ |
| |
| static void Connect(void *arg) |
| { |
| PRStatus rv; |
| char *buffer = NULL; |
| PRFileDesc *clientSock; |
| Shared *shared = (Shared*)arg; |
| PRInt32 loop, bytes, flags = 0; |
| struct Descriptor { PRInt32 length; PRUint32 checksum; } descriptor; |
| debug_out = (0 == shared->debug) ? NULL : PR_GetSpecialFD(PR_StandardError); |
| |
| buffer = (char*)PR_MALLOC(shared->message_length); |
| |
| for (bytes = 0; bytes < shared->message_length; ++bytes) |
| buffer[bytes] = (char)bytes; |
| |
| descriptor.checksum = 0; |
| for (bytes = 0; bytes < shared->message_length; ++bytes) |
| { |
| PRUint32 overflow = descriptor.checksum & 0x80000000; |
| descriptor.checksum = (descriptor.checksum << 1); |
| if (0x00000000 != overflow) descriptor.checksum += 1; |
| descriptor.checksum += buffer[bytes]; |
| } |
| descriptor.checksum = PR_htonl(descriptor.checksum); |
| |
| for (loop = 0; loop < shared->messages; ++loop) |
| { |
| if (shared->debug > 1) |
| PR_fprintf(debug_out, "[%d]socket ... ", loop); |
| clientSock = PR_NewTCPSocket(); |
| if (clientSock) |
| { |
| /* |
| * We need to slow down the rate of generating connect requests, |
| * otherwise the listen backlog queue on the accept side may |
| * become full and we will get connection refused or timeout |
| * error. |
| */ |
| |
| PR_Sleep(shared->dally); |
| if (shared->debug > 1) |
| { |
| char buf[128]; |
| PR_NetAddrToString(&shared->serverAddress, buf, sizeof(buf)); |
| PR_fprintf(debug_out, "connecting to %s ... ", buf); |
| } |
| rv = PR_Connect( |
| clientSock, &shared->serverAddress, Timeout(shared)); |
| if (PR_SUCCESS == rv) |
| { |
| PRInt32 descriptor_length = (loop < (shared->messages - 1)) ? |
| shared->message_length : 0; |
| descriptor.length = PR_htonl(descriptor_length); |
| if (shared->debug > 1) |
| PR_fprintf( |
| debug_out, "sending %d bytes ... ", descriptor_length); |
| CauseTimeout(shared); /* might cause server to timeout */ |
| bytes = PR_Send( |
| clientSock, &descriptor, sizeof(descriptor), |
| flags, Timeout(shared)); |
| if (bytes != sizeof(descriptor)) |
| { |
| shared->failed = PR_TRUE; |
| if (shared->debug > 0) |
| PL_FPrintError(debug_out, "PR_Send failed"); |
| } |
| if (0 != descriptor_length) |
| { |
| CauseTimeout(shared); |
| bytes = PR_Send( |
| clientSock, buffer, descriptor_length, |
| flags, Timeout(shared)); |
| if (bytes != descriptor_length) |
| { |
| shared->failed = PR_TRUE; |
| if (shared->debug > 0) |
| PL_FPrintError(debug_out, "PR_Send failed"); |
| } |
| } |
| if (shared->debug > 1) PR_fprintf(debug_out, "closing ... "); |
| rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH); |
| rv = PR_Close(clientSock); |
| if (shared->debug > 1) |
| { |
| if (PR_SUCCESS == rv) PR_fprintf(debug_out, "\n"); |
| else PL_FPrintError(debug_out, "shutdown failed"); |
| } |
| } |
| else |
| { |
| if (shared->debug > 1) PL_FPrintError(debug_out, "connect failed"); |
| PR_Close(clientSock); |
| if ((loop == 0) && (PR_GetError() == PR_CONNECT_REFUSED_ERROR)) |
| { |
| if (MakeReceiver(shared) == PR_FAILURE) break; |
| } |
| else |
| { |
| if (shared->debug > 1) PR_fprintf(debug_out, " exiting\n"); |
| break; |
| } |
| } |
| } |
| else |
| { |
| shared->failed = PR_TRUE; |
| if (shared->debug > 0) PL_FPrintError(debug_out, "create socket"); |
| break; |
| } |
| } |
| |
| PR_DELETE(buffer); |
| } /* Connect */ |
| |
| int Tmocon(int argc, char **argv) |
| { |
| /* |
| * USAGE |
| * -d turn on debugging output (default = off) |
| * -v turn on verbose output (default = off) |
| * -h <n> dns name of host serving the connection (default = self) |
| * -i dally intermittantly to cause timeouts (default = off) |
| * -m <n> number of messages to send (default = 100) |
| * -s <n> size of each message (default = 100) |
| * -t <n> number of threads sending (default = 1) |
| * -G use global threads (default = local) |
| * -T <n> timeout on I/O operations (seconds) (default = 10) |
| * -D <n> dally between connect requests (seconds)(default = 0) |
| * -R randomize the dally types around 'T' (default = no) |
| */ |
| |
| PRStatus rv; |
| int exitStatus; |
| PLOptStatus os; |
| Shared *shared = NULL; |
| PRThread **thread = NULL; |
| PRIntn index, threads = DEFAULT_THREADS; |
| PRThreadScope thread_scope = PR_LOCAL_THREAD; |
| PRInt32 dally = DEFAULT_DALLY, timeout = DEFAULT_TIMEOUT; |
| PLOptState *opt = PL_CreateOptState(argc, argv, "divGRh:m:s:t:T:D:"); |
| |
| shared = PR_NEWZAP(Shared); |
| |
| shared->debug = 0; |
| shared->failed = PR_FALSE; |
| shared->random = PR_FALSE; |
| shared->messages = DEFAULT_MESSAGES; |
| shared->message_length = DEFAULT_MESSAGESIZE; |
| |
| PR_STDIO_INIT(); |
| memset(&shared->serverAddress, 0, sizeof(shared->serverAddress)); |
| rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &shared->serverAddress); |
| PR_ASSERT(PR_SUCCESS == rv); |
| |
| while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) |
| { |
| if (PL_OPT_BAD == os) continue; |
| switch (opt->option) |
| { |
| case 'd': |
| if (0 == shared->debug) shared->debug = 1; |
| break; |
| case 'v': |
| if (0 == shared->debug) shared->debug = 2; |
| break; |
| case 'i': |
| shared->intermittant = PR_TRUE; |
| break; |
| case 'R': |
| shared->random = PR_TRUE; |
| break; |
| case 'G': |
| thread_scope = PR_GLOBAL_THREAD; |
| break; |
| case 'h': /* the value for backlock */ |
| { |
| PRIntn es = 0; |
| PRHostEnt host; |
| char buffer[1024]; |
| (void)PR_GetHostByName( |
| opt->value, buffer, sizeof(buffer), &host); |
| es = PR_EnumerateHostEnt( |
| es, &host, BASE_PORT, &shared->serverAddress); |
| PR_ASSERT(es > 0); |
| } |
| break; |
| case 'm': /* number of messages to send */ |
| shared->messages = atoi(opt->value); |
| break; |
| case 't': /* number of threads sending */ |
| threads = atoi(opt->value); |
| break; |
| case 'D': /* dally time between transmissions */ |
| dally = atoi(opt->value); |
| break; |
| case 'T': /* timeout on I/O operations */ |
| timeout = atoi(opt->value); |
| break; |
| case 's': /* total size of each message */ |
| shared->message_length = atoi(opt->value); |
| break; |
| default: |
| break; |
| } |
| } |
| PL_DestroyOptState(opt); |
| |
| if (0 == timeout) timeout = DEFAULT_TIMEOUT; |
| if (0 == threads) threads = DEFAULT_THREADS; |
| if (0 == shared->messages) shared->messages = DEFAULT_MESSAGES; |
| if (0 == shared->message_length) shared->message_length = DEFAULT_MESSAGESIZE; |
| |
| shared->dally = PR_SecondsToInterval(dally); |
| shared->timeout = PR_SecondsToInterval(timeout); |
| |
| thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*)); |
| |
| for (index = 0; index < threads; ++index) |
| thread[index] = PR_CreateThread( |
| PR_USER_THREAD, Connect, shared, |
| PR_PRIORITY_NORMAL, thread_scope, |
| PR_JOINABLE_THREAD, 0); |
| for (index = 0; index < threads; ++index) |
| rv = PR_JoinThread(thread[index]); |
| |
| PR_DELETE(thread); |
| |
| PR_fprintf( |
| PR_GetSpecialFD(PR_StandardError), "%s\n", |
| ((shared->failed) ? "FAILED" : "PASSED")); |
| exitStatus = (shared->failed) ? 1 : 0; |
| PR_DELETE(shared); |
| return exitStatus; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| return (PR_VersionCheck(PR_VERSION)) ? |
| PR_Initialize(Tmocon, argc, argv, 4) : -1; |
| } /* main */ |
| |
| /* tmocon.c */ |
| |
| |