program Project34RTSPproxy; {$APPTYPE CONSOLE} (* Synopse mORMot framework Sample 34 - RTSP over HTTP proxy Implements RTSP stream tunnelling over HTTP. Purpose of this sample is to illustrate low-level use of TAsynchConnections, on both Windows and Linux. The HTTP transport is built from two separate HTTP GET and POST requests initiated by the client. The server then binds the connections to form a virtual full-duplex connection. See https://goo.gl/CX6VA3 for reference material about this horrible, but widely accepted, Apple's hack. Version 1.18 - Initial Release first line of uses clause below must be {$I SynDprUses.inc} to enable FastMM4 *) uses {$I SynDprUses.inc} SysUtils, SynCommons, SynTable, SynLog, SynCrtSock, SynBidirSock, mORMot, // just for object serialization in logs SynProtoRTSPHTTP; {$R *.res} const CONCURRENT = 500; // one socket consumes two file descriptors, so each stream opens 6 files; // under Linux with "ulimit -H -n" = 4096, maximum is 4096/6 = 680 streams: // add "* hard nofiles 65535" to /etc/security/limits.conf to make it higher { Some rough numbers, on various Operating Systems: CONCURRENT OS API Time Sockets Polled Steps 100 Windows XP select 190 ms 300 200 10 100 Windows Seven select 190 ms 300 200 10 100 Linux poll 200 ms 300 200 10 100 Linux epoll 190 ms 300 200 10 500 Windows XP select 544 ms 1500 1000 10 500 Windows Seven select 990 ms 1500 1000 10 500 Linux poll 380 ms 1500 1000 10 500 Linux epoll 344 ms 1500 1000 10 5000 Windows XP N/A 5000 Windows Seven select 27.61 s 15000 10000 10 5000 Windows Seven WSAPoll 33.70 s 15000 10000 10 5000 Linux poll 2.70 s 15000 10000 10 5000 Linux epoll 2.59 s 15000 10000 10 10000 Windows XP N/A 10000 Windows Seven select 116.32 s 30000 20000 10 10000 Windows Seven WSAPoll 118.23 s 30000 20000 10 10000 Linux poll 9.48 s 30000 20000 10 10000 Linux epoll 9.33 s 30000 20000 10 10000 Linux epoll 65.1 s 30000 20000 100 10000 Linux epoll 10 min 30000 20000 1000 10000 Linux epoll 20 min 30000 20000 2000 Purpose of this test is to create a given number of concurrent GET/POST HTTP requests, creating one RTSP connection each. Then we run POST and GET small operations on all connections, in a loop, and check the proper RTSP transfer. So here polling will have most of the sockets notified with a few pending bytes proxied during each loop, which is probably the worse case possible. Above numbers are published to give a performance idea of this micro-benchmark testing, on various systems and APIs. All process did take place with logs enabled, on the same physical PC. Note that the Windows Seven native system (not a VM) may be slow down by its AntiVirus software, whereas the XP VM did not have any AntiVirus installed. WSAPoll API was very disappointing: it is slightly slower than plain Select! In the future, we will eventually uses the IOCP API on Windows, which is told to be much faster (but also much more difficult to implement). An alternative may be to use https://github.com/piscisaureus/wepoll (eventually statically linked), to keep the epoll API and reduce time-to-market. Memory consumption was similar on all OS and API methods. In all cases, the Linux VM with poll/epoll did show the best scaling abilities. The latest test case, creating a lot of traffic, was very stable about its CPU and memory consumption (most time spent in the kernel), and reported "reads=22,520,000 (1 GB) writes=2,510,000 (426 MB)" impressive statistics. } var server: TRTSPOverHTTPServer; timer: TPrecisionTimer; clients, steps: integer; begin if (paramcount = 0) or not TryStrToInt(paramstr(1), clients) then clients := CONCURRENT else if (paramcount = 1) or not TryStrToInt(paramstr(2), steps) then steps := 10; TSynLog.Family.HighResolutionTimeStamp := true; TSynLog.Family.PerThreadLog := ptIdentifiedInOnFile; if steps<200 then TSynLog.Family.Level := LOG_VERBOSE else TSynLog.Family.Level := LOG_STACKTRACE + [sllCustom1]; TSynLog.Family.EchoToConsole := LOG_STACKTRACE + [sllCustom1]; server := TRTSPOverHTTPServer.Create('127.0.0.1', '4999', '4998', TSynLog, nil, nil); try //server.Clients.Options := [paoWritePollOnly]; //server.Options := [acoVerboseLog]; writeln(server.ClassName, ' running'); writeln(' performing tests with ', clients, ' concurrent streams using ', server.Clients.PollRead.PollClass.ClassName, #10); timer.Start; server.RegressionTests(nil, clients, steps); writeln(#10' tests finished in ', timer.Stop); {$ifdef MSWINDOWS} writeln('Press [Enter] to close server.'); Readln; {$endif} finally server.Free; end; end.