|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /******************************************************************* |
|
7 ** udpsrc.c -- Test basic function of UDP server |
|
8 ** |
|
9 ** udpsrv operates on the same machine with program udpclt. |
|
10 ** udpsrv is the server side of a udp sockets application. |
|
11 ** udpclt is the client side of a udp sockets application. |
|
12 ** |
|
13 ** The test is designed to assist developers in porting/debugging |
|
14 ** the UDP socket functions of NSPR. |
|
15 ** |
|
16 ** This test is not a stress test. |
|
17 ** |
|
18 ** main() starts two threads: UDP_Server() and UDP_Client(); |
|
19 ** main() uses PR_JoinThread() to wait for the threads to complete. |
|
20 ** |
|
21 ** UDP_Server() does repeated recvfrom()s from a socket. |
|
22 ** He detects an EOF condition set by UDP_Client(). For each |
|
23 ** packet received by UDP_Server(), he checks its content for |
|
24 ** expected content, then sends the packet back to UDP_Client(). |
|
25 ** |
|
26 ** UDP_Client() sends packets to UDP_Server() using sendto() |
|
27 ** he recieves packets back from the server via recvfrom(). |
|
28 ** After he sends enough packets containing UDP_AMOUNT_TO_WRITE |
|
29 ** bytes of data, he sends an EOF message. |
|
30 ** |
|
31 ** The test issues a pass/fail message at end. |
|
32 ** |
|
33 ** Notes: |
|
34 ** The variable "_debug_on" can be set to 1 to cause diagnostic |
|
35 ** messages related to client/server synchronization. Useful when |
|
36 ** the test hangs. |
|
37 ** |
|
38 ** Error messages are written to stdout. |
|
39 ** |
|
40 ******************************************************************** |
|
41 */ |
|
42 /* --- include files --- */ |
|
43 #include "nspr.h" |
|
44 #include "prpriv.h" |
|
45 |
|
46 #include "plgetopt.h" |
|
47 #include "prttools.h" |
|
48 |
|
49 #include <stdio.h> |
|
50 #include <stdlib.h> |
|
51 #include <string.h> |
|
52 #include <errno.h> |
|
53 |
|
54 #ifdef XP_PC |
|
55 #define mode_t int |
|
56 #endif |
|
57 |
|
58 #define UDP_BUF_SIZE 4096 |
|
59 #define UDP_DGRAM_SIZE 128 |
|
60 #define UDP_AMOUNT_TO_WRITE (PRInt32)((UDP_DGRAM_SIZE * 1000l) +1) |
|
61 #define NUM_UDP_CLIENTS 1 |
|
62 #define NUM_UDP_DATAGRAMS_PER_CLIENT 5 |
|
63 #define UDP_SERVER_PORT 9050 |
|
64 #define UDP_CLIENT_PORT 9053 |
|
65 #define MY_INADDR PR_INADDR_ANY |
|
66 #define PEER_INADDR PR_INADDR_LOOPBACK |
|
67 |
|
68 #define UDP_TIMEOUT 400000 |
|
69 /* #define UDP_TIMEOUT PR_INTERVAL_NO_TIMEOUT */ |
|
70 |
|
71 /* --- static data --- */ |
|
72 static PRIntn _debug_on = 0; |
|
73 static PRBool passed = PR_TRUE; |
|
74 static PRUint32 cltBytesRead = 0; |
|
75 static PRUint32 srvBytesRead = 0; |
|
76 static PRFileDesc *output = NULL; |
|
77 |
|
78 /* --- static function declarations --- */ |
|
79 #define DPRINTF(arg) if (_debug_on) PR_fprintf(output, arg) |
|
80 |
|
81 |
|
82 |
|
83 /******************************************************************* |
|
84 ** ListNetAddr() -- Display the Net Address on stdout |
|
85 ** |
|
86 ** Description: displays the component parts of a PRNetAddr struct |
|
87 ** |
|
88 ** Arguments: address of PRNetAddr structure to display |
|
89 ** |
|
90 ** Returns: void |
|
91 ** |
|
92 ** Notes: |
|
93 ** |
|
94 ******************************************************************** |
|
95 */ |
|
96 void ListNetAddr( char *msg, PRNetAddr *na ) |
|
97 { |
|
98 char mbuf[256]; |
|
99 |
|
100 sprintf( mbuf, "ListNetAddr: %s family: %d, port: %d, ip: %8.8X\n", |
|
101 msg, na->inet.family, PR_ntohs( na->inet.port), PR_ntohl(na->inet.ip) ); |
|
102 #if 0 |
|
103 DPRINTF( mbuf ); |
|
104 #endif |
|
105 } /* --- end ListNetAddr() --- */ |
|
106 |
|
107 /******************************************************************** |
|
108 ** UDP_Server() -- Test a UDP server application |
|
109 ** |
|
110 ** Description: The Server side of a UDP Client/Server application. |
|
111 ** |
|
112 ** Arguments: none |
|
113 ** |
|
114 ** Returns: void |
|
115 ** |
|
116 ** Notes: |
|
117 ** |
|
118 ** |
|
119 ******************************************************************** |
|
120 */ |
|
121 static void PR_CALLBACK UDP_Server( void *arg ) |
|
122 { |
|
123 static char svrBuf[UDP_BUF_SIZE]; |
|
124 PRFileDesc *svrSock; |
|
125 PRInt32 rv; |
|
126 PRNetAddr netaddr; |
|
127 PRBool bound = PR_FALSE; |
|
128 PRBool endOfInput = PR_FALSE; |
|
129 PRInt32 numBytes = UDP_DGRAM_SIZE; |
|
130 |
|
131 DPRINTF("udpsrv: UDP_Server(): starting\n" ); |
|
132 |
|
133 /* --- Create the socket --- */ |
|
134 DPRINTF("udpsrv: UDP_Server(): Creating UDP Socket\n" ); |
|
135 svrSock = PR_NewUDPSocket(); |
|
136 if ( svrSock == NULL ) |
|
137 { |
|
138 passed = PR_FALSE; |
|
139 if (debug_mode) |
|
140 PR_fprintf(output, |
|
141 "udpsrv: UDP_Server(): PR_NewUDPSocket() returned NULL\n" ); |
|
142 return; |
|
143 } |
|
144 |
|
145 /* --- Initialize the sockaddr_in structure --- */ |
|
146 memset( &netaddr, 0, sizeof( netaddr )); |
|
147 netaddr.inet.family = PR_AF_INET; |
|
148 netaddr.inet.port = PR_htons( UDP_SERVER_PORT ); |
|
149 netaddr.inet.ip = PR_htonl( MY_INADDR ); |
|
150 |
|
151 /* --- Bind the socket --- */ |
|
152 while ( !bound ) |
|
153 { |
|
154 DPRINTF("udpsrv: UDP_Server(): Binding socket\n" ); |
|
155 rv = PR_Bind( svrSock, &netaddr ); |
|
156 if ( rv < 0 ) |
|
157 { |
|
158 if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR ) |
|
159 { |
|
160 if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \ |
|
161 PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n"); |
|
162 PR_Sleep( PR_MillisecondsToInterval( 2000 )); |
|
163 continue; |
|
164 } |
|
165 else |
|
166 { |
|
167 passed = PR_FALSE; |
|
168 if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \ |
|
169 PR_Bind(): failed: %ld with error: %ld\n", |
|
170 rv, PR_GetError() ); |
|
171 PR_Close( svrSock ); |
|
172 return; |
|
173 } |
|
174 } |
|
175 else |
|
176 bound = PR_TRUE; |
|
177 } |
|
178 ListNetAddr( "UDP_Server: after bind", &netaddr ); |
|
179 |
|
180 /* --- Recv the socket --- */ |
|
181 while( !endOfInput ) |
|
182 { |
|
183 DPRINTF("udpsrv: UDP_Server(): RecvFrom() socket\n" ); |
|
184 rv = PR_RecvFrom( svrSock, svrBuf, numBytes, 0, &netaddr, UDP_TIMEOUT ); |
|
185 if ( rv == -1 ) |
|
186 { |
|
187 passed = PR_FALSE; |
|
188 if (debug_mode) |
|
189 PR_fprintf(output, |
|
190 "udpsrv: UDP_Server(): PR_RecvFrom(): failed with error: %ld\n", |
|
191 PR_GetError() ); |
|
192 PR_Close( svrSock ); |
|
193 return; |
|
194 } |
|
195 ListNetAddr( "UDP_Server after RecvFrom", &netaddr ); |
|
196 |
|
197 srvBytesRead += rv; |
|
198 |
|
199 if ( svrBuf[0] == 'E' ) |
|
200 { |
|
201 DPRINTF("udpsrv: UDP_Server(): EOF on input detected\n" ); |
|
202 endOfInput = PR_TRUE; |
|
203 } |
|
204 |
|
205 /* --- Send the socket --- */ |
|
206 DPRINTF("udpsrv: UDP_Server(): SendTo(): socket\n" ); |
|
207 rv = PR_SendTo( svrSock, svrBuf, rv, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT ); |
|
208 if ( rv == -1 ) |
|
209 { |
|
210 passed = PR_FALSE; |
|
211 if (debug_mode) |
|
212 PR_fprintf(output, |
|
213 "udpsrv: UDP_Server(): PR_SendTo(): failed with error: %ld\n", |
|
214 PR_GetError() ); |
|
215 PR_Close( svrSock ); |
|
216 return; |
|
217 } |
|
218 ListNetAddr( "UDP_Server after SendTo", &netaddr ); |
|
219 } |
|
220 |
|
221 /* --- Close the socket --- */ |
|
222 DPRINTF("udpsrv: UDP_Server(): Closing socket\n" ); |
|
223 rv = PR_Close( svrSock ); |
|
224 if ( rv != PR_SUCCESS ) |
|
225 { |
|
226 passed = PR_FALSE; |
|
227 if (debug_mode) |
|
228 PR_fprintf(output, |
|
229 "udpsrv: UDP_Server(): PR_Close(): failed to close socket\n" ); |
|
230 return; |
|
231 } |
|
232 |
|
233 DPRINTF("udpsrv: UDP_Server(): Normal end\n" ); |
|
234 } /* --- end UDP_Server() --- */ |
|
235 |
|
236 |
|
237 static char cltBuf[UDP_BUF_SIZE]; |
|
238 static char cltBufin[UDP_BUF_SIZE]; |
|
239 /******************************************************************** |
|
240 ** UDP_Client() -- Test a UDP client application |
|
241 ** |
|
242 ** Description: |
|
243 ** |
|
244 ** Arguments: |
|
245 ** |
|
246 ** |
|
247 ** Returns: |
|
248 ** 0 -- Successful execution |
|
249 ** 1 -- Test failed. |
|
250 ** |
|
251 ** Notes: |
|
252 ** |
|
253 ** |
|
254 ******************************************************************** |
|
255 */ |
|
256 static void PR_CALLBACK UDP_Client( void *arg ) |
|
257 { |
|
258 PRFileDesc *cltSock; |
|
259 PRInt32 rv; |
|
260 PRBool bound = PR_FALSE; |
|
261 PRNetAddr netaddr; |
|
262 PRNetAddr netaddrx; |
|
263 PRBool endOfInput = PR_FALSE; |
|
264 PRInt32 numBytes = UDP_DGRAM_SIZE; |
|
265 PRInt32 writeThisMany = UDP_AMOUNT_TO_WRITE; |
|
266 int i; |
|
267 |
|
268 |
|
269 DPRINTF("udpsrv: UDP_Client(): starting\n" ); |
|
270 |
|
271 /* --- Create the socket --- */ |
|
272 cltSock = PR_NewUDPSocket(); |
|
273 if ( cltSock == NULL ) |
|
274 { |
|
275 passed = PR_FALSE; |
|
276 if (debug_mode) |
|
277 PR_fprintf(output, |
|
278 "udpsrv: UDP_Client(): PR_NewUDPSocket() returned NULL\n" ); |
|
279 return; |
|
280 } |
|
281 |
|
282 /* --- Initialize the sockaddr_in structure --- */ |
|
283 memset( &netaddr, 0, sizeof( netaddr )); |
|
284 netaddr.inet.family = PR_AF_INET; |
|
285 netaddr.inet.ip = PR_htonl( MY_INADDR ); |
|
286 netaddr.inet.port = PR_htons( UDP_CLIENT_PORT ); |
|
287 |
|
288 /* --- Initialize the write buffer --- */ |
|
289 for ( i = 0; i < UDP_BUF_SIZE ; i++ ) |
|
290 cltBuf[i] = i; |
|
291 |
|
292 /* --- Bind the socket --- */ |
|
293 while ( !bound ) |
|
294 { |
|
295 DPRINTF("udpsrv: UDP_Client(): Binding socket\n" ); |
|
296 rv = PR_Bind( cltSock, &netaddr ); |
|
297 if ( rv < 0 ) |
|
298 { |
|
299 if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR ) |
|
300 { |
|
301 if (debug_mode) |
|
302 PR_fprintf(output, |
|
303 "udpsrv: UDP_Client(): PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n"); |
|
304 PR_Sleep( PR_MillisecondsToInterval( 2000 )); |
|
305 continue; |
|
306 } |
|
307 else |
|
308 { |
|
309 passed = PR_FALSE; |
|
310 if (debug_mode) |
|
311 PR_fprintf(output, |
|
312 "udpsrv: UDP_Client(): PR_Bind(): failed: %ld with error: %ld\n", |
|
313 rv, PR_GetError() ); |
|
314 PR_Close( cltSock ); |
|
315 return; |
|
316 } |
|
317 } |
|
318 else |
|
319 bound = PR_TRUE; |
|
320 } |
|
321 ListNetAddr( "UDP_Client after Bind", &netaddr ); |
|
322 |
|
323 /* --- Initialize the sockaddr_in structure --- */ |
|
324 memset( &netaddr, 0, sizeof( netaddr )); |
|
325 netaddr.inet.family = PR_AF_INET; |
|
326 netaddr.inet.ip = PR_htonl( PEER_INADDR ); |
|
327 netaddr.inet.port = PR_htons( UDP_SERVER_PORT ); |
|
328 |
|
329 /* --- send and receive packets until no more data left */ |
|
330 while( !endOfInput ) |
|
331 { |
|
332 /* |
|
333 ** Signal EOF in the data stream on the last packet |
|
334 */ |
|
335 if ( writeThisMany <= UDP_DGRAM_SIZE ) |
|
336 { |
|
337 DPRINTF("udpsrv: UDP_Client(): Send EOF packet\n" ); |
|
338 cltBuf[0] = 'E'; |
|
339 endOfInput = PR_TRUE; |
|
340 } |
|
341 |
|
342 /* --- SendTo the socket --- */ |
|
343 if ( writeThisMany > UDP_DGRAM_SIZE ) |
|
344 numBytes = UDP_DGRAM_SIZE; |
|
345 else |
|
346 numBytes = writeThisMany; |
|
347 writeThisMany -= numBytes; |
|
348 { |
|
349 char mbuf[256]; |
|
350 sprintf( mbuf, "udpsrv: UDP_Client(): write_this_many: %d, numbytes: %d\n", |
|
351 writeThisMany, numBytes ); |
|
352 DPRINTF( mbuf ); |
|
353 } |
|
354 |
|
355 DPRINTF("udpsrv: UDP_Client(): SendTo(): socket\n" ); |
|
356 rv = PR_SendTo( cltSock, cltBuf, numBytes, 0, &netaddr, UDP_TIMEOUT ); |
|
357 if ( rv == -1 ) |
|
358 { |
|
359 passed = PR_FALSE; |
|
360 if (debug_mode) |
|
361 PR_fprintf(output, |
|
362 "udpsrv: UDP_Client(): PR_SendTo(): failed with error: %ld\n", |
|
363 PR_GetError() ); |
|
364 PR_Close( cltSock ); |
|
365 return; |
|
366 } |
|
367 ListNetAddr( "UDP_Client after SendTo", &netaddr ); |
|
368 |
|
369 /* --- RecvFrom the socket --- */ |
|
370 memset( cltBufin, 0, UDP_BUF_SIZE ); |
|
371 DPRINTF("udpsrv: UDP_Client(): RecvFrom(): socket\n" ); |
|
372 rv = PR_RecvFrom( cltSock, cltBufin, numBytes, 0, &netaddrx, UDP_TIMEOUT ); |
|
373 if ( rv == -1 ) |
|
374 { |
|
375 passed = PR_FALSE; |
|
376 if (debug_mode) PR_fprintf(output, |
|
377 "udpsrv: UDP_Client(): PR_RecvFrom(): failed with error: %ld\n", |
|
378 PR_GetError() ); |
|
379 PR_Close( cltSock ); |
|
380 return; |
|
381 } |
|
382 ListNetAddr( "UDP_Client after RecvFrom()", &netaddr ); |
|
383 cltBytesRead += rv; |
|
384 |
|
385 /* --- verify buffer --- */ |
|
386 for ( i = 0; i < rv ; i++ ) |
|
387 { |
|
388 if ( cltBufin[i] != i ) |
|
389 { |
|
390 /* --- special case, end of input --- */ |
|
391 if ( endOfInput && i == 0 && cltBufin[0] == 'E' ) |
|
392 continue; |
|
393 passed = PR_FALSE; |
|
394 if (debug_mode) PR_fprintf(output, |
|
395 "udpsrv: UDP_Client(): return data mismatch\n" ); |
|
396 PR_Close( cltSock ); |
|
397 return; |
|
398 } |
|
399 } |
|
400 if (debug_mode) PR_fprintf(output, "."); |
|
401 } |
|
402 |
|
403 /* --- Close the socket --- */ |
|
404 DPRINTF("udpsrv: UDP_Server(): Closing socket\n" ); |
|
405 rv = PR_Close( cltSock ); |
|
406 if ( rv != PR_SUCCESS ) |
|
407 { |
|
408 passed = PR_FALSE; |
|
409 if (debug_mode) PR_fprintf(output, |
|
410 "udpsrv: UDP_Client(): PR_Close(): failed to close socket\n" ); |
|
411 return; |
|
412 } |
|
413 DPRINTF("udpsrv: UDP_Client(): ending\n" ); |
|
414 } /* --- end UDP_Client() --- */ |
|
415 |
|
416 /******************************************************************** |
|
417 ** main() -- udpsrv |
|
418 ** |
|
419 ** arguments: |
|
420 ** |
|
421 ** Returns: |
|
422 ** 0 -- Successful execution |
|
423 ** 1 -- Test failed. |
|
424 ** |
|
425 ** Description: |
|
426 ** |
|
427 ** Standard test case setup. |
|
428 ** |
|
429 ** Calls the function UDP_Server() |
|
430 ** |
|
431 ******************************************************************** |
|
432 */ |
|
433 |
|
434 int main(int argc, char **argv) |
|
435 { |
|
436 PRThread *srv, *clt; |
|
437 /* The command line argument: -d is used to determine if the test is being run |
|
438 in debug mode. The regress tool requires only one line output:PASS or FAIL. |
|
439 All of the printfs associated with this test has been handled with a if (debug_mode) |
|
440 test. |
|
441 Usage: test_name -d -v |
|
442 */ |
|
443 PLOptStatus os; |
|
444 PLOptState *opt = PL_CreateOptState(argc, argv, "dv"); |
|
445 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) |
|
446 { |
|
447 if (PL_OPT_BAD == os) continue; |
|
448 switch (opt->option) |
|
449 { |
|
450 case 'd': /* debug mode */ |
|
451 debug_mode = 1; |
|
452 break; |
|
453 case 'v': /* verbose mode */ |
|
454 _debug_on = 1; |
|
455 break; |
|
456 default: |
|
457 break; |
|
458 } |
|
459 } |
|
460 PL_DestroyOptState(opt); |
|
461 |
|
462 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); |
|
463 PR_STDIO_INIT(); |
|
464 output = PR_STDERR; |
|
465 |
|
466 PR_SetConcurrency(4); |
|
467 |
|
468 /* |
|
469 ** Create the Server thread |
|
470 */ |
|
471 DPRINTF( "udpsrv: Creating Server Thread\n" ); |
|
472 srv = PR_CreateThread( PR_USER_THREAD, |
|
473 UDP_Server, |
|
474 (void *) 0, |
|
475 PR_PRIORITY_LOW, |
|
476 PR_LOCAL_THREAD, |
|
477 PR_JOINABLE_THREAD, |
|
478 0 ); |
|
479 if ( srv == NULL ) |
|
480 { |
|
481 if (debug_mode) PR_fprintf(output, "udpsrv: Cannot create server thread\n" ); |
|
482 passed = PR_FALSE; |
|
483 } |
|
484 |
|
485 /* |
|
486 ** Give the Server time to Start |
|
487 */ |
|
488 DPRINTF( "udpsrv: Pausing to allow Server to start\n" ); |
|
489 PR_Sleep( PR_MillisecondsToInterval(200) ); |
|
490 |
|
491 /* |
|
492 ** Create the Client thread |
|
493 */ |
|
494 DPRINTF( "udpsrv: Creating Client Thread\n" ); |
|
495 clt = PR_CreateThread( PR_USER_THREAD, |
|
496 UDP_Client, |
|
497 (void *) 0, |
|
498 PR_PRIORITY_LOW, |
|
499 PR_LOCAL_THREAD, |
|
500 PR_JOINABLE_THREAD, |
|
501 0 ); |
|
502 if ( clt == NULL ) |
|
503 { |
|
504 if (debug_mode) PR_fprintf(output, "udpsrv: Cannot create server thread\n" ); |
|
505 passed = PR_FALSE; |
|
506 } |
|
507 |
|
508 /* |
|
509 ** |
|
510 */ |
|
511 DPRINTF("udpsrv: Waiting to join Server & Client Threads\n" ); |
|
512 PR_JoinThread( srv ); |
|
513 PR_JoinThread( clt ); |
|
514 |
|
515 /* |
|
516 ** Evaluate test results |
|
517 */ |
|
518 if (debug_mode) PR_fprintf(output, "\n\nudpsrv: main(): cltBytesRead(%ld), \ |
|
519 srvBytesRead(%ld), expected(%ld)\n", |
|
520 cltBytesRead, srvBytesRead, UDP_AMOUNT_TO_WRITE ); |
|
521 if ( cltBytesRead != srvBytesRead || cltBytesRead != UDP_AMOUNT_TO_WRITE ) |
|
522 { |
|
523 passed = PR_FALSE; |
|
524 } |
|
525 PR_Cleanup(); |
|
526 if ( passed ) |
|
527 return 0; |
|
528 else |
|
529 return 1; |
|
530 } /* --- end main() --- */ |