📜 ⬆️ ⬇️

speedtest.net via C ++

All the code below is an excerpt from one of my recent project, in which it was necessary to determine the download and upload speed. It was reluctant to reinvent the wheel , therefore, there was a desire to use the service www.speedtest.net , as the most respected and efficient of services of this type. However, as practice has shown, he turned out to be quite unfriendly and had to invent some kind of bicycle.



So, I started this part of the work with digging Google and found one interesting link - http://speedtest.net/speedtest-config.php , which loads a rather large XML file, the beginning of which I will give below.
')
  1. <?xml version = "1.0" encoding = "UTF-8" ?> <settings > <client ip = "*.*.*.*" lat = "60.0000" lon = "100.0000" isp = "Samara Telecom, JSC" isprating = "3.3" rating = "5" ispdlavg = "4449" ispulavg = "1394" /> <licensekey > 9c1687ea58e5e770-1df5b7cd427370f7-4b62a84526ea1f56 </licensekey > <customer > speedtest </customer > <servers > <server url = "http://speedtest.pronea.no/speedtest/upload.php" lat = "69.6828" lon = "18.9428" name = "Tromso" country = "Norway" countrycode = "NO" sponsor = "Pronea AS" sponsorurl = "http://www.pronea.no/" id = "1327" gid = "0" bigsamples = "1" /> <server url = "http://test.nax.no/speedtest/upload.php" lat = "65.8333" lon = "13.2000" name = "Helgeland" country = "Norway" countrycode = "NO" sponsor = "Noraxess Broadband" sponsorurl = "http://www.nax.no" id = "1362" gid = "0" url2 = "http://85.221.66.41/speedtest/upload.php" bigsamples = "1" />
  2. <?xml version = "1.0" encoding = "UTF-8" ?> <settings > <client ip = "*.*.*.*" lat = "60.0000" lon = "100.0000" isp = "Samara Telecom, JSC" isprating = "3.3" rating = "5" ispdlavg = "4449" ispulavg = "1394" /> <licensekey > 9c1687ea58e5e770-1df5b7cd427370f7-4b62a84526ea1f56 </licensekey > <customer > speedtest </customer > <servers > <server url = "http://speedtest.pronea.no/speedtest/upload.php" lat = "69.6828" lon = "18.9428" name = "Tromso" country = "Norway" countrycode = "NO" sponsor = "Pronea AS" sponsorurl = "http://www.pronea.no/" id = "1327" gid = "0" bigsamples = "1" /> <server url = "http://test.nax.no/speedtest/upload.php" lat = "65.8333" lon = "13.2000" name = "Helgeland" country = "Norway" countrycode = "NO" sponsor = "Noraxess Broadband" sponsorurl = "http://www.nax.no" id = "1362" gid = "0" url2 = "http://85.221.66.41/speedtest/upload.php" bigsamples = "1" />
  3. <?xml version = "1.0" encoding = "UTF-8" ?> <settings > <client ip = "*.*.*.*" lat = "60.0000" lon = "100.0000" isp = "Samara Telecom, JSC" isprating = "3.3" rating = "5" ispdlavg = "4449" ispulavg = "1394" /> <licensekey > 9c1687ea58e5e770-1df5b7cd427370f7-4b62a84526ea1f56 </licensekey > <customer > speedtest </customer > <servers > <server url = "http://speedtest.pronea.no/speedtest/upload.php" lat = "69.6828" lon = "18.9428" name = "Tromso" country = "Norway" countrycode = "NO" sponsor = "Pronea AS" sponsorurl = "http://www.pronea.no/" id = "1327" gid = "0" bigsamples = "1" /> <server url = "http://test.nax.no/speedtest/upload.php" lat = "65.8333" lon = "13.2000" name = "Helgeland" country = "Norway" countrycode = "NO" sponsor = "Noraxess Broadband" sponsorurl = "http://www.nax.no" id = "1362" gid = "0" url2 = "http://85.221.66.41/speedtest/upload.php" bigsamples = "1" />
  4. <?xml version = "1.0" encoding = "UTF-8" ?> <settings > <client ip = "*.*.*.*" lat = "60.0000" lon = "100.0000" isp = "Samara Telecom, JSC" isprating = "3.3" rating = "5" ispdlavg = "4449" ispulavg = "1394" /> <licensekey > 9c1687ea58e5e770-1df5b7cd427370f7-4b62a84526ea1f56 </licensekey > <customer > speedtest </customer > <servers > <server url = "http://speedtest.pronea.no/speedtest/upload.php" lat = "69.6828" lon = "18.9428" name = "Tromso" country = "Norway" countrycode = "NO" sponsor = "Pronea AS" sponsorurl = "http://www.pronea.no/" id = "1327" gid = "0" bigsamples = "1" /> <server url = "http://test.nax.no/speedtest/upload.php" lat = "65.8333" lon = "13.2000" name = "Helgeland" country = "Norway" countrycode = "NO" sponsor = "Noraxess Broadband" sponsorurl = "http://www.nax.no" id = "1362" gid = "0" url2 = "http://85.221.66.41/speedtest/upload.php" bigsamples = "1" />
  5. <?xml version = "1.0" encoding = "UTF-8" ?> <settings > <client ip = "*.*.*.*" lat = "60.0000" lon = "100.0000" isp = "Samara Telecom, JSC" isprating = "3.3" rating = "5" ispdlavg = "4449" ispulavg = "1394" /> <licensekey > 9c1687ea58e5e770-1df5b7cd427370f7-4b62a84526ea1f56 </licensekey > <customer > speedtest </customer > <servers > <server url = "http://speedtest.pronea.no/speedtest/upload.php" lat = "69.6828" lon = "18.9428" name = "Tromso" country = "Norway" countrycode = "NO" sponsor = "Pronea AS" sponsorurl = "http://www.pronea.no/" id = "1327" gid = "0" bigsamples = "1" /> <server url = "http://test.nax.no/speedtest/upload.php" lat = "65.8333" lon = "13.2000" name = "Helgeland" country = "Norway" countrycode = "NO" sponsor = "Noraxess Broadband" sponsorurl = "http://www.nax.no" id = "1362" gid = "0" url2 = "http://85.221.66.41/speedtest/upload.php" bigsamples = "1" />
  6. <?xml version = "1.0" encoding = "UTF-8" ?> <settings > <client ip = "*.*.*.*" lat = "60.0000" lon = "100.0000" isp = "Samara Telecom, JSC" isprating = "3.3" rating = "5" ispdlavg = "4449" ispulavg = "1394" /> <licensekey > 9c1687ea58e5e770-1df5b7cd427370f7-4b62a84526ea1f56 </licensekey > <customer > speedtest </customer > <servers > <server url = "http://speedtest.pronea.no/speedtest/upload.php" lat = "69.6828" lon = "18.9428" name = "Tromso" country = "Norway" countrycode = "NO" sponsor = "Pronea AS" sponsorurl = "http://www.pronea.no/" id = "1327" gid = "0" bigsamples = "1" /> <server url = "http://test.nax.no/speedtest/upload.php" lat = "65.8333" lon = "13.2000" name = "Helgeland" country = "Norway" countrycode = "NO" sponsor = "Noraxess Broadband" sponsorurl = "http://www.nax.no" id = "1362" gid = "0" url2 = "http://85.221.66.41/speedtest/upload.php" bigsamples = "1" />
  7. <?xml version = "1.0" encoding = "UTF-8" ?> <settings > <client ip = "*.*.*.*" lat = "60.0000" lon = "100.0000" isp = "Samara Telecom, JSC" isprating = "3.3" rating = "5" ispdlavg = "4449" ispulavg = "1394" /> <licensekey > 9c1687ea58e5e770-1df5b7cd427370f7-4b62a84526ea1f56 </licensekey > <customer > speedtest </customer > <servers > <server url = "http://speedtest.pronea.no/speedtest/upload.php" lat = "69.6828" lon = "18.9428" name = "Tromso" country = "Norway" countrycode = "NO" sponsor = "Pronea AS" sponsorurl = "http://www.pronea.no/" id = "1327" gid = "0" bigsamples = "1" /> <server url = "http://test.nax.no/speedtest/upload.php" lat = "65.8333" lon = "13.2000" name = "Helgeland" country = "Norway" countrycode = "NO" sponsor = "Noraxess Broadband" sponsorurl = "http://www.nax.no" id = "1362" gid = "0" url2 = "http://85.221.66.41/speedtest/upload.php" bigsamples = "1" />


And all further xml is a list of servers on which the speedtest server component is installed. When I realized that the lat and lon parameters are nothing more than latitude and longitude, that is, latitude and longitude, and I’ll have to find my nearest server with my hands - I was thrown into a cold sweat. Schliemazla, I thought, and then the magic of big C ++ began.

The _sendPacket function is a simple construction of the http package, it does not carry any scientific interest in itself, therefore, with your permission, I will skip it. Let's go straight to _getConfig.

  1. void _getConfig ( )
  2. {
  3. std :: stringstream netspeedConfigStream = _sendPacket ( _SpeedtestServer, _SpeedtestConfigUrl ) ;
  4. // Create empty property tree object
  5. using boost :: property_tree :: ptree ;
  6. ptree PropertyTree ;
  7. // Load XML file.
  8. // No namespace qualification is needed, because of Koenig
  9. // lookup on the second argument. If reading fails, exception
  10. // is thrown.
  11. read_xml ( netspeedConfigStream, PropertyTree ) ;
  12. ptree Settings = PropertyTree. get_child ( "settings" ) ;
  13. // Construct client location
  14. _ServerLocation * _ClientLocation ;
  15. // Declare list of Server Locations
  16. std :: vector < _ServerLocation > * _ServerLocationList ;
  17. _ClientLocation = new _ServerLocation ( Settings. Begin ( ) - > second, false ) ;
  18. _ServerLocationList = new std :: vector < _ServerLocation > ;
  19. // Construct list from XML
  20. BOOST_FOREACH ( ptree :: value_type & v, PropertyTree. Get_child ( "settings.servers" ) )
  21. _ServerLocationList - > push_back ( _ServerLocation ( v. Second , true ) ) ;
  22. std :: sort ( _ServerLocationList - > begin ( ) , _ServerLocationList - > end ( ) , _ServerLocationSortFunctor ( _ClientLocation ) ) ;
  23. // Url of the nearest server
  24. std :: vector < std :: string > SplitUrl ;
  25. boost :: split ( SplitUrl, _ServerLocationList - > begin ( ) - > GetUrl ( ) , boost :: is_any_of ( "/" ) ) ;
  26. delete _ClientLocation ;
  27. delete _ServerLocationList ;
  28. _NearestServer = SplitUrl [ 2 ] ;
  29. for ( int i = 3 ; i < SplitUrl. size ( ) - 1 ; ++ i ) // TODO: rewrite with iterators
  30. {
  31. _NearestServerUrl. append ( "/" ) ;
  32. _NearestServerUrl. append ( SplitUrl [ i ] ) ;
  33. }
  34. return ;
  35. }


As you can see from the code, the XML is being retrieved, parsed, and the _ServerLocation structures are populated. In order to prevent violence from bicycles, the search for the nearest server was issued under the call of the STL sort, for which I had to write a sorting functor.

  1. struct _ServerLocationSortFunctor
  2. {
  3. public :
  4. _ServerLocationSortFunctor ( _ServerLocation * ClientLocation ) : _ClientLocation ( ClientLocation ) { }
  5. bool operator ( ) ( _ServerLocation & _First, _ServerLocation & _Second )
  6. {
  7. return _ServerDist ( _ClientLocation, & _First ) < _ServerDist ( _ClientLocation, & _Second ) ;
  8. }
  9. private :
  10. _ServerLocation * _ClientLocation ;
  11. inline double deg2rad ( double ServerAplpha )
  12. {
  13. return ServerAplpha * PI / 180 ;
  14. }
  15. inline double hypot3 ( double dx, double dy, double dz )
  16. {
  17. return sqrt ( dx * dx + dy * dy + dz * dz ) ;
  18. }
  19. double _ServerDist ( _ServerLocation * _First, _ServerLocation * _Second )
  20. {
  21. double phi1 = deg2rad ( _First - > GetLon ( ) ) ;
  22. double psi1 = deg2rad ( _First - > GetLat ( ) ) ;
  23. double phi2 = deg2rad ( _Second - > GetLon ( ) ) ;
  24. double psi2 = deg2rad ( _Second - > GetLat ( ) ) ;
  25. double cos_psi1 = cos ( psi1 ) ;
  26. double x1 = EarthRadius * cos ( phi1 ) * cos_psi1 ;
  27. double y1 = EarthRadius * sin ( phi1 ) * cos_psi1 ;
  28. double z1 = EarthRadius * sin ( psi1 ) ;
  29. double cos_psi2 = cos ( psi2 ) ;
  30. double x2 = EarthRadius * cos ( phi2 ) * cos_psi2 ;
  31. double y2 = EarthRadius * sin ( phi2 ) * cos_psi2 ;
  32. double z2 = EarthRadius * sin ( psi2 ) ;
  33. double d = hypot3 ( x2 - x1, y2 - y1, z2 - z1 ) ;
  34. return EarthRadius * acos ( 1 - ( d * d ) / ( 2 * EarthRadius * EarthRadius ) ) ;
  35. }
  36. } ;


The school course is not geography, not geometry in action, well, and the nearest server is found.

Further it becomes not so interesting, because the speed measurement code itself is trivial.
  1. void _testLatency ( )
  2. {
  3. std :: string Url ( _NearestServerUrl ) ;
  4. Url. append ( _LatencyUrl ) ;
  5. boost :: timer LatencyTimer ;
  6. _sendPacket ( _NearestServer, Url ) ;
  7. _LatencyTime = LatencyTimer. elapsed ( ) ;
  8. std :: cout << "PING:" << _LatencyTime << std :: endl ;
  9. return ;
  10. }
  11. void _testDownload ( )
  12. {
  13. std :: string Url ( _NearestServerUrl ) ;
  14. Url. append ( _DownloadUrl ) ;
  15. boost :: timer DownloadTimer ;
  16. std :: stringstream DownloadPacket = _sendPacket ( _NearestServer, Url ) ;
  17. double DownloadTime = DownloadTimer. elapsed ( ) ;
  18. double DownloadSpeed = ( DownloadPacket. str ( ) . length ( ) / ( DownloadTime - _LatencyTime ) ) * 8/1024 ;
  19. std :: cout << "DOWNLOAD:" << DownloadSpeed << std :: endl ;
  20. return ;
  21. }
  22. void _testUpload ( )
  23. {
  24. std :: string Url ( _NearestServerUrl ) ;
  25. Url. append ( _UploadUrl ) ;
  26. std :: string UploadDataValue ( _UploadBytes, 'a' ) ;
  27. std :: string UploadData ( "content0 =" ) ;
  28. UploadData + = UploadDataValue ;
  29. boost :: timer UploadTimer ;
  30. _sendPacket ( _NearestServer, Url, true , UploadData ) ;
  31. double UploadTime = UploadTimer. elapsed ( ) ;
  32. double UploadSpeed = ( _UploadBytes / ( UploadTime - _LatencyTime ) ) * 8/1024 ;
  33. std :: cout << "UPLOAD:" << UploadSpeed << std :: endl ;
  34. }


Maybe someone thread and come in handy. The code was tested on Visual Studio 2010 (the rest of the project was sharpened under Windows).

I want to say thank you to the documentation on STL and Boost, as well as to the pieces of the source code that I found on the Internet for self-study.

The full code of this opus is available at http://paste.pocoo.org/show/215424/
______________________
The text was prepared in the Blog Editor from © SoftCoder.ru

Source: https://habr.com/ru/post/93916/


All Articles