Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00196 007138 14658329 na godz. na dobę w sumie
Programowanie w Linuksie. Ćwiczenia - książka
Programowanie w Linuksie. Ćwiczenia - książka
Autor: , , Liczba stron: 320
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-4345-5 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> inne - programowanie
Porównaj ceny (książka, ebook, audiobook).

Programowanie w Linuksie? To naprawdę proste!

Systemy należące do rodziny Linux zdobyły ogromną popularność jako stabilne, wydajne, bezpieczne i darmowe środowiska operacyjne zarówno wśród użytkowników prywatnych, jak i wielkich korporacji. Dziś środowiska te można spotkać już niemal wszędzie, a różne dystrybucje walczą o palmę pierwszeństwa i bez kompleksów konkurują z zamkniętymi komercyjnymi systemami, takimi jak MS Windows czy Mac OS X. Wzrostowi popularności Linuksa wśród 'zwykłych' użytkowników towarzyszy oczywiście wzrost zapotrzebowania na oprogramowanie użytkowe, ponieważ nikt nie będzie chciał korzystać z systemu operacyjnego, jeśli nie będzie w stanie znaleźć odpowiednich dla siebie narzędzi pracy.

Wielu programistów niechętnie odnosi się do tworzenia aplikacji działających pod Linuksem, ponieważ wydaje im się, że brak w nim odpowiednich narzędzi, system stawia im wyższe wymagania, a sam kod pisze się trudniej. Tymczasem Linux obsługuje się podobnie jak inne systemy, a możliwości, jakie proponuje, są naprawdę spore. 'Programowanie w Linuksie. Ćwiczenia' to książka, która w praktyczny sposób prezentuje najbardziej popularne języki programowania w tym systemie oraz ich zastosowania. Pomaga też wyposażyć warsztat programisty w bezpłatne narzędzia i właściwie je skonfigurować. Informuje, jak rozpocząć tworzenie aplikacji i rozwinąć swoje umiejętności.

Odkryj moc drzemiącą w Linuksie! Naucz się w nim programować!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

• Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis treĂci Rozdziaï 1. Wprowadzenie JÚzyki programowania JÚzyk C JÚzyk C++ JÚzyk Java JÚzyk Python JÚzyk Perl PHP JavaScript Rozdziaï 2. Darmowe narzÚdzia programistyczne Rozdziaï 3. Edytory tekstowe Kompilatory Zintegrowane Ărodowiska programistyczne Zastosowania wybranych Ărodowisk programistycznych JÚzyk C JÚzyk C++ JÚzyk Java JÚzyk Python JÚzyk Perl Graficzny interfejs uĝytkownika JÚzyk PHP JavaScript NarzÚdzia 5 9 11 12 13 15 17 18 20 23 23 48 65 123 123 152 169 195 211 224 249 267 280 4 Programowanie w Linuksie • mwiczenia 3 Zastosowania wybranych Ărodowisk programistycznych Po wstÚpnym omówieniu w poprzednim rozdziale róĝnych Ărodowisk programistycznych teraz przyszedï czas na poka- zanie, jak uĝywa siÚ ich w praktyce w codziennej pracy pro- gramisty. W poniĝszym tekĂcie w szczególnoĂci przedstawiono, jak ustawia siÚ potrzebne opcje umoĝliwiajÈce kompilowanie i urucha- mianie programów, a takĝe procedury instalacji koniecznych bibliotek oraz dodawania ich do projektów. Uĝyto do tych celów prostych przy- kïadów oraz zadañ programistycznych do samodzielnego rozwiÈza- nia. Kody ěródïowe czytelnik znajdzie w archiwum na serwerze FTP wydawnictwa Helion. JÚzyk C Przykïadowe programy Uĝyte programy prezentujÈ wybrane zagadnienia zwiÈzane z progra- mowaniem systemowym oraz sieciowym. Gïównym celem rozdziaïu jest jednak zaprezentowanie (na uĝytecznych przykïadach programów 124 Programowanie w Linuksie • mwiczenia przygotowanych w jÚzyku C), jakie opcje i ustawienia zastosowaÊ w Code::Blocks IDE, a takĝe GCC, aby skompilowaÊ i uruchomiÊ kon- kretny program. m W I C Z E N I E 3.1 Mnoĝenie macierzy W nowych wersjach gcc i icc zostaï zaimplementowany standard OpenMP.1 Naleĝy napisaÊ program, który bÚdzie mnoĝyï dwie macierze dwuwymiarowe. MajÈ zostaÊ przygotowane dwie wersje programu — jedna bez uĝycia OpenMP, a druga z uĝyciem OpenMP. Macierze bÚdÈ zawieraÊ wartoĂci losowo generowane przez sam program. Operator — z linii poleceñ — ma podawaÊ trzy parametry. Pierwszy okreĂlajÈcy wysokoĂÊ pierwszej macierzy, drugi szerokoĂÊ pierwszej macierzy i wysokoĂÊ drugiej, a ostatni szerokoĂÊ drugiej macierzy. InteresujÈcym dla operatora wynikiem jest czas wykonywania obliczeñ. Listing 3.1.1. Mnoĝenie macierzy 1 #include stdio.h 2 #include stdlib.h 3 #include time.h 4 #define DEFAULT_SIZE 100 5 #define true 1 6 #define false 0 Na poczÈtku trzeba doïÈczyÊ podstawowe biblioteki do obsïugi wejĂcia i wyjĂcia, a takĝe czasu. Naleĝy zdefiniowaÊ staïe okreĂlajÈce rozmiar (DEFAULT_SIZE) macierzy i wartoĂci logiczne (true, false). 7 //funkcja generująca macierze 8 int **tabCreate(int y, int x, int losuj) { 9 int **a,i,i2; 10 a = (int **)malloc(sizeof(int *)*y); //przydzielenie pamiĊci dla jednego wymiaru 11 for(i=0;i y;i++) { 12 a[i] = (int *)malloc(sizeof(int)*x); //przydzielenie pamiĊci dla drugiego wymiaru 13 if(losuj) { 14 for(i2=0;i2 x;i2++) a[i][i2] = rand() 10; //generowanie elementów 15 } else for(i2=0;i2 x;i2++) a[i][i2] = 0; 1 http://gcc.gnu.org/gcc-4.2/changes.html Rozdziaï 3. • Zastosowania wybranych Ărodowisk programistycznych 125 16 } 17 return a; 18 } Ustawienie zmiennej losuj na true powoduje automatyczne genero- wanie tablicy, w przeciwnym wypadku jest ona wypeïniana zerami. 19 //funkcja wyliczająca róĪnicĊ miĊdzy dwoma czasami 20 long long int toddiff(struct timeval *tod1, struct timeval *tod2) 21 { 22 long long t1, t2; 23 t1 = tod1- tv_sec * 1000000 + tod1- tv_usec; 24 t2 = tod2- tv_sec * 1000000 + tod2- tv_usec; 25 return t1 - t2; 26 } 27 int main( int argc, char *argv[]) 28 { 29 int **a, **b, **c; //deklaracja 3 tablic 30 int i,j,k; 31 struct timeval tod1, tod2; 32 int temp; 33 int w[3]; 34 srand(time(NULL)); 35 for(i=0;i 3;i++) { 36 if(argc =2+i) { 37 temp = 0; 38 sscanf(argv[1+i], d , temp); 39 if(temp 0) w[i] = temp; 40 else { 41 w[i] = DEFAULT_SIZE; 42 } 43 } 44 else { 45 w[i] = DEFAULT_SIZE; 46 } 47 printf( arg[ d] = d , i,w[i]); 48 } Powyĝszy kod od linijki 35 sïuĝy do pobrania wymiarów macierzy. Naleĝy podaÊ trzy parametry. SÈ to kolejno: „wysokoĂÊ pierwszej macierzy”, „szerokoĂÊ pierwszej i jednoczeĂnie wysokoĂÊ drugiej ma- cierzy”, „szerokoĂÊ drugiej macierzy”. Gdy nie zostanÈ podane parame- try, rozmiary zostanÈ ustawione automatycznie (DEFAULT_SIZE). 49 //utworzenie 3 macierzy 50 a = tabCreate(w[0],w[1],true); 51 b = tabCreate(w[1],w[2],true); 52 c = tabCreate(w[0],w[2],false); 126 Programowanie w Linuksie • mwiczenia 53 printf( Start ); 54 gettimeofday( tod1, NULL); 55 //------------------------ 56 for(i=0;i w[0];i++){ 57 for(j=0;j w[2];j++) { 58 for(k=0;k w[1];k++) { 59 c[i][j] += a[i][k]*b[k][j]; 60 } 61 } 62 } 63 //------------------------ Kod znajdujÈcy siÚ miÚdzy linijkami 55 i 63 odpowiada za mnoĝenie macierzy. Przy póěniejszej modyfikacji wïaĂnie tu bÚdÈ wprowadzane zmiany. 64 gettimeofday( tod2, NULL); 65 //zwalnianie pamiĊci 66 for(i=0;i w[0];i++) { 67 free(c[i]); 68 free(a[i]); 69 } 70 for(i=0;i w[1];i++) { 71 free(b[i]); 72 } 73 free(a); 74 free(b); 75 free(c); 76 printf( Czas: ld milisekund ,(long int)(toddiff( tod2, tod1) / 1000.0)); 77 return 0; 78 } Mnoĝenie macierzy z uĝyciem OpenMP Kolejny program w duĝej czÚĂci ma taki sam kod jak poprzedni, dla- tego zostanÈ przedstawione tylko zmienione lub dodane fragmenty kodu. PierwszÈ nowÈ czynnoĂciÈ jest dodanie biblioteki umoĝliwia- jÈcej korzystanie z OpenMP — czyli dla gcc omp.h . NastÚpnie naleĝy zmodyfikowaÊ kod znajdujÈcy siÚ miÚdzy linijkami 55 i 63. Listing 3.1.2. Mnoĝenie macierzy z uĝyciem OpenMP 55 //------------------------ 55a #pragma omp parallel shared(a,b,c,w) private(i, j, k) Rozdziaï 3. • Zastosowania wybranych Ărodowisk programistycznych 127 Mamy zamiar wykorzystaÊ algorytm, który „zrównolegli” mnoĝenia macierzy, wiÚc trzeba o tym poinformowaÊ kompilator. OdpowiedniÈ dyrektywÚ wpisano w linii 55a. Omp jest sïowem kluczowym dla OpenMP, parallel sïuĝy do wskazania kompilatorowi, który obszar bÚdzie zrównoleglony, zaĂ shared i private okreĂlajÈ, które zmienne bÚdÈ wspólne (wszystkie wÈtki majÈ dostÚp do tej samej zmiennej), a które prywatne (kaĝdy wÈtek ma wïasnÈ kopiÚ zmiennej). Jak widaÊ, wszystkie liczniki pÚtli zostaïy zmiennymi prywatnymi, dziÚki czemu kaĝdy wÈtek bÚdzie miaï swój wïasny licznik. Warto wspomnieÊ, ĝe w praktyce za wspólne zmienne zwykïo siÚ przyjmowaÊ te, które okreĂlajÈ liczbÚ iteracji pÚtli, a takĝe zmienne uĝywane w dziaïaniach, których wartoĂci w trakcie obliczeñ nie zmieniajÈ siÚ. 55b { 55c #pragma omp for schedule(dynamic) Uĝyta tutaj dyrektywa schedule jest dopuszczalna tylko dla pÚtli for. DziÚki niej moĝna w pewnym stopniu kontrolowaÊ podziaï iteracji pomiÚdzy wÈtki. Schedule przyjmuje dwa parametry — sposób podziaïu iteracji i rozmiar podzbioru (jest to opcjonalny parametr). Zastoso- wany przez nas zapis oznacza, ĝe kaĝdy wÈtek bÚdzie miaï przydzie- lony jednoelementowy podzbiór iteracji. 56 for(i=0;i w[0];i++){ 57 for(j=0;j w[2];j++) { 58 for(k=0;k w[1];k++) { 59 c[i][j] += a[i][k]*b[k][j]; 60 } 61 } 62 } 62a } 63 //------------------------ Skoro oba programy sÈ juĝ gotowe, to teraz naleĝy je skompilowaÊ i przetestowaÊ. Uĝyjemy do tego celu najpierw kompilatora GNU GCC. PierwszÈ wersjÚ programu naleĝy skompilowaÊ przy standardowych ustawieniach kompilatora, a w drugim przypadku naleĝy uĝyÊ specjal- nej flagi –fopenmp. Dodatkowo naleĝy wykonaÊ kompilacjÚ za pomocÈ kompilatora ICC (opcja do kompilacji z OpenMP to -openmp). AnalizujÈc wpisy w tabeli 3.1, nietrudno zauwaĝyÊ, ĝe uĝycie OpenMP zasadniczo zwiÚksza szybkoĂÊ mnoĝenia macierzy, ale równieĝ rodzaj uĝytego kompilatora ma duĝe znaczenie. 128 Programowanie w Linuksie • mwiczenia Tabela 3.1. Wpïyw uĝytego kompilatora i bibliotek na szybkoĂÊ pracy programu mnoĝÈcego macierze dwuwymiarowe Wymiar pierwszej macierzy Wymiar drugiej macierzy NarzÚdzie uĝyte do kompilacji GCC GCC w wersji z OpenMP ICC ICC w wersji z OpenMP Czas potrzebny na mnoĝenie macierzy [ms] 500×500 500×500 100×1000 1000×1000 1345 3448 886 1957 109 237 72 138 1000×1000 1000×2000 34448 19521 2383 1364 Programy byïy testowane na komputerze z procesorem Phenom II N620 i 4 GB pamiÚci RAM. m W I C Z E N I E 3.2 Utworzenie obrazka Napisz program generujÈcy i zapisujÈcy kolorowy obraz o wymiarach 400 na 400 pikseli w formacie PPM (ang. Portable PixMap). Tïo obrazka powinno przechodziÊ — w sposób ciÈgïy — od koloru niebieskiego (w lewym górnym rogu) do zielonego (w prawym dolnym rogu). Istot- nymi elementami obrazka powinny byÊ zestawy czerwonych koncen- trycznych okrÚgów tworzÈce regularnÈ siatkÚ. Na poczÈtku naleĝy poszukaÊ informacji o formacie PPM. Moĝna je znaleěÊ w Internecie lub teĝ uruchomiÊ terminal w Ubuntu i wpisaÊ polecenie man ppm. Na stronach elektronicznego podrÚcznika uĝyt- kownika (man) dostÚpna jest dokïadna specyfikacja nagïówka, a takĝe sposobu reprezentacji danych dla interesujÈcego nas formatu. PPM to prosty format przeznaczony do przechowywania kolorowych grafik w trybie map bitowych. Format pliku PPM zawiera (wg podrÚcznika man2): 1. „magiczny numer ” okreĂlajÈcy rodzaj pliku — P3 (tekstowy) lub P6 (binarny); 2 Specyfikacja PPM z podrÚcznika man. Rozdziaï 3. • Zastosowania wybranych Ărodowisk programistycznych 129 2. biaïy znak (spacja, tabulator, CR, LF); 3. szerokoĂÊ i wysokoĂÊ zapisane jako dziesiÚtne znaki ASCII rozdzielone biaïym znakiem; 4. biaïy znak; 5. maksymalnÈ wartoĂÊ komponentu kolorowego, znów jako dziesiÚtne ASCII; 6. wartoĂci kolejnych kolorów zapisane tekstowo (dla P3) lub binarnie (dla P6). Listing 3.2.1. Generowanie obrazka 1 #include stdio.h 2 #include stdlib.h 3 int main() 4 { 5 const int szer = 400; //szerokoĞü 6 const int wys = 400; //wysokoĞü 7 const float wsp = 3.2; //3.2 ~= (szer+wys) / 255 8 int i,j; //liczniki pĊtli 9 static unsigned char kolor[3]; //tablica kolorów 10 FILE * file; //deskryptor pliku 11 file = fopen( obraz.ppm , wb );//otwarcie pliku Funkcja fopen() otwiera plik o nazwie i Ăcieĝce okreĂlonej przez pierw- szy parametr oraz wiÈĝe z nim strumieñ. Drugi parametr okreĂla spo- sób, cel, a takĝe tryb otwarcia strumienia. W naszym przypadku otwie- rany bÚdzie plik obraz.ppm (znajdujÈcy siÚ w tym samym katalogu co kod ěródïowy) tylko do zapisu ( w ) w trybie binarnym ( b ). Inne moĝliwe parametry to r (tylko do odczytu), a (dodaj na koniec istniejÈcego pliku lub utwórz nowy ), r+ (do odczytu i zapisu jed- noczeĂnie — modyfikacja pliku), w+ (do zapisu i odczytu — jeĂli plik istnieje, zostanie nadpisany). Moĝna wybraÊ takĝe tryb tekstowy ( t ). WartoĂÊ zwracana przez funkcjÚ fopen() to adres utworzonego strumienia lub NULL w przypadku niepowodzenia. 12 fprintf(file, P6 d d 255 , szer, wys); //nagáówek Funkcja fprintf() umieszcza sformatowane dane w strumieniu okre- Ălonym przez pierwszy parametr. W podanym przypadku dane sÈ zapisywane do pliku w formie okreĂlonej przez ïañcuch formatujÈcy (drugi parametr). Za symbolem d bÚdzie wstawiany ciÈg znaków okreĂlony przez parametr nieustalony. WartoĂciÈ tej funkcji staje siÚ liczba wysyïanych znaków lub EOF w przypadku wystÈpienia bïÚdu. 130 Programowanie w Linuksie • mwiczenia 13 for(i=0;i wys;i++){ 14 for(j=0;j szer;j++){ 15 kolor[0] = abs(j*j+i*i) 255; //czerwone kóáeczka 16 /* zielony gradient — i+j — coraz mocniejszy kolor, 3.2 — Īeby w obrazku mieĞciá siĊ ´jeden gradient */ 17 kolor[1] = round(abs(i+j)/wsp); 18 /* niebieski gradient od caáoĞci odejmujemy i i j */ 19 kolor[2] = (int)(abs(255-(i+j)/wsp)); 20 fwrite(kolor,1,3,file); Funkcja fwrite() wysyïa do pliku trzy elementy (o rozmiarze jednego bajta kaĝdy) z tablicy kolor. Jeĝeli operacja powiedzie siÚ, wartoĂciÈ zwracanÈ jest liczba wszystkich wysïanych elementów. W przypadku bïÚdu moĝe byÊ mniejsza od wartoĂci okreĂlanej przez trzeci parametr (moĝe byÊ takĝe równa zero). 21 } 22 } 23 fclose(file);//zamkniĊcie pliku Funkcja fclose(file) zamyka strumieñ podany przez parametr file. Wszystkie bufory zwiÈzane ze strumieniem sÈ czyszczone. Zwracana wartoĂÊ to 0 w przypadku powodzenia lub EOF, jeĝeli wystÈpiï jaki- kolwiek bïÈd. 24 return 0; 25 } Po skompilowaniu i uruchomieniu programu zostanie utworzony obra- zek (rysunek 3.1), którego naleĝy szukaÊ w katalogu roboczym pliku ěródïowego (w przypadku gdy zostaï uĝyty Code::Blocks). Rysunek 3.1. Utworzony obraz w formacie PPM Rozdziaï 3. • Zastosowania wybranych Ărodowisk programistycznych 131 Na koniec naleĝy zauwaĝyÊ, ĝe w trakcie pisania tego programu zasto- sowano sekwencyjny dostÚp do danych (od poczÈtku do koñca). Moĝ- liwy jest takĝe swobodny dostÚp do danych (np. przez uĝycie funkcji fseek(), fgetpos()). InspiracjÈ tego zadania byïa strona3 Wikibooks. Autorzy ksiÈĝki majÈ ĂwiadomoĂÊ, ĝe opis tego, co ma siÚ znaleěÊ na obrazku (delikatnie mówiÈc), nie jest zbyt precyzyjny i pokazuje, ĝe jeden obraz to wiÚcej niĝ setki sïów. m W I C Z E N I E 3.3 Uprawnienia dla pliku — zadanie do samodzielnego wykonania W Linuksie z kaĝdym plikiem powiÈzane sÈ prawa okreĂlajÈce, kto co moĝe z nim zrobiÊ (odczytaÊ, zapisaÊ, wykonaÊ). W praktyce mamy trzy typy uĝytkowników: wïaĂciciel pliku, kaĝdy, kto naleĝy do grupy powiÈzanej z wïaĂcicielem, i inni. Uprawnienia sÈ — z zasady — zapi- sywane w postaci czterocyfrowych liczb ósemkowych, gdzie 0400 oznacza pozwolenie odczytu, 0200 to pozwolenie zapisu i 0100 umoĝ- liwienie uruchomienia pliku. Jest tak dla kaĝdej grupy. W celu okre- Ălenia praw dostÚpu sumuje siÚ poszczególne wartoĂci.4 Najbardziej prawdopodobne uprawnienie to 0755. Zadanie polega na napisaniu programu zmieniajÈcego uprawnienia pliku. Uprawnienia powinny byÊ podane w postaci ósemkowej. Aby to zrobiÊ, naleĝy uĝyÊ chmod(nazwa pliku, uprawnienia);. m W I C Z E N I E 3.4 Swobodny dostÚp do danych Zakïada siÚ, ĝe istnieje plik tekstowy, w którym wszystkie linie majÈ takÈ samÈ dïugoĂÊ (w naszym przypadku jest 11 znaków, ïÈcznie ze znakiem koñca linii). UĝywajÈc funkcji fseek(), naleĝy napisaÊ pro- gram wyĂwietlajÈcy zawartoĂÊ linii o podanym numerze. Na poczÈtek 3 http://pl.wikibooks.org/wiki/C/Czytanie_i_pisanie_do_plik C3 B3w 4 Haviland K.: Unix — Programowanie systemowe, Wydawnictwo RM, Warszawa1999. 132 Programowanie w Linuksie • mwiczenia trzeba utworzyÊ plik tekstowy o nazwie lista. Moĝna posïuĝyÊ siÚ np. poleceniem cat lista lub teĝ dowolnym edytorem tekstowym. Naleĝy pamiÚtaÊ o staïej liczbie znaków w linii. Listing 3.4.1. Co jest w linii? 1 #include stdio.h 2 #include stdlib.h 3 #define DLUGOSC 11 Definicja staïej okreĂlajÈcej dïugoĂÊ linii. 4 char linia[DLUGOSC]; Definicja zmiennej globalnej okreĂlajÈcej bufor, do którego bÚdÈ wpi- sywane dane. 5 int main() 6 { 7 FILE * plik; // deskryptor pliku 8 int pozycja; // pozycja wskaĨnika 9 int linie = 5; // linia, której zawartoĞü mamy wyĞwietliü 10 long offset; 11 plik = fopen ./lista , rt ); //otwieramy plik 12 if(plik == NULL) 13 { 14 printf Nie ma pliku ); 15 } 16 offset = (linie -1) * DLUGOSC; Linia 16 okreĂla pozycjÚ, w której naleĝy ustawiÊ „kursor”. 17 pozycja = fseek(plik, offset, SEEK_SET); Funkcja fseek() zmienia wartoĂÊ pozycji wskaěnika w pliku zwiÈzanym ze strumieniem (okreĂlonym przez pierwszy parametr) o liczbÚ bajtów (okreĂlonÈ przez drugi parametr) od wskazanej pozycji (ostatni para- metr). UdostÚpnia trzy wartoĂci sterujÈce poruszaniem siÚ po pliku: SEEK_SET (poczÈtek pliku), SEEK_CUR (pozycja aktualna) i SEEK_END (koniec pliku). WartoĂÊ zwracana to 0 w przypadku powodzenia lub -1, gdy wystÈpi bïÈd. 18 if(pozycja==-1) 19 { 20 printf Nie moge ustawic pozycji ); 21 } 22 else 23 { 24 fgets(linia, DLUGOSC, plik); Rozdziaï 3. • Zastosowania wybranych Ărodowisk programistycznych 133 Za pomocÈ funkcji fgets() moĝna odczytaÊ liniÚ o dïugoĂci wskazanej przez drugi parametr z pliku lista (trzeci parametr). Wynik jest zapi- sywany do bufora (pierwszy parametr) i nastÚpnie wartoĂÊ jest zwra- cana. Funkcja czyta do momentu odczytania DLUGOSC – 1 znaków lub do napotkania znaku koñca linii. Do odczytanego ïañcucha dodaje znak koñca wiersza i (koniec ïañcucha). W przypadku niepowo- dzenia zwraca NULL. 25 printf s , linia); 26 } 27 fclose(plik); 28 return 0; 29 } Moĝna teraz przetestowaÊ dziaïanie programu, a nastÚpnie utworzyÊ plik tekstowy z innÈ liczbÈ linii i zobaczyÊ, jak program bÚdzie siÚ zachowywaï. m W I C Z E N I E 3.5 Modyfikacja Êwiczenia 3.4 — zadanie do samodzielnego wykonania Program z Êwiczenia 3.4 ma kilka istotnych wad (np. na sztywno ma wpisany numer linii do wyĂwietlenia, liczba wierszy musi byÊ znana). Wady te naleĝy usunÈÊ. W szczególnoĂci naleĝy napisaÊ funkcjÚ typu void, wyĂwietlajÈcÈ zawartoĂÊ linii o podanym numerze, a takĝe — w sekcji main — dopisaÊ kod, który bÚdzie wyĂwietlaï zadany przez operatora numer linii z pliku. m W I C Z E N I E 3.6 Wyszukiwanie wzoru — zadanie do samodzielnego wykonania Naleĝy napisaÊ program wyszukujÈcy okreĂlonÈ sekwencjÚ znaków oraz zliczajÈcy liczbÚ jej wystÈpieñ. Wynik (szukanÈ sekwencjÚ zna- ków oraz liczbÚ jej wystÈpieñ) naleĝy zapisaÊ w pliku wyjĂciowym. Zakïada siÚ, ĝe plik z tekstem zostaï wczeĂniej przygotowany, a szukana sekwencja znaków bÚdzie zadawana z klawiatury terminalu. 134 Programowanie w Linuksie • mwiczenia Naleĝy wykorzystaÊ algorytm K-M-P.5 m W I C Z E N I E 3.7 Program klient-serwer Naleĝy napisaÊ oprogramowanie, które bÚdzie wyliczaïo sumÚ dwóch liczb. Caïy proces ma byÊ realizowany przez dwa programy pracujÈce w trybie klient-serwer. Zadaniem serwera bÚdzie odbieranie od klienta pary liczb, wyliczanie ich sumy i odsyïanie wyniku. Do komunikacji klienta z serwerem naleĝy uĝyÊ mechanizmu gniazd oraz protokoïu TCP. W drugim kroku program serwera naleĝy zmodyfikowaÊ (wyko- rzystujÈc funkcjÚ fork()), tak aby mógï obsïuĝyÊ kilka klientów. Praca serwera to wykonywanie prostej sekwencji dziaïañ: 1. start, 2. utworzenie gniazda, 3. bindowanie gniazda i portu, 4. ustawienie gniazda w tryb nasïuchiwania, 5. zaakceptowanie poïÈczenia, 6. wymiana komunikatów z klientem (pÚtla, w której pobierane sÈ dane od klienta, generowana suma oraz odsyïany wynik sumowania do klienta), 7. zamkniÚcie poïÈczenia i gniazda. Dziaïania klienta sprowadzaÊ siÚ bÚdÈ do: 1. utworzenia gniazda, 2. nawiÈzania poïÈczenia z serwerem, 3. odebrania wiadomoĂci od serwera, 4. wysïania danych, 5. odebrania wyniku, 6. zakoñczenia poïÈczenia. Zgodnie z poleceniem podanym w Êwiczeniu naleĝy uĝyÊ gniazd oraz protokoïu TCP. Naleĝy wspomnieÊ, ĝe gniazda sieciowe sÈ doĂÊ 5 Algorytm K-M-P (ang. Knutha-Morrisa-Pratta) wyszukiwania wzorca wykorzystujÈcy tablicÚ. Rozdziaï 3. • Zastosowania wybranych Ărodowisk programistycznych 135 uniwersalnym mechanizmem komunikacji miÚdzy procesami. Ich implementacja w systemie Linux jest wzorowana na kodzie pochodzÈ- cym z systemu BSD-Unix (ang. Berkeley System Distribution). Komu- nikacja moĝe siÚ odbyÊ tylko wtedy, gdy oba procesy utworzÈ gniazda (kaĝdy po swojej stronie). W programie wykorzystano funkcje socket(), bind(), listen(), accept(), send(), recv() i close(). Funkcje te wymagajÈ bibliotek sys/types.h i sys/socket.h . Teraz gdy zostaï przedstawiony zarys programu oraz zdefiniowano, jakich narzÚdzi naleĝy uĝyÊ, wystarczy uruchomiÊ Code::Blocks IDE i jako pierwszy przygotowaÊ nowy projekt (kod dla serwera). Listing 3.7.1. Kod serwera 1 #include stdio.h 2 #include stdlib.h 3 #include string.h 4 #include netinet/in.h 5 #include sys/socket.h 6 #include sys/types.h Powyĝsze linijki zawierajÈ definicje struktur, a takĝe podstawowych funkcji systemowych dla gniazd. netinet/in.h zawiera definicje struktur in_addr i sockaddr_in dla rodziny protokoïów internetowych. W sys/socket.h znajduje siÚ struktura sockaddr. ZaĂ sys/types.h zawiera makrodefinicje oraz definicje typów danych (np. u_short — unsigned short). Z funkcji do obsïugi gniazd moĝna korzystaÊ po doda- niu sys/types.h i sys/socket.h . 7 #define MSG_LEN 512 8 #define PORTNUM 60000 Powyĝsze linie prezentujÈ definicje maksymalnej dïugoĂci wiadomo- Ăci i numeru portu. Teoretycznie numer portu to liczba z zakresu od 0 do 65 535, ale w praktyce do naszych celów nie moĝemy uĝyÊ tzw. portów dobrze znanych (przedziaï od 0 do 1023) oraz lepiej nie uĝy- waÊ „portów zarejestrowanych” (przedziaï od 1024 do 49 151). Po pro- stu te porty sÈ (odpowiednio) zastrzeĝone lub zarezerwowane przez IANA (ang. Internet Assigned Numbers Authority) dla uĝywanych pow- szechnie usïug.6 9 int main(void) { 10 int s, result, cs = 0; 6 Numery portów: http://www.iana.org/assignments/port-numbers. 136 Programowanie w Linuksie • mwiczenia 11 int a,b, wynik; 12 char msg[MSG_LEN]; 13 struct sockaddr_in laddr; 14 socklen_t laddr_len; 15 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //tworzymy gniazdo 16 if (s 0) {//obsáuga báĊdu 17 perror( socket ); 18 return 1; 19 } Za pomocÈ socket() tworzone jest gniazdo. Funkcja ta ma trzy para- metry. Pierwszy z nich okreĂla rodzinÚ — w tym przypadku jest to staïa AF_INET oznaczajÈca protokoïy Internetu (moĝna spotkaÊ takĝe nazwÚ PF_INET). Przedrostek AF_ oznacza rodzinÚ adresów, a PF_ rodzinÚ protokoïów. Obie notacje moĝna stosowaÊ zamiennie (sÈ równowaĝne). Drugi argument funkcji socket() okreĂla rodzaj gniazda (SOCK_STREAM — gniazdo strumieniowe). Ostatni parametr wskazuje protokóï. Zgod- nie z treĂciÈ zadania naleĝy zastosowaÊ protokóï TCP, wiÚc natural- nym bÚdzie podanie staïej IPPROTO_TCP. Funkcja socket() zwraca liczbÚ caïkowitÈ, którÈ nazywa siÚ deskryptorem gniazda. 20 memset( laddr, 0, sizeof(struct sockaddr)); //zerowanie struktury laddr 21 laddr.sin_family = AF_INET; //protokoáy Internetu 22 laddr.sin_port = htons(PORTNUM); //serwer bĊdzie nasáuchiwaü na porcie 23 laddr.sin_addr.s_addr = htonl(INADDR_ANY);//kaĪdy klient moĪe siĊ poáączyü Linijki 20 – 23 powodujÈ dowiÈzanie adresu lokalnego. DziÚki temu klient bÚdzie mógï wysyïaÊ dane do serwera. 24 result = bind(s, (struct sockaddr*) laddr, sizeof(struct ´sockaddr));//bindujemy port z gniazdem 25 if (result 0) {//obsáuga báĊdu 26 close(s); 27 perror( bind ); 28 return 1; 29 } Po utworzeniu fizycznego gniazda naleĝy powiÈzaÊ je z nazwÈ, tak aby inne procesy mogïy siÚ do niego ïatwo odwoïaÊ. Sïuĝy do tego funkcja bind(). W jej wywoïaniu trzeba podaÊ trzy parametry. Pierwszy okreĂla deskryptor gniazda, drugi to wskaěnik do struktury zawierajÈ- cej adres, a ostatni podaje rozmiar struktury. 30 result = listen(s, 1);//ustawienie gniazda w tryb nasáuchiwania 31 if (result 0) {//obsáuga báĊdu 32 close(s); 33 perror( listen ); 34 return 1; 35 } Rozdziaï 3. • Zastosowania wybranych Ărodowisk programistycznych 137 Kolejnym krokiem jest wskazanie, ĝe serwer „chce przyjmowaÊ poïÈ- czenia — okreĂlonÈ liczbÚ poïÈczeñ”. Sïuĝy do tego funkcja listen() wymagajÈca podania dwu parametrów. Pierwszym jest deskryptor gniazda, a drugi okreĂla maksymalnÈ liczbÚ poïÈczeñ (w naszym przypadku jest to jedno poïÈczenie), które system umieszcza w kolejce w oczekiwaniu na wykonanie funkcji accept(). 36 laddr_len = sizeof(struct sockaddr); 37 cs = accept(s, (struct sockaddr*) laddr, laddr_len); //akceptowanie poáączenia 38 if (cs 0){//obsáuga báĊdu 39 perror( accept ); 40 return 1; 41 } Funkcja systemowa accept() wywoïywana jest po listen(). Pobiera ona pierwsze ĝÈdanie poïÈczenia z kolejki i tworzy nowe gniazdo o takich samych wïaĂciwoĂciach, jakie ma jej pierwszy argument (des- kryptor gniazda). Dwa pozostaïe adresy okreĂlajÈ klienta, którego doty- czy poïÈczenie. Po wywoïaniu accept() wÈtek serwera jest blokowany i czeka na poïÈczenie. 42 while(cs){ 43 memset(msg, 0, MSG_LEN); //wyzerowanie bufora wiadomoĞci 44 printf( Polaczenie zaakceptowane. ); 45 strncpy(msg, Podaj liczby: , MSG_LEN); 46 result = send(cs, msg, strlen(msg), 0);//wyĞlij tekst do klienta 47 if (result = 0) {//obsáuga báĊdu 48 close(cs); //zamkniĊcie gniazda 49 perror( send ); 50 return 1; 51 } KolejnÈ funkcjÈ umoĝliwiajÈcÈ komunikacjÚ jest funkcja send() sïu- ĝÈca do wysyïania danych. Wymaga podania czterech parametrów: deskryptora gniazda, wiadomoĂci w postaci ciÈgu znaków, dïugoĂci wiadomoĂci w bajtach oraz flagi (u nas nie korzysta siÚ z ĝadnej, wiÚc naleĝy podaÊ 0). W takiej postaci funkcja send() jest funkcjÈ blokujÈcÈ. Zostanie odblokowana wtedy, gdy wszystkie dane zostanÈ wysïane. TakÈ samÈ listÚ parametrów przyjmuje funkcja recv(), która odbiera dane. 52 memset(msg, 0, MSG_LEN); //wyzerowanie bufora wiadomoĞci 53 result = recv(cs, msg, MSG_LEN, 0);//oczekiwanie na odpowiedĨ klienta 54 if (result = 0) {//obsáuga báĊdu 55 close(cs); 56 perror( recv ); 138 Programowanie w Linuksie • mwiczenia 57 return 1; 58 } 59 msg[result-2]= ; 60 sscanf(msg, d d , a, b); //odczytanie liczb 61 wynik=a+b; // wykonanie dziaáania 62 printf( wynik d , wynik); //wydrukowanie wyniku na serwerze 63 sprintf(msg, Wynik = d ,wynik); // wpisanie wyniku do zmiennej msg 64 result = send(cs, msg, strlen(msg)+1, 0); //wyĞlij tekst do klienta 65 if (result 0) {//obsáuga báĊdu 66 close(cs); 67 perror( send ); 68 return 1; 69 } 70 } //koniec pĊtli 71 close(cs); //zamkniĊcie poáączenia 72 close(s); //zamkniĊcie gniazda 73 return 0; 74 } Po napisaniu kodu programu i wykreowaniu projektu naleĝy urucho- miÊ serwer. W efekcie powinno ukazaÊ siÚ puste okienko. Kolejnym krokiem bÚdzie przetestowanie serwera. NajproĂciej wykorzystaÊ do tego celu telnet, jako parametry podajÈc nazwÚ hosta i port, z którym ma zostaÊ nawiÈzane poïÈczenie. Po uruchomieniu terminalu i poja- wieniu siÚ znaku zachÚty naleĝy wpisaÊ: telnet localhost 60000. Gdy wszystko zadziaïa prawidïowo, w oknie terminalu powinna ukazaÊ siÚ wiadomoĂÊ od serwera. Rysunek 3.2 przedstawia wynik dziaïania omawianego testu. Rysunek 3.2. Wynik testowania serwera
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Programowanie w Linuksie. Ćwiczenia
Autor:
, ,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: