Application de mesure reseau pour iOS

De Ensiwiki
Aller à : navigation, rechercher
Application of networking measurment under iOS
Project Project of speciality 2A
Sub-project Networking
Students Antoine SPITAELS (TEL)

Simon GUIGNOUARD (TEL)

Siwar KRIAA (TEL)

Promo 2012
Tutor Franck ROUSSEAU Franck.Rousseau@imag.fr


Ipmt-iphone.png Ipmt-ios2.png





Introduction

The aim of this project is to develop an iPhone application for network performance measurement in real time enabling wireless mobility. IPMT, a tool developped in C by the team Drakkar was used as a basis. IPMT is a tool initially implemented on Unix plateforms that is composed of two programs for sending traffic respectively in TCP or in UDP and two programs for receiving data. We have to adapt the IPMT code in order to be compatible with an iOS plateform, and to build an user interface to remplace the command line interface.

This project could be divided in two parts:

  • Adaptation from Posix plateform to iOS
  • Build a Graphical interface

To get IPMT source code:

svn checkout svn://svn.ligforge.imag.fr/svnroot/ipmt
svn checkout https://svn.ligforge.imag.fr/svnroot/ipmt

From a posix plateform to iOS

Flags insertion

The application is initially implemented in C-language and run under several OS (Solaris, FreeBSD, MacOS X and Windows/Cygwin). The first step consists on adapting this application for iOS. The language for developing applications for iPhone is Objective-C,an overlayer of C. We don't virtually have to rewrite the entire application but rather check the compatibility of all C libraries and all IPMT functions with the iOS plateform.

Using flags such as #ifdef __FreeBSD_, #ifdef __APPLE_ enables you to force the compiler to compile or not a certain piece of code depending on the OS.

First of all, you will need to include the following header "TargetConditionals.h" for Apple plateforms as shown below:

#ifdef __APPLE__ 
#include "TargetConditionals.h" 
#endif

This librairy allow you to use the Apple's Flag to target a specific device (TARGET_IPHONE_SIMULATOR,TARGET_OS_IPHONE,TARGET_MAC_OS...)


For example:

#if !TARGET_IPHONE_SIMULATOR && !TARGET_OS_IPHONE    
   exit(1); 
#else 
   return 0; 
 #endif

It this example, the application will quit when this function is executed on Unix OS: exit(0) and the command line will be given back to the user but under iOS this code implies only the end of the function.

Management of executables

If your application is implemented on a Posix plateform then it might include many executables which are linked within a Makefile. For iPhone, only one executable is allowed. Then, to adapt your application to iOS two solutions are possible:

  • Keep several executables and use NSTask
  • Replace each main by a function while coding for iPhone. Only one main should be left.

We choose the simplest solution: using a function instead of an executable:

#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
int
function(int argc,char **argv)
#else
int
main(int argc,char **argv)
#endif

To launch a function or a NSTask without losing the user interaction we have to launch the IPMT function in a new thread which is liberated when we quit the application. In order to create a thread we choose to use NSThread

Reset of parameters

In order to allow relaunch of IPMT tools in iOS, we have to make some changes to the IPMT code. The first consists on resetting every variable and function used in the IPMT program. To adapt a command line tool to a graphical application we have to reset the arguments analyzer. << int getopt(int, char * const [], const char *) >> "getopt" is a function that analyses the arguments of the command line. The first argument "int" indicates the number of elements of the table "char*[] " containing arguments transmitted to the main(). An option is preceded by "-". "optind" is a variable which indicates the next element to be analyzed in the char*[]. The system set the value of this variable to 1. Under iOS, to be able to analyze the same table of strings many times ie each time we launch a request, we have to reset "getopt" by setting the value of "optind" to 1.

   #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
   optind=1;
   #endif  
   while ((ch = getopt(argc, argv, "abdegHhi:jM:m:r:o:p:t:vk46q")) != -1)

You can get further information on this issue on this page [1]

Timer

We meet some issues in iOS with timer, the C Timer causes a OS crash. So we decide to replace the initial timer of IPMT by a NSTimer.
Our NSTimer have two main goal:

    • update the result display
    • replace the initial timer of IPMT which allows the user to control the bitrate of the test

graphical interface

Integration of command lines

Under a Posix plateform the application is launched thanks to command lines directly from a terminal. This method is not possible under iPhone because there is natively no shell or another way to access to the network layer, and especially, typing command lines with a tiny virtual keyboard is not very convenient. Therefore, the first thing to do is to provide a user-friendly graphical interface.

For beginners in Objective-C, the Interface Builder tool integrated in Xcode is quite helpful to quickly set a simple and functional GUI. Classic objects of user interface (UITextfield, UIToolbar, UIButton etc.) can be graphically manipulated. Then, objects have just to be linked to their declaration.

In our application, we retrieve arguments entered by the user thanks to different objects (UITextfield, UISegmentedControl, UISlider) that we store in a table. The following example shows how to add an option to the table of arguments.

Finally, our table will contain the command line that the user would have entered in the terminal. (eg: tcpmt -p 13000 -d 10 10.0.2.10) Then, we just have to call the function with the number of parameters and the table as arguments. In our case, functions are coded in C, so for example we call: tcpmt(nb_param,arg);

Tricks about editable UITextField

Make the view slide up/down

When the user tap on an editable textfield, the keyboard shows up and can hide the textfield if it is on the bottom part of the view. This is quite bothering if the user wants to see what he is typing. The solution is to make the entire view slide up on the background, in order that the textfield appears on the visible part of the view (above the keyboard). Thus, we have to implement two methods (textFieldShouldBeginEditing and textFieldShouldEndEditing) to get the desired effect.
See code below.


Add a button to the keyboard

Done button
Keyboard7.png

We wanted to accept only numbers in several textfields so we set default keyboard to numpad which provide the advantage to ease the typing of numbers and to oblige the user to enter numbers. However, numpad keyboard does not provide a return button. Therefore we needed to add a "Done" button to the keyboard in order to make it disappear when the button is tapped. See code below.

-(void)addButtonToKeyboard {
create custom button
doneButton.frame = CGRectMake(0, 163, 106, 53);
doneButton.adjustsImageWhenHighlighted = NO;
[doneButton setImage:[UIImage imageNamed:@"DoneUp3.png"] forState:UIControlStateNormal];
[doneButton setImage:[UIImage imageNamed:@"DoneDown3.png"] forState:UIControlStateHighlighted];
[doneButton addTarget:self action:@selector(doneButton:) forControlEvents:UIControlEventTouchUpInside];
	// locate keyboard view
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView* keyboard;
for(int i=0; i<[tempWindow.subviews count]; i++) {
		keyboard = [tempWindow.subviews objectAtIndex:i];
		// keyboard found, add the button
		if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 3.2) {
			if([[keyboard description] hasPrefix:@"<UIPeripheralHost"] == YES)
				[keyboard addSubview:doneButton];
		} else {
			if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES)
				[keyboard addSubview:doneButton];
		}
	}
 }

Rool down the keyboard

In order to make the keyboard disappear when typing is done we have to implement a little method, that has to be linked to the Did end on exit event of the textfield.

-(IBAction) doneEditing:(id)sender {
   [sender resignFirstResponder];
}

Graphic plotting

CorePlot is a tierce plotting framework that enables creating graphs in an iPhone application. There are many versions of CorePlot. In our case, we have chosen the recent one available on Google Code [2]. To install this framework on Xcode 4, you have to follow these instructions:

  1. Copy the CorePlotHeaders directory to your Xcode project.
  2. Copy the Frameworks file to your Xcode project
  3. Open your apps Target Build Settings, and for Other Linker Flags include this: -ObjC -all_load
  4. Add the QuartzCore framework to the project.

This is the static method for installing the framewok. Thus, you are not supposed to re-install CorePlot each time you change the device in order to test the application. All the previous configuration will figure on you GIT deposit. Finally, you should import the following header "CorePlot-CocoaTouch.h" in the appropriate files to be able to call classes of CorePlot.

Example of a basic graph

//create a CPGraphHostingView object that will host the graph
graphView = [ [ CPGraphHostingView alloc ] initWithFrame:CGRectMake(0.0, 40.0, 320.0, 395.0-20.0) ];

//add the graphView to the current view
 [ self.view addSubview: graphView];

//create a graph and make graphView host it
graph = [[ [ CPXYGraph alloc ] initWithFrame: self.view.bounds ] autorelease];
graphView.hostedGraph = graph;

//Create a scatter plot
CPScatterPlot *plot1 = [[[CPScatterPlot alloc]initWithFrame:self.view.bounds] autorelease];

//add it to the graph
[graph addPlot:plot1];
 
//define the plotting space
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;

//Defines the number of points to plot
- (NSUInteger)numberOfRecordsForPlot:(CPPlot *)plot {
   return 10;    
}

//Value of points - example
- (NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
        if (fieldEnum == CPScatterPlotFieldX) {
              return [ NSNumber numberWithInteger:-10.0+index ];
         } else if (fieldEnum == CPScatterPlotFieldY) { 
              return [ NSNumber numberWithFloat:(-10.0+index)/2.0 ];
         }
    return [ NSNumber numberWithFloat:0.0 ];
}