Как стать автором
Обновить

speedtest.net via C++

Время на прочтение10 мин
Количество просмотров6.6K
Весь нижеприведенный код — выдержка из одного моего недавнего проекта, в рамках которого было необходимо определять скорость download и upload. Изобретать велосипед было неохота, посему возникло желание воспользоваться сервисом www.speedtest.net, как наиболее уважаемым и работоспособным из сервисов такого типа. Впрочем, как показала практика, он оказался вполне недружелюбным и некий велосипед таки пришлось изобрести.



Итак, начал я данную часть работы с перекапывания Гугла и нашел таки одну интересную ссылочку — http://speedtest.net/speedtest-config.php, по которой грузится немаленькая XML-ка, начало которой я приведу ниже.

  1.  
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <settings>
  4. <client ip="*.*.*.*" lat="60.0000" lon="100.0000" isp="Samara Telecom, JSC" isprating="3.3" rating="5" ispdlavg="4449" ispulavg="1394" />
  5. <licensekey>9c1687ea58e5e770-1df5b7cd427370f7-4b62a84526ea1f56</licensekey>
  6. <customer>speedtest</customer>
  7. <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" />
  8. <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" />
  9.  


И вся дальнейшая xml — это список серверов, на которых установлена серверная компонента speedtest-а. Когда я понял, что параметры lat и lon — это не что иное, как latitude и longitude, сиречь широта и долгота, и искать ближайший ко мне сервер придется руками — меня бросило в холодный пот. «Шлимазлы», — подумал я и тут началась магия большого C++.

Функция _sendPacket являет собой простое конструирование http-пакета, никакого научного интереса в себе не несет, посему, с вашего позволения, я ее пропущу. Перейдем сразу к _getConfig.

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


Как видно из кода — идет получение, последующий разбор XML и заполнение вектора структур _ServerLocation. В целях непротивления велосипедам насилием поиск ближайшего сервера был оформлен под вызов STL-евского sort, для чего пришлось написать функтор сортировки.

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


Школьный курс не то географии, не то геометрии в действии, ну, а ближайший сервер найден.

Дальше становится не так интересно, потому что сам код измерения скорости — тривиален.
  1.  
  2.         void _testLatency()
  3.         {
  4.                 std::string Url(_NearestServerUrl);
  5.                 Url.append(_LatencyUrl);
  6.                
  7.                 boost::timer LatencyTimer;
  8.                 _sendPacket(_NearestServer, Url);
  9.                 _LatencyTime = LatencyTimer.elapsed();
  10.                
  11.                 std::cout << "PING: " << _LatencyTime << std::endl;
  12.                
  13.                 return;
  14.         }
  15.        
  16.         void _testDownload()
  17.         {
  18.                 std::string Url(_NearestServerUrl);
  19.                 Url.append(_DownloadUrl);
  20.                
  21.                 boost::timer DownloadTimer;
  22.                 std::stringstream DownloadPacket = _sendPacket(_NearestServer, Url);
  23.                 double DownloadTime = DownloadTimer.elapsed();
  24.                
  25.                 double DownloadSpeed = (DownloadPacket.str().length() / (DownloadTime - _LatencyTime)) * 8 / 1024;
  26.                
  27.                 std::cout << "DOWNLOAD: " << DownloadSpeed << std::endl;
  28.                
  29.                 return;
  30.         }
  31.        
  32.         void _testUpload()
  33.         {
  34.                 std::string Url(_NearestServerUrl);
  35.                 Url.append(_UploadUrl);
  36.                
  37.                 std::string UploadDataValue(_UploadBytes, 'a');
  38.                 std::string UploadData("content0=");
  39.                
  40.                 UploadData += UploadDataValue;
  41.                
  42.                 boost::timer UploadTimer;
  43.                 _sendPacket(_NearestServer, Url, true, UploadData);
  44.                 double UploadTime = UploadTimer.elapsed();
  45.                
  46.                 double UploadSpeed = (_UploadBytes / (UploadTime - _LatencyTime)) * 8 / 1024;
  47.                
  48.                 std::cout << "UPLOAD: " << UploadSpeed << std::endl;
  49.                
  50.         }
  51.  


Может быть, кому-нить и пригодится. Протестирован код был на Visual Studio 2010 (остальная часть проекта была заточена под Windows).

Хочу сказать спасибо документации по STL и Boost, а также кускам исходников, найденых мною на просторах Интернета для самообучения.

Полный код данного опуса доступен по ссылке http://paste.pocoo.org/show/215424/
______________________
Текст подготовлен в Редакторе Блогов от © SoftCoder.ru
Теги:
Хабы:
+20
Комментарии17

Публикации

Изменить настройки темы

Истории

Работа

QT разработчик
6 вакансий
Программист C++
123 вакансии

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн