C/C++ - Sockety i TCP/UDP pod Linuxem i Windowsem (by Gynvael Coldwind, 07.08.06) @ wyklady.net
Thx dla MeMeK'a, jacekowskiego i Minias'a za aktywny udzial w wykladzie ;>

<@Gynvael> Witam wszystkich zebranych ;>
<@Gynvael> Nazywam sie Gynvael Coldwind, jestem pracownikiem jednej z polskich firm antywirusowych, oraz studentem ostatniego roku informatyki inzynierskiej na Politechnice Wroclawskiej.
<@Gynvael> I mam zaszczyt prowadzic swoj 21 wyklad, ktory bedzie o programowaniu TCP i UDP w systemach Windows oraz Linux ;>
<@Gynvael> Wyklad chcial bym zadedykowac mojej ukochanej Arashi ;* Tak po prostu ;>
<@Gynvael> Wyklad bedzie mozna komentowac (jak sie skonczy) na naszym forum - http://forum.wyklady.net/index.php?topic=69.0
<@Gynvael> OK..
<@Gynvael> Co bedzie potrzebne ?
<@Gynvael> 1) komputer z systemem 'nixowym badz ms windows ;>
<@Gynvael> 2) kompilator, najlepiej z rodziny gcc (MinGW pod win32 (mingw.org), badz defaultowy gcc pod 'nixy)
<@Gynvael> 3) przyda sie rowniez szwajcarski scyzoryk - netcat, oraz program netstat (kazdy powinien go miec, jest domyslnie z systemem instalowany (zarowno linux jak i windows))
<@Gynvael> o NetCat'cie poczytac mozna np w artykule unknow'a
<@Gynvael> http://uw-team.org/index.php?id=arty/netcat
<@Gynvael> sciagnac go mozna ze strony:
<@Gynvael> http://www.vulnwatch.org/netcat/
<@Gynvael> (zarowno pod win32 jak i 'nixy)
<@Gynvael> lub jego wersje GNU ze strony http://netcat.sourceforge.net/download.php
<@Gynvael> o netstat'cie poczytac mozna w MANie
<@Gynvael> http://www.linuxmanpages.com/man8/netstat.8.php
<@Gynvael> lub MSDN'ie: http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/netstat.mspx?mfr=true
<@Gynvael> Ew polecic jeszcze moge, do kombinowania we wlasnym zakresie programy tcpdump (lub windump) oraz http://www.sysinternals.com/Utilities/TcpView.html
<@Gynvael> to ostatnie to taki ladny okienkowy programik pokazujacy co sie z czym laczy ;>
<@Gynvael> dodatkowo informacji mozna szukac na google
<@Gynvael> http://www.google.com/linux
<@Gynvael> http://www.google.com/microsoft
<@Gynvael> OK. Teraz w skrocie plan wykladu
<@Gynvael> pierwsza czesc bedzie poswiecona tworzeniu socketow TCP, i laczeniu sie nimi, oraz wyjasnieniu co sie dzieje w kodzie
<@Gynvael> potem bedzie przyklad serwera w TCP, tj socketow nasluchujacych
<@Gynvael> a potem chwile o UDP, czyli bezpolaczeniowym przesylaniu danych
<@Gynvael> na koniec wspomne o protokole SNMP pod windowsem, na przykladzie wlasnego netstata
<@Gynvael> Wszelkie pytania prosze NA PRIV ;>
<@Gynvael> Co ciekawsze (tj takie na ktore umiem odpowiedziec ;p) bede przeklejal na kanal i udzielal odpowiedzi ;>
<@Gynvael> w miare mozliwosci ofc ;>
<@Gynvael> <Gormack> Dlugo jeszcze ??? :]
<@Gynvael> tak ;>
<@Gynvael> hehehe ;>
<@Gynvael> ok
<@Gynvael> Czym jest SOCKET, po co to i do czego nam ?
<@Gynvael> Socket z eng. znaczy 'gniazdo'
<@Gynvael> Takie gniazdo jak np mamy gniazdko od pradu czy od sluchawek ;> A nie takie od ptaszkoof

<@Gynvael> Wezmy taka przykladowa siec lokalna
<@Gynvael> Sa 3 komputery
<@Gynvael> KOMP A, KOMP B i KOMP C
<@Gynvael> kazdy komputer ma swoj Adres IP
<@Gynvael> Nie bede sie zaglebial w to 'co to jest adres IP' etc. Byl wyklad o tym jesli mnie pamiec nie myli
<@Gynvael> logi/sieci1.txt
<@Gynvael> komputery maja swoje nazwy
<@Gynvael> mylaptop.lan
<@Gynvael> myserver.lan
<@Gynvael> etc etc
<@Gynvael> Dodatkowo KOMP C ma otwarte pare portow
<@Gynvael> sa to TCP 22, TCP 80 i UDP 53
<@Gynvael> dla programisty, otwarty port oznacza tyle
<@Gynvael> ze jest utworzone gniazdo (socket)
<@Gynvael> ktore nasluchuje na danym porcie
<@Gynvael> Zalozmy ze KOMP A chce sciagnac jakas strone z KOMP C (zauwazmy ze port 80, czyli HTTP uzywane przez WWW jest otwarty)
<@Gynvael> w tym celu tworzy nowe gniazdo

<@Gynvael> sluzy do tego komenda socket() (bedzie o niej i innych za chwile w przykladach)
<@Gynvael> nastepnie za pomoca polecenia connect() stara sie uzyskac polaczenie z portem 80 na KOMP C

<@Gynvael> Obrazowo mowiac, mozna powiedziec ze kabelkiem laczymy dwa gniazdka ;>

<@Gynvael> na KOMP C pracuje funkcja accept(), ktora, w momencie gdy ktos sie chce polaczyc, tworzy nowe gniazdo dla tej osoby
<@Gynvael> w tym momencie mamy utworzone polaczenie TCP
<@Gynvael> z komputera o IP 192.168.0.2 i portu zrodlowego 3343 (czemu akurat taki ? bedzie o tym pozniej)
<@Gynvael> do komputera o IP 192.168.0.1 i portu 80
<@Gynvael> Standardowo w protokole HTTP transmisja danych wyglada nastepujaco:
<@Gynvael> - klient wysyla zadanie
<@Gynvael> - a server odsyla odpowiedz

<@Gynvael> Zadanie wysylane jest za pomoca funkcji send()
<@Gynvael> a odbierane po stronie servera za pomoca funkcji recv()
<@Gynvael> server sobie tam mieli zadanie, po czym odsyla jakas www

<@Gynvael> role sie zamienily, laptop odbiera (recv) a server wysyla (send)
<@Gynvael> Gdy wszystkie dane zostana juz odebrane

<@Gynvael> klient zamyka socket za pomoca close() (badz closesocket() pod win32)
<@Gynvael> Tak w skrocie wyglada przebieg pobierania strony za pomoca protokolu TCP
<@Gynvael> Wazne rzeczy o ktorych trzeba pamietac korzystajac z TCP:
<@Gynvael> - trzeba znac adres komputera docelowego
<@Gynvael> - trzeba znac port docelowy na ktory sie laczymy
<@Gynvael> - trzeba nawiazac polaczenie zanim zaczniemy wymieniac dane
<@Gynvael> OK. A teraz troche kodu.
<@Gynvael> Zacznijmy od proby polaczenia sie z jakims adresem IP / portem, i rozlaczenie odrazu
<@Gynvael> sock/sock1.c
<@Gynvael> Co program robi:
<@Gynvael> 0) w przypadku windows'a uruchamia obsluge socketow
<@Gynvael> 1) tworzy socket
<@Gynvael> 2) kaze mu sie polaczyc na jakis tam adres (podany w argumencie programu)
<@Gynvael> 3) rozlacza sie (niszczac socket)
<@Gynvael> 4) pod windowsem zamyka obsluge socketow
<@Gynvael> skompilujmy
<@Gynvael> * win32:
<@Gynvael> * gcc sock1.c -lws2_32 -o sock1.exe
<@Gynvael> * sock1.exe 127.0.0.1 1234
<@Gynvael> * linux:
<@Gynvael> * gcc sock1.c -o sock1
<@Gynvael> * ./sock1 127.0.0.1 1234
<@Gynvael> tak wyglada kompilacja pod danymi systemami
<@Gynvael> Jak to przetestowac ? W tym momencie wchodzi do gry netcat
<@Gynvael> netcat -p 1234 -v
<@Gynvael> zle
<@Gynvael> netcat -l -p 1234 -v
<@Gynvael> 20:06:31 >netcat -l -p 1234 -v
<@Gynvael> listening on [any] 1234 ...
<@Gynvael> odpalenie tego polecenia spowodowalo utworzenie nasluchujacego socketa na porcie 1234
<@Gynvael> opcja -v powoduje wypisanie informacji o tym ze sie ktos podlaczyl
<@Gynvael> kompilujemy wiec sock1.c i odpalamy z parametrem 127.0.0.1 1234
<@Gynvael> 20:27:07 >gcc sock1.c -lws2_32 -o sock1.exe
<@Gynvael> 20:27:20 >sock1.exe 127.0.0.1 1234
<@Gynvael> Creating socket...
<@Gynvael> Connecting to 127.0.0.1:1234...
<@Gynvael> Connected!!!
<@Gynvael> 20:27:25 >
<%dragon> ale jestem wqr...
<@Gynvael> natomiast na drugiej konsoli (tej z netcatem) pojawilo sie
<@Gynvael> dragon csii wyklad ;*
<@Gynvael> DNS fwd/rev mismatch: localhost != yume
<@Gynvael> connect to [127.0.0.1] from localhost [127.0.0.1] 3040
<@Gynvael> czyli netcat otrzymal polaczenie od komputera 127.0.0.1 (to ofc adres zwrotny, czyli nasz komp ;>) z portu zrodlowego 3040
<@Gynvael> czemu port zrodlowy to akurat 3040 ?
<@Gynvael> a nie np 1234 ?
<@Gynvael> albo 12736 ?
<@Gynvael> poniewaz, w momencie gdy nie wybieramy portu zrodlowego, tj nie podczepiamy socketa zrodlowego pod konkretny port/ip, to system wybiera losowy port
<@Gynvael> w sumie nie do konca losowy, poniewaz sa to kolejne porty po prostu
<@Gynvael> <jacekowski> nowe kernele 2.6 wybieraja porty losowo
<@Gynvael> ;>
<@Gynvael> obra
<@Gynvael> dobra
<@Gynvael> widzimy ze program dziala
<@Gynvael> przeanalizujmy kod zrodlowy
<@Gynvael> najpierw jest sporo includow
<@Gynvael> dla 'nixow sa to
<@Gynvael> #include <netinet/in.h>
<@Gynvael> #include <netdb.h>
<@Gynvael> #include <arpa/inet.h>
<@Gynvael> #include <unistd.h>
<@Gynvael> #include <sys/socket.h>
<@Gynvael> dla windy
<@Gynvael> #include <windows.h>
<@Gynvael> #include <winsock.h>
<@Gynvael> oraz sa trzy wspolne
<@Gynvael> #include <stdio.h>
<@Gynvael> #include <stdlib.h>
<@Gynvael> #include <string.h>
<@Gynvael> czemu akurat tyle pod linuxem includow ? (potem jeszcze jeden dojdzie)
<@Gynvael> funkcje po prostu sa porozrzucane po roznych headerach.. ale to w niczym nei przeszkadza
<@Gynvael> jest tak jeszcze jedno makro zdefiniowane
<@Gynvael> #define closesocket(a) close(a)
<@Gynvael> pod linuxami do zamykania socketow sluzy funkcja close()
<@Gynvael> natomiast pod winda closesocket()
<@Gynvael> to makro jest po to zeby odrobine latwiej sie pisalo przenosny kod ;>
<@Gynvael> dalej mamy funkcje main()
<@Gynvael> a w niej zdefiniowane pare zmiennych
<@Gynvael> unsigned long ip;
<@Gynvael> unsigned short port;
<@Gynvael> struct sockaddr_in sock_info;
<@Gynvael> int ret, sock;
<@Gynvael> zaznacze tutaj ze (szczegolnie pod winda) do socketow jest uzywany typ SOCKET
<@Gynvael> SOCKET to tak na prawde int - deskryptor dla danego procesu
<@Gynvael> wiec bede to nazywal po imieniu, czyli int
<@Gynvael> *deskryptor socketu dla danego procesu
<@Gynvael> int sock <=- nasz utworzony socket do tej zmiennej zostanie zapamietany
<@Gynvael> o struct sockaddr_in za chwile ;>
<@Gynvael> pod windowsem dodatkow jest jeszcze jedna zmienna
<@Gynvael> #ifndef __unix__
<@Gynvael> WSADATA wsdat;
<@Gynvael> #endif
<@Gynvael> jest to struktura (o ktorej wiecej za chwile)
<@Gynvael> w ktora WinSock (systemowy lib od socketow pod windowsami) wrzuca informacje o sobie w momecie inicjalizacji
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/wsadata_2.asp
<@Gynvael> tam mozna o niej poczytac
<@Gynvael> Program spodziewa sie 2ch parametrow - ip i portu
<@Gynvael> if(argc < 3) return 1;
<@Gynvael> ip = inet_addr(argv[1]);
<@Gynvael> sscanf(argv[2], "%hu", &amp;port);
<@Gynvael> to zalatwia sprawe parsingu argumentow (nie ma tutaj kontroli bledow, chcialem zeby kod byl wmiare czysty)
<@Gynvael> funkcja inet_addr sluzy do tlumaczenia adresu ip zapisanego w stringu, np "127.0.0.1" do postaci binarnej
<@Gynvael> czyli do unsigned int
<@Gynvael> funkcja dziala w prosty sposob
<@Gynvael> zalozmy na chwile ze unsigned int to 4ry oddzielne bajty
<@Gynvael> unsigned char bajt[4];
<@Gynvael> mamy ip w postaci 127.0.0.1, sa to cztery liczby
<@Gynvael> 127 0 0 1
<@Gynvael> tlumaczymy je ze stringa na int, i wrzucamy w poszczegolne bajty
<@Gynvael> bajt[0] = 127;
<@Gynvael> bajt[1] = bajt[2] = 0;
<@Gynvael> bajt[3] = 1;
<@Gynvael> dzieki temu uzyskujemy cale ip w jednej zmiennej unsigned int (ofc dla IPv6 to nie przejdzie ;p)
<@Gynvael> wynikowe IP w hexa bedzie W PAMIECI wygladac tak: 7f 00 00 01
<@Gynvael> i teraz wazna sprawa jesli chodzi o komunikacje
<@Gynvael> ADRES (ip, port) jest przyjmowany przez funkcje zapisany BIG ENDIAN (czyli 'bajtami od tylu')
<@Gynvael> do konwersji naszej (intelowskiej - malej) endiany na big endian (network safe endian) sluza funkcje
<@Gynvael> htonl (do konwertji unsigned int)
<@Gynvael> i htons (do konsersji unsigned short)
<@Gynvael> ta ostatnia za chwile w kodzie sie pojawi
<@Gynvael> a
<@Gynvael> o inet_addr mozna poczytac:
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/inet_addr_2.asp
<@Gynvael> http://www.linuxmanpages.com/man3/inet_addr.3.php
<@Gynvael> pierwszy to link to MSDN, drugi do MANa
<@Gynvael> o htonl i htons mozna poczytac:
<@Gynvael> htonl
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/htonl_2.asp
<@Gynvael> http://www.linuxmanpages.com/man3/htonl.3.php
<@Gynvael> htons
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/htons_2.asp
<@Gynvael> http://www.linuxmanpages.com/man3/htons.3.php
<@Gynvael> po odczycie argumentow mamy kawalek kodu pod winde
<@Gynvael> #ifndef __unix__
<@Gynvael> memset(&amp;wsdat,0,sizeof(wsdat));
<@Gynvael> WSAStartup(0x0101,&amp;wsdat);
<@Gynvael> #endif
<@Gynvael> WSAStarup powoduje inicjalizache winsocka pod winda
<@Gynvael> bez tego nie bedzie mozna nic na socketach robic
<@Gynvael> gdybysmy probowali, dostaniemy od systemu error
<@Gynvael> Error: Either the application has not called WSAStartup, or WSAStartup failed.
<@Gynvael> (o tym jak uzyskac errory w stringach, za chwile)
<@Gynvael> o WSAStartup mozna poczytac:
<@Gynvael> WSAStartup
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/wsastartup_2.asp
<@Gynvael> nastepnie jest kod tworzacy socket
<@Gynvael> puts("Creating socket...");
<@Gynvael> sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
<@Gynvael> funckja przyjmuje 3 parametry - rodzine, rodzaj i protokol
<@Gynvael> w przypadku TCP jest to AF_INET, SOCK_STREAM (udp by mialo SOCK_DATAGRAM) i IPPROTO_TCP (udp by mialo IPPROTO_UDP)
<@Gynvael> funkcja socket zwraca -1 w przypadku bledu (sa na to -1 makra btw, SOCKET_ERROR i INVALID_SOCKET pod winda)
<@Gynvael> lub handle socketu
<@Gynvael> w tym przykladzie jak wspomnialem nie ma obslugi bledow
<@Gynvael> wiec nie sprawdzamy czy -1 zostalo zwrocone
<@Gynvael> o funkcji socket() mozna poczytac
<@Gynvael> socket
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/socket_2.asp
<@Gynvael> http://www.linuxmanpages.com/man2/socket.2.php
<@Gynvael> o tam ;>
<@Gynvael> printf("Connecting to %s:%u...\n", inet_ntoa(*(struct in_addr*)&amp;ip), port);
<@Gynvael> odwrotnoscia funkcji inet_addr jest inet_ntoa
<@Gynvael> funkcja przyjmuje struct in_addr, ktory tak na prawde jest bardzo zapakowanym unsigned intem ;p
<@Gynvael> wiec 'ip' bedace typu unsigned int mozemy spokojnie zrzutowac na ten tym (struct in_addr) ;>
<@Gynvael> inet_ntoa
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/inet_ntoa_2.asp
<@Gynvael> http://www.linuxmanpages.com/man3/inet_ntoa.3.php
<@Gynvael> tam wyzej napisalem SOCK_DATAGRAM w przypadku UDP...
<@Gynvael> <jacekowski> SOCK_DGRAM*
<@Gynvael> powinno byc SOCK_DGRAM ;>
<@Gynvael> thx jacekowski ;>
<@Gynvael> sock_info.sin_family = AF_INET;
<@Gynvael> sock_info.sin_addr.s_addr = ip;
<@Gynvael> sock_info.sin_port = htons(port);
<@Gynvael> sock_info to struktura typu struct sockaddr_in
<@Gynvael> w niej jest zapisany adres (caly, tj IP + PORT)
<@Gynvael> korzystamy z niej do wskazania funkcja gdzie maja sie laczyc (na jaki port i ip), lub gdzie maja nasluchiwac
<@Gynvael> 3 pola sa w niej najwazaniejsza
<@Gynvael> .sin_family - rodzina protokolu - AF_INET, jak w przypadku socket()
<@Gynvael> .sin_addr.s_addr - tam wrzucamy ip (zakodowane BIG ENDIAN.. funkcja inet_addr ktora uzylismy dba o to, natomiast gdybysmy jej nie uzywali, trzeba by htonl uzyc)
<@Gynvael> .sin_port - tam wrzucamy port (zakodowany BIN ENDIAN, stad to htons() tam jest przy port)
<@Gynvael> jeszcze jest pole
<@Gynvael> .sin_zero
<@Gynvael> ktore trzeba wyzerowac
<@Gynvael> memset(sock_info.sin_zero, 0, sizeof(sock_info.sin_zero));
<@Gynvael> ok
<@Gynvael> Mamy juz strukture, teraz mozemy podac ja funkcji connect() i poprosic ja zeby tam sie polaczyla
<@Gynvael> if(connect(sock, (struct sockaddr*)&amp;sock_info, sizeof( struct sockaddr )) != -1)
<@Gynvael> puts("Connected!!!");
<@Gynvael> funkcja connect przyjmuje 3 parametry
<@Gynvael> 1) handle socketu (to co socket() zwroci), czyli 'sock' w naszym wypadku
<@Gynvael> 2) adres do struktury typu 'struct sockaddr*'... my w przypadku protokolu TCP/IP uzywamy 'struct sockaddr_in', wiec musimy przerzutowac wskaznik
<@Gynvael> struktury sa kompatybilne oczywiscie, po prostu maja inne pola ;>
<@Gynvael> 3) wielkosc struktury ;>
<@Gynvael> funkcja connect zwraca -1 jesli sie nie uda polaczyc
<@Gynvael> czyli jesli funckja sie w tym przypadku polaczy, zostaje wypisany napis 'Connected!!!'
<@Gynvael> connect
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/connect_2.asp
<@Gynvael> http://www.linuxmanpages.com/man2/connect.2.php
<@Gynvael> tam mozna o tej funkcji poczytac
<@Gynvael> closesocket(sock);
<@Gynvael> #ifndef __unix__
<@Gynvael> WSACleanup();
<@Gynvael> #endif
<@Gynvael> potem zamykamy socket
<@Gynvael> a pod winda dodatkowo wywolujemy WSACleanup
<@Gynvael> ktore deinicjalizuje winsocka
<@Gynvael> dodam tutaj ze w przypadku gdybysmy WSAStartup wywolali np 3 razy w roznych modulach
<@Gynvael> to WSACleanup rowniez trzeba 3 razy wywolac
<@Gynvael> poniewaz dwa pierwsze nic nie zrobia (wewnatrzny licznik zmniejsza)
<@Gynvael> dopiero trzecia wyczysci stuff po uzyciu ;>
<@Gynvael> dzieki temu nie musimy sie przejmowac tym czy inne liby ktore uzywamy w sofcie tego nie wlaczaja i czy czasem nam nie wylacza etc
<@Gynvael> ;>
<@Gynvael> WSACleanup
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/wsacleanup_2.asp
<@Gynvael> tam mozna poczytac o tej funkcji
<@Gynvael> co do closesocket, rozumiem ze pamietacie ze pod 'nixami to jest tylko makro na close ?:>
<@Gynvael> poczytac o obu tych funkcjach mozna:
<@Gynvael> closesocket
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/closesocket_2.asp
<@Gynvael> close
<@Gynvael> http://www.linuxmanpages.com/man2/close.2.php
<@Gynvael> o tam
<@Gynvael> tyle jesli chodzi o pierwszy przyklad ;>
<@Gynvael> dodajmy do tego obsluge bledow i jakies debug-messages
<@Gynvael> sock/sock2.c
<@Gynvael> to jest praktycznie ten sam kod, tylko ze dodana jest funkcja
<@Gynvael> void my_perror(void);
<@Gynvael> oraz obsluga bledow
<@Gynvael> zanim jeszcze zaczne kod omawiac
<@Gynvael> wroce do funkcji
<@Gynvael> WSAStartup
<@Gynvael> jak wspomnialem ustawia ona strukture WSADATA, wrzucajac tam informacje o winsocku
<@Gynvael> gdybysmy po WSAStartup gdzies wrzucili nastepujacy kod:
<@Gynvael> printf("Ver called : %x.%x\n"
<@Gynvael> "Ver supported: %x.%x\n"
<@Gynvael> "Desc : %s\n"
<@Gynvael> "SysStatus : %s\n",
<@Gynvael> wsdat.wVersion >> 8, wsdat.wVersion &amp; 0xff,
<@Gynvael> wsdat.wHighVersion >> 8, wsdat.wHighVersion &amp; 0xff,
<@Gynvael> wsdat.szDescription, wsdat.szSystemStatus);
<@Gynvael> to bysmy dostali pare dodatkowych informacji
<@Gynvael> Ver called : 1.1
<@Gynvael> Ver supported: 2.2
<@Gynvael> Desc : WinSock 2.0
<@Gynvael> SysStatus : Running
<@Gynvael> pierwsze to jest wersja winsocka jaka nam do szczescia wystarcza... na potrzeby tego wykladu 1.1 wystarcza w zupelnosci
<@Gynvael> skad system wie ze chcielismy 1.1 ?
<@Gynvael> WSAStartup(0x0101,&amp;wsdat);
<@Gynvael> 0x0101 == 01 01 == 1.1 ;>
<@Gynvael> Ver supported (pole .wHighVersion) to jest maxymalna wersja winsocka obslugiwana przez dana DLLke
<@Gynvael> Desc (.szDescription) to jest opis ;> WinSock 2.0.. odkrywcze ;>
<@Gynvael> a ostatnie pole (.szSystemStatus) to jeszcze bardziej odkrywcze pole dot statusu systemu (winsocka)
<@Gynvael> ok
<@Gynvael> wrocmy do drugiego przykladu
<@Gynvael> void my_perror(void);
<@Gynvael> zalozmy ze wykrylismy blad
<@Gynvael> i chcemy wyswietlic co sie stalo uzytkownikowi
<@Gynvael> pod linuxem blad zostaje zapisany do zmiennej errno
<@Gynvael> funkcja perror sluzy do wypisania opisu bledu
<@Gynvael> pod winda tez jest funkcja perror, ale na nic sie nie przyda w przypadku socketow
<@Gynvael> winsock ma swoja niezwykle skomplikowana funkcje
<@Gynvael> WSAGetLastError
<@Gynvael> ktora zwraca numer bledu
<@Gynvael> mialem przyjemnosc jej sie przyjrzec
<@Gynvael> wiec nawet przedstawie wam jej zreversowany kod
<@Gynvael> DWORD WSAGetLastError(void) {
<@Gynvael> return GetLastError();
<@Gynvael> }
<@Gynvael> ;>
<@Gynvael> GetLastError to standardowa windowsowa funkcja zwracajaca numer bledu
<@Gynvael> jak z numeru bledu zrobic string ? sluzy do tego funkcja FormatMessage
<@Gynvael> o w/w funkcja poczytac mozna:
<@Gynvael> FormatMessage
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/formatmessage.asp
<@Gynvael> GetLastError
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/getlasterror.asp
<@Gynvael> perror
<@Gynvael> http://www.linuxmanpages.com/man3/perror.3.php
<@Gynvael> dodatkowo bledy winsocka wymienione sa na
<@Gynvael> WinSock errors
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp
<@Gynvael> wiec
<@Gynvael> funckja wypisujaca co sie stalo pod linuxa wyglada tak:
<@Gynvael> perror("Error");
<@Gynvael> np w przypadku zlego protokolu wypisze to
<@Gynvael> $ ./sock2 127.0.0.1 6667
<@Gynvael> Creating socket...
<@Gynvael> Error: Protocol not supported
<@Gynvael> pod windowsem do wypisania bledu sluzy funkcja
<@Gynvael> char buf[1024];
<@Gynvael> FormatMessage(
<@Gynvael> FORMAT_MESSAGE_FROM_SYSTEM,
<@Gynvael> NULL,
<@Gynvael> GetLastError(),
<@Gynvael> MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
<@Gynvael> buf, sizeof(buf), NULL );
<@Gynvael> printf("Error: %s", buf);
<@Gynvael> i w przypadku analogicznego bledu, uzyskamy na wyjsciu:
<@Gynvael> >sock2.exe 127.0.0.1 6667
<@Gynvael> Creating socket...
<@Gynvael> Error: The requested protocol has not been configured into the system, or no implementation for it exists.
<@Gynvael> porownajcie sobie przyklad sock1.c z sock2.c
<@Gynvael> doszly glownie ify:
<@Gynvael> if(WSAStartup(0x0101,&amp;wsdat) != 0)
<@Gynvael> my_perror();
<@Gynvael> ...
<@Gynvael> if(sock == -1)
<@Gynvael> goto err;
<@Gynvael> ...
<@Gynvael> if(connect(sock, (struct sockaddr*)&amp;sock_info, sizeof( struct sockaddr )) == -1)
<@Gynvael> goto err;
<@Gynvael> ...
<@Gynvael> err:
<@Gynvael> my_perror();
<@Gynvael> #ifndef __unix__
<@Gynvael> if(ws_init) WSACleanup();
<@Gynvael> #endif
<@Gynvael> return 1;
<@Gynvael> OK ;> czas na przyklad numer 3
<@Gynvael> Przyklad numer 3 laczy sie z www.google.pl
<@Gynvael> na port 80 (HTTP)
<@Gynvael> wysyla zadanie:
<@Gynvael> GET / HTTP/1.1\r\n
<@Gynvael> host: www.google.pl\r\n
<@Gynvael> \r\n
<@Gynvael> i wypisuje na stdout wszystko co od google dostanie
<@Gynvael> Skad wiemy co wyslac do google ?
<@Gynvael> o protokole HTTP w wersji 1.1 mowi rfc 2616
<@Gynvael> rfc (request for comments) to w duzym skrocie zbior dokumentacji do wszystkiego
<@Gynvael> tj wszystkich wazniejszy protokolow (POP3, SMTP, HTTP, IRC, etc)
<@Gynvael> i innych rzeczy (implementacja TCP/IP za pomoca golebi pocztowych czy algorytm MD5)
<@Gynvael> HTTP 1.1
<@Gynvael> http://rfc.net/rfc2616.html
<@Gynvael> na rfc.net sa wszystkie (wiekszosc) RFC wydanych
<@Gynvael> taki hint, jesli ktos ma mozliwosc, to sciagnijcie sobie wszystkie na dysk.. warto ;>
<@Gynvael> moj ping mi mowi ze www.google.pl w tej chwili ma ip 66.249.85.104
<@Gynvael> (google ma duuuzo ip ;> wiec u was moze byc inne)
<@Gynvael> <jacekowski> w ktorym rfc to jest implementacja TCP/IP za pomoca golebi pocztowych?
<@Gynvael> http://www.ietf.org/rfc/rfc1149.txt
<@Gynvael> ;>
<@Gynvael> wyglada na to ze to samo IP a nie TCP/IP ;> ale nvm ;> i tak ubaw jest przy czytaniu tego
<@Gynvael> ;>
<@Gynvael> dobra
<@Gynvael> nowe rzeczy ktore doszly:
<@Gynvael> send(sock, "GET / HTTP/1.1\r\nhost: www.google.pl\r\n\r\n", 39, 0);
<@Gynvael> while((ret = recv(sock, data, sizeof(data), 0)) > 0)
<@Gynvael> {
<@Gynvael> fwrite(data, 1, ret, stdout);
<@Gynvael> fflush(stdout);
<@Gynvael> }
<@Gynvael> dwie nowe funkcje
<@Gynvael> send i recv
<@Gynvael> send sluzy do wysylania
<@Gynvael> send(socket, bufor z danymi, ile danych wyslac, flagi)
<@Gynvael> o flagach nic mowil nie bede, poniewaz sa bardzo zalezne od systemu operacyjnego ;>
<@Gynvael> a do szczescia nie sa nam potrzebne
<@Gynvael> oraz funkcja recv()
<@Gynvael> recv przyjmuje socket, bufor na dane, wielkosc bufora, oraz flagi
<@Gynvael> oraz zwraca ilosc odebranych bajtow
<@Gynvael> LUB
<@Gynvael> 0 jesli sie klient rozlaczy
<@Gynvael> LUB 1 w przypadku bledu
<@Gynvael> eee
<@Gynvael> -1 w przypadku bledu
<@Gynvael> ;p
<@Gynvael> jak widac to co otrzymamy recv, odrazu leci na stdout
<@Gynvael> (fwrite dobite fflushem)
<@Gynvael> o obu funkcjach mozna poczytac:
<@Gynvael> recv
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/recv_2.asp
<@Gynvael> http://www.linuxmanpages.com/man2/recv.2.php
<@Gynvael> send
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/send_2.asp
<@Gynvael> http://www.linuxmanpages.com/man2/send.2.php
<@Gynvael> o tam
<@Gynvael> ok.. jak to wyglada w praktyce
<@Gynvael> jak skompilowac jest jak zwykle w kodzie na samej gorze
<@Gynvael> 21:17:41 >gcc sock3.c -lws2_32 -o sock3.exe
<@Gynvael> 21:23:44 >sock3.exe 66.249.85.104 80
<@Gynvael> Creating socket...
<@Gynvael> Connecting to 66.249.85.104:80...
<@Gynvael> Connected!
<@Gynvael> HTTP/1.1 200 OK
<@Gynvael> Cache-Control: private
<@Gynvael> Content-Type: text/html
<@Gynvael> Server: GWS/2.1
<@Gynvael> Transfer-Encoding: chunked
<@Gynvael> Date: Mon, 07 Aug 2006 19:24:03 GMT
<@Gynvael> b58
<@Gynvael> [...]
<@Gynvael> Prosze zauwazyc ze po wyslaniu strony server sie nie rozlaczyl ;>
<@Gynvael> mozemy wiec sprawdzic netstatem czy polaczenie istnieje
<@Gynvael> >netstat -anp tcp | find "66.249.85.104"
<@Gynvael> TCP 192.168.0.99:2626 66.249.85.104:80 ESTABLISHED
<@Gynvael> ja mam NAT, wiec moim zrodlowym IP jest ip w LANie
<@Gynvael> port zrodlowy to w moim przypadku 2626
<@Gynvael> odpalmy program jeszcze raz
<@Gynvael> a potem jeszcze
<@Gynvael> i netstat odpalajmy za kazdym razem
<@Gynvael> bedzie widac jak sie port zrodlowy zmienia
<@Gynvael> >netstat -anp tcp | find "66.249.85.104"
<@Gynvael> TCP 192.168.0.99:2627 66.249.85.104:80 ESTABLISHED
<@Gynvael> >netstat -anp tcp | find "66.249.85.104"
<@Gynvael> TCP 192.168.0.99:2628 66.249.85.104:80 ESTABLISHED
<@Gynvael> >netstat -anp tcp | find "66.249.85.104"
<@Gynvael> TCP 192.168.0.99:2629 66.249.85.104:80 ESTABLISHED
<@Gynvael> 2627..2628..2629
<@Gynvael> na mojej windzie port rosnie o 1 ;>
<@Gynvael> no dobra.. ale my chcemy sobie wybrac port zrodlowy
<@Gynvael> czemy bysmy mieli chceic ?
<@Gynvael> - niektore protokoly (patrz FTP) wymagaja ustalonego portu zrodlowego
<@Gynvael> - bo tak ;>
<@Gynvael> jacekowski podpowiada ze na linuxie powinno byc
<@Gynvael> <jacekowski> powinno byc netstat -anp tcp | grep "66.249.85.104"
<@Gynvael> (to moje dotyczy windy ofc)
<@Gynvael> thx ;>
<@Gynvael> ok
<@Gynvael> do wybrania portu/adresu zrodlowego sluzy polecenie bind
<@Gynvael> poczytac mozna o nim:
<@Gynvael> bind
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/bind_2.asp
<@Gynvael> http://www.linuxmanpages.com/man2/bind.2.php
<@Gynvael> o tam
<@Gynvael> ;>
<@Gynvael> sock/sock4.c
<@Gynvael> rzucmy okiem na przyklad 4rty
<@Gynvael> kod ktory doszedl:
<@Gynvael> sock_local.sin_family = AF_INET;
<@Gynvael> sock_local.sin_addr.s_addr = inet_addr("0.0.0.0");
<@Gynvael> sock_local.sin_port = htons(31337);
<@Gynvael> memset(sock_local.sin_zero, 0, sizeof(sock_info.sin_zero));
<@Gynvael> if(bind(sock, (struct sockaddr*)&amp;sock_local, sizeof(struct sockaddr)) == -1)
<@Gynvael> goto err;
<@Gynvael> bind przyjmuje podobnie jak connect(), socket, i strukture z adresem oraz jej wielkosc
<@Gynvael> struktura z adresem jest identyczna jak ta w przypadku connect
<@Gynvael> i adresu na ktory sie laczymy
<@Gynvael> ale dwa slowa o niej
<@Gynvael> .sin_addr.s_addr = inet_addr("0.0.0.0");
<@Gynvael> IP 0.0.0.0 oznacza 'lacz sie z dowolnego interfejsu'
<@Gynvael> gdybysmy przykladowo dali tam "127.0.0.1", to nie mogli bysmy sie polaczyc nigdzie indziej niz na ip 127.*.*.*
<@Gynvael> w przypadku socketow listen, bind na 0.0.0.0 bedzie oznaczac 'akceptuj polaczenia z dowolnego interfejsu' ;>
<@Gynvael> jacekowski mi mowi ze pod linuxem do netstat powinny byc inne przelaczniki
<@Gynvael> <jacekowski> netstat -np | grep "66.249.85.104"
<@Gynvael> ew
<@Gynvael> ew-npt
<@Gynvael> ew -npt
<@Gynvael> ;>
<@Gynvael> dobra
<@Gynvael> a co do portu
<@Gynvael> to prosze pamietac o htons ;>
<@Gynvael> dalem tam port 31337 ;>
<@Gynvael> ok
<@Gynvael> kompilacja, odpalenie
<@Gynvael> i netstat zeby zobaczyc port zrodlowy
<@Gynvael> 21:27:35 >gcc sock4.c -lws2_32 -o sock4.exe
<@Gynvael> 21:33:35 >sock4.exe 66.249.85.104 80 > plik
<@Gynvael> Creating socket...
<@Gynvael> Connecting to 66.249.85.104:80...
<@Gynvael> Connected!
<@Gynvael> a teraz netstat
<@Gynvael> >netstat -anp tcp | find "66.249.85.104"
<@Gynvael> TCP 192.168.0.99:31337 66.249.85.104:80 ESTABLISHED
<@Gynvael> jak widac port zrodlowy faktycznie jest 31337 ;>
<@Gynvael> co by sie stalo gdyby juz jeden socket zrodlowy byl na 31337 zbindowany ?
<@Gynvael> dostali bysmy error
<@Gynvael> Error: Only one usage of each socket address (protocol/network address/port) is normally permitted.
<@Gynvael> taki pod winda
<@Gynvael> pod linuxem:
<@Gynvael> Error: Address already in use
<@Gynvael> OK. tyle o bind()
<@Gynvael> caly czas pracujemy na IP
<@Gynvael> 66.249.85.104 zamiast www.google.pl etc
<@Gynvael> pobawmy sie wiec w rezwiazywanie adresow domen ;>
<@Gynvael> o DNS poczytac mozna
<@Gynvael> DNS
<@Gynvael> http://pl.wikipedia.org/wiki/DNS
<@Gynvael> a glowna funkcja ktora bedziemy uzywac jest
<@Gynvael> gethostbyname
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/gethostbyname_2.asp
<@Gynvael> http://www.linuxmanpages.com/man3/gethostbyname.3.php
<@Gynvael> rzucmy okiem na przyklad kolejny
<@Gynvael> sock/sock5.c
<@Gynvael> nowy kawalek kodu to
<@Gynvael> ip = inet_addr(argv[1]);
<@Gynvael> if(ip == INADDR_NONE)
<@Gynvael> {
<@Gynvael> hostip = gethostbyname(argv[1]);
<@Gynvael> if(hostip == NULL)
<@Gynvael> goto err;
<@Gynvael> memcpy(&amp;ip, hostip->h_addr_list[0], 4);
<@Gynvael> }
<@Gynvael> inet_addr pamietacie
<@Gynvael> jesli ip zwrocne bedzie nieprawidlowe
<@Gynvael> czyli INADDR_NONE
<@Gynvael> to w takim razie mamy prawo przypuszczac ze nie dostalismy ip
<@Gynvael> tylko adres
<@Gynvael> funkcja gethostbyname() dostaje string
<@Gynvael> i zwraca adres (statycznej) struktury hostent
<@Gynvael> o tej strukturze powiem cos wiecej za chwile
<@Gynvael> narazie nas powinno to interesowac, ze pierwszy ip danego hosta (bo do domeny moze byc wiecej ip podpiete)
<@Gynvael> jest w ->h_addr_list[0]
<@Gynvael> zakodowany big endian ofc ;>
<@Gynvael> mozemy przetestowac programik, czy faktycznie sie polaczyc tam gdzie ma
<@Gynvael> a
<@Gynvael> jeszcze jedna zmiana doszla
<@Gynvael> ret = snprintf(data, sizeof(data), "GET / HTTP/1.1\r\nhost: %s\r\n\r\n", argv[1]);
<@Gynvael> czyli w sumie mozemy sciagac dowolny kawalek sieci
<@Gynvael> kompilacja i test
<@Gynvael> 20:27:25 >gcc sock5.c -o sock5.exe -lws2_32
<@Gynvael> 21:41:26 >sock5.exe wyklady.net 80
<@Gynvael> Creating socket...
<@Gynvael> Connecting to 193.189.117.75:80...
<@Gynvael> Connected!
<@Gynvael> HTTP/1.1 200 OK
<@Gynvael> Date: Mon, 07 Aug 2006 19:41:42 GMT
<@Gynvael> Server: Nephax Webserver
<@Gynvael> Transfer-Encoding: chunked
<@Gynvael> Content-Type: text/html
<@Gynvael> e1d
<@Gynvael> [...]
<@Gynvael> jak widac, dziala ;>
<@Gynvael> ok
<@Gynvael> ok.. pobawmy sie strukturka hostent
<@Gynvael> sock/sock6.c
<@Gynvael> to w sumie obciety program
<@Gynvael> ktory wyswietla wszystkie pola struktury hostent w przyjemny sposob
<@Gynvael> sa w niej nastepujace ciekawe pola:
<@Gynvael> ->h_aliases - lista (char**, jak np argv czy envp w main()) alternatywnych nazw domen
<@Gynvael> ->h_addr_list - lista (j/w) adresow ip
<@Gynvael> ta strukturka sie odrobine pod linuxem i winda rozni, ale zawartosc jej jest identyczna
<@Gynvael> ->h_name - glowna domena
<@Gynvael> skomilujmy i odpalmy np na google.pl
<@Gynvael> 17:23:59 >sock6 www.google.pl
<@Gynvael> Resolving "www.google.pl"...
<@Gynvael> Official host name: www.l.google.com
<@Gynvael> Alternative host name list:
<@Gynvael> - www.google.pl
<@Gynvael> - www.google.com
<@Gynvael> Resolved IP's:
<@Gynvael> - 66.249.85.99
<@Gynvael> - 66.249.85.104
<@Gynvael> jak widac mamy 2 rozne ip do wyboru ;>
<@Gynvael> dobra ;> tyle jesli chodzi o polaczenia TCP
<@Gynvael> teraz porozmawiamy o nasluchiwaniu TCP
<@Gynvael> postaram sie strescic zeby jeszcze dwa slowa o SNMP i UDP powiedziec ;>
<@Gynvael> sock/sock7.c
<@Gynvael> nasluchiwanie zbudowane jest tak:
<@Gynvael> 0) init winsocka pod winda
<@Gynvael> 1) stworzenie socketu (takie same parametry)
<@Gynvael> 2) zbindowanie go do konkretnego ip (lub nie -> 0.0.0.0) i portu (prosze pamietac ze porty 0-1023 wymagaja prawa roota/admina zeby sie na nich zbindowac)
<@Gynvael> 3) rozpoczecie nasluchiwania (listen())
<@Gynvael> 4) zaakceptowanie klienta (accept())
<@Gynvael> 5) jakies tam deinicjalizacje ;>
<@Gynvael> o funkcjach listen i accept mozna poczytac:
<@Gynvael> accept
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/accept_2.asp
<@Gynvael> http://www.linuxmanpages.com/man2/accept.2.php
<@Gynvael> listen
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/listen_2.asp
<@Gynvael> http://www.linuxmanpages.com/man2/listen.2.php
<@Gynvael> o bind() juz mowilismy
<@Gynvael> rzucmy okiem na listen
<@Gynvael> listen(sock, SOMAXCONN)
<@Gynvael> pierwszy parametr zrozumialy - socket
<@Gynvael> drugi tzw backlog count, czyli wielkosc kolejki polaczen do zaakceptowania
<@Gynvael> SOMAXCONN to "ile sie da" ;>
<@Gynvael> lsiten zwraca -1 jesli cos nie wyjdzie
<@Gynvael> remote = accept(sock, (struct sockaddr*)&amp;sock_remote, &amp;size);
<@Gynvael> accept natomiast oczekuje na polaczenie.. jesli sie jakies pojawi, to tworzy nowy socket (przez ktory bedzie nastepowac komunikacja z klientem) i go zwraca
<@Gynvael> dodatkowo wypelnia strukture sockaddr informacja o IP i porcie polaczonej osoby
<@Gynvael> i teraz wazna rzecz
<@Gynvael> polecenia
<@Gynvael> accept
<@Gynvael> recv
<@Gynvael> send
<@Gynvael> dzialaja jak np scanf czy gets
<@Gynvael> tj zatrzymuja wykonanie programu
<@Gynvael> az nie dojda dane / nie wysla sie dane / nie pojawi sie nowe polaczenie
<@Gynvael> wiec jesli bysmy odebrali dwa polaczenia
<@Gynvael> do recv() z oczekiwaniem na dane od drugiego
<@Gynvael> uniemozliwi odbior danych od drugiego
<@Gynvael> az do momentu gdy ten czegos nie wysle
<@Gynvael> jak to rozwiazac ? o tym za chwile
<@Gynvael> najpierw przetestujmy przyklad
<@Gynvael> 21:41:29 >sock7.exe 1234
<@Gynvael> Creating socket...
<@Gynvael> Binding to 0.0.0.0:1234...
<@Gynvael> Listening...
<@Gynvael> ok... czy netstat to widzi ?
<@Gynvael> TCP 0.0.0.0:1234 0.0.0.0:0 LISTENING
<@Gynvael> wyglada na to ze tak ;>
<@Gynvael> podlaczmy sie czyms na ten socket, np przegladarka
<@Gynvael> http://127.0.0.1:1234/ ;>
<@Gynvael> Client connected: 127.0.0.1:3090
<@Gynvael> GET / HTTP/1.1
<@Gynvael> Host: 127.0.0.1:1234
<@Gynvael> [...]
<@Gynvael> ;>
<@Gynvael> TCP 127.0.0.1:1234 127.0.0.1:3090 ESTABLISHED
<@Gynvael> netstat dojrzal to polaczenie
<@Gynvael> dobra
<@Gynvael> a teraz jak rozwiazac problem wielu polaczen ?
<@Gynvael> 1) stworzyc tablice na sockety
<@Gynvael> 2) kazdy tworzony socket przerabiac na socket asynchroniczny
<@Gynvael> co to znaczy socket asynchroniczny ?
<@Gynvael> tyle ze taki socket nie blokuje programu
<@Gynvael> jesli nie ma danych, recv zwraca -1
<@Gynvael> jesli nie ma nowego klienta, accept zwraca -1
<@Gynvael> jesli nie udalo sie odrazu wszystkiego wyslac, send zwraca ile sie udalo
<@Gynvael> jak przestawic socket w taki tryb?
<@Gynvael> rzucmy okiem na kolejny przyklad
<@Gynvael> sock/sock9.c
<@Gynvael> (przyklad sock8 sie duzo nie rozni, wiec go pomijam)
<@Gynvael> doszla tutaj nowa funkcja
<@Gynvael> void sockmode(int sock, int asyn);
<@Gynvael> sluzy ona do zmiany socketa w dany tryb (synchroniczny - asyn = 0, asynchroniczny - asyn = 1)
<@Gynvael> jej implementacja wyglada tak:
<@Gynvael> void sockmode(int sock, int asyn)
<@Gynvael> {
<@Gynvael> #ifdef __unix__
<@Gynvael> fcntl(sock, F_SETFL, asyn ? O_NONBLOCK : 0 );
<@Gynvael> #else
<@Gynvael> ioctlsocket(sock, FIONBIO, (unsigned long*)&amp;asyn);
<@Gynvael> #endif
<@Gynvael> }
<@Gynvael> pod nixami doszedl btw nowy include
<@Gynvael> #include <fcntl.h>
<@Gynvael> te dwie funkcje - fcntl i ioctlsocket robia analogicnzie to samo
<@Gynvael> jeden pod nixami, drugie pod winda
<@Gynvael> poczytac o tych funkcjach mozna:
<@Gynvael> fcntl
<@Gynvael> http://www.linuxmanpages.com/man2/fcntl.2.php
<@Gynvael> ioctlsocket
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/ioctlsocket_2.asp
<@Gynvael> o tam
<@Gynvael> ;>
<@Gynvael> analize petli odbierajacej zostawie wam.. w sumie duzo nowego tam nie ma
<@Gynvael> jako ze teraz nic nie blokuje programu
<@Gynvael> to sobie 100% procka zjada ;>
<@Gynvael> #ifdef __unix__
<@Gynvael> usleep(10000);
<@Gynvael> #else
<@Gynvael> Sleep(10);
<@Gynvael> #endif
<@Gynvael> mozna uzyc usleep i Sleep zeby sie detachnac CPU od naszego procesu i dac mu odpoczac ;>
<@Gynvael> dobra.. tyle jesli chodzi o TCP
<@Gynvael> teraz dwa kroociootkie przyklady na UDP
<@Gynvael> aaa
<@Gynvael> wazna rzecz jesli chodzi o TCP
<@Gynvael> 1) pakiety na 100% dojda (jesli jest fizyczna mozliwosc) w niezmienionej formie
<@Gynvael> o to sie bac nie musimy
<@Gynvael> 2) kolejnosc pakietow bedzie taka jak przy wysylaniu
<@Gynvael> 3) ale pamkiety moga zostac przefragmentowane.. tj jesli wyslemy dwa pakiety: AAA BBB, to mozemy czasem dostac 3 pakiety AA AB BB
<@Gynvael> przy projektowaniu ewentualnego protokolu trzeba to brac pod uwage
<@Gynvael> dodam ze zazwyczaj stosuje sie 3 bajtowa sekwecje:
<@Gynvael> 1 bajt - ID pakietu (to wewnetrzene ID u nas w programie.. np ID == 0 to moze byc pobranie lokacji gracza a ID == 1 wyslanie lokacji gracza)
<@Gynvael> 2 bajty - wielkosc pakietu
<@Gynvael> dzieki tym dwom bajtom wiadomo ile bajtow nalezy do tego naszego pakietu ;>
<@Gynvael> i gdzie sie zaczyna nastepny
<@Gynvael> natomiast w UDP
<@Gynvael> nie mamy pewnosci czy pakiet dojdzie
<@Gynvael> i nie mamy pewnosci czy sie nie zmieni
<@Gynvael> i nie mamy pewnosci czy kolejnosc bedzie taka sama ;p
<@Gynvael> nie ma kontroli tego wszystkiego
<@Gynvael> dzieki czemu wszystko dziala szybciej
<@Gynvael> idealne do gier, ale nieprzydatne przy rzeczach ktore na 100% musza dobre dojsc
<@Gynvael> w przypadku UDP nie ma czegos takiego jak polaczenia
<@Gynvael> wiec zadnego listen/accept/connect tam nie uswiadczymy
<@Gynvael> jedynie socket/closesocket/close/bind oraz nowe funkcje
<@Gynvael> sendto - wyslij do (dostaje to co send + dodatkowo adres gdzie ma dane wyslac (ip+port, w ten strukturce sockaddr_in))
<@Gynvael> recvfrom - odbierze dane (to co recv, dodatkowo wypelnia strukturke sockaddr_in informacjami od kogo cos dostal)
<@Gynvael> <jacekowski> udp zapewnia ze pakiet sie nie zmieni (ma sume kontrolna)
<@Gynvael> my mistake ;>
<@Gynvael> sock/sockudpc.c
<@Gynvael> sock/sockudps.c
<@Gynvael> rzucmy okiem na te dwa kody
<@Gynvael> pierwszy z nich dziala tak:
<@Gynvael> 0) inituje wsocka jesli trzeba (winda)
<@Gynvael> 1) tworzy socket
<@Gynvael> 2) binduje go do jakiegos lokalnego portu i adresu
<@Gynvael> 3) co sekunde wysyla "BUM!" na podane w parametrze programu IP/PORT (UDP ofc)
<@Gynvael> drugi natomiast
<@Gynvael> 0, 1) bez zmian
<@Gynvael> 2) bez zmian, z tym ze port jest podany w parametrze
<@Gynvael> 3) odczytuje wiadomosci i je pokazuje
<@Gynvael> odpalmy je
<@Gynvael> 21:55:37 >sockUDPs 1234
<@Gynvael> Creating socket...
<@Gynvael> Reciving packets...
<@Gynvael> UDP 0.0.0.0:1234 *:*
<@Gynvael> prosze zauwazyc ze netstat pokazal ze cos tam nasluchuje
<@Gynvael> 22:09:53 >sockUDPc localhost 1234
<@Gynvael> Creating socket...
<@Gynvael> Sending packets to 127.0.0.1:1234...
<@Gynvael> Recived from 127.0.0.1:1235 packet: BUM!
<@Gynvael> Recived from 127.0.0.1:1235 packet: BUM!
<@Gynvael> Recived from 127.0.0.1:1235 packet: BUM!
<@Gynvael> Recived from 127.0.0.1:1235 packet: BUM!
<@Gynvael> Recived from 127.0.0.1:1235 packet: BUM!
<@Gynvael> server zaczal odbierac
<@Gynvael> czyli dziala ;>
<@Gynvael> source pozostawiam do dokladnej wlasnej analizy
<@Gynvael> dodam tylko linki do recvfrom i sendto
<@Gynvael> sendto
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/sendto_2.asp
<@Gynvael> http://www.linuxmanpages.com/man2/sendto.2.php
<@Gynvael> recvfrom
<@Gynvael> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/recvfrom_2.asp
<@Gynvael> http://www.linuxmanpages.com/man2/recvfrom.2.php
<@Gynvael> calkiem niezly tutorial jaki znalalzem o tym jest na :
<@Gynvael> http://www.ragestorm.net/tutorial?id=12
<@Gynvael> dobra
<@Gynvael> teraz pare linkow
<@Gynvael> tyle jesli chodzi o TCP i UDP dzisiaj
<@Gynvael> powiem jeszcze 3 slowa o SNMP za chwile
<@Gynvael> pare linkow na koneic
<@Gynvael> http://gynvael.vexillium.org/?stuff=ircbot.stuff
<@Gynvael> http://gynvael.vexillium.org/?stuff=http_parse.stuff
<@Gynvael> http://tangentsoft.net/wskfaq/
<@Gynvael> to pierwsze to prosty bot ircowy
<@Gynvael> drugie to tutorial jak parsowac w locie http
<@Gynvael> dodam ze to dla fanatykow, lepiej uzyc po prostu np libCURL
<@Gynvael> do sciagania via http
<@Gynvael> a to ostatnie to FAQ grupy dyskusyjnej poswieconej winsockowi
<@Gynvael> genialny FAQ dodam ;>
<@Gynvael> ok
<@Gynvael> teraz chwile o SNMP - czyli jak zrobic wlasnego Netstata pod windowsem
<@Gynvael> http://pl.wikipedia.org/wiki/SNMP
<@Gynvael> SNMP - Simple Network Managment Protocol
<@Gynvael> to bardzo pogiety protokul do zarzadzania siecia
<@Gynvael> zabawa polega na tym ze mamy drzewko polecen/wartosci
<@Gynvael> z ktorych kazdy wezel jest oznaczony liczba
<@Gynvael> np
<@Gynvael> 1.2.3.4
<@Gynvael> to zapis analogiczny do dyskowego c:\1\2\3\4 ;> albo /1/2/3/4
<@Gynvael> ;>
<@Gynvael> uzywany w SNMP
<@Gynvael> rzeczy zwiazane z netem sa w drzewie 1.3.6.1
<@Gynvael> http://www.alvestrand.no/objectid/1.3.6.1.html
<@Gynvael> tam mozna o nim poczytac
<@Gynvael> sock/snmp3.cpp
<@Gynvael> sock/snmp11.h
<@Gynvael> to sa natomiast kody mojego netstata
<@Gynvael> (sprzed paru lat, wiec nie piekne ;p)
<@Gynvael> jako ze SNMP pozostawiam fascynatom, to pobierznie tylko omowie co sie dzieje
<@Gynvael> najpierw jest inicjacja winsocka
<@Gynvael> WSAStartup(0x0002,&amp;wsdat)
<@Gynvael> potem otwieramy DLLke inetmib1.dll
<@Gynvael> w niej jest interfejs (api) do SNMP pod windowsem
<@Gynvael> if(!mib.OpenMib("inetmib1.dll"))
<@Gynvael> inicjacja dllki (pobierane sa pointerki na funkcje do mojej klasy mib)
<@Gynvael> a potem sa wysylane zapytania
<@Gynvael> wlasnie w postaci takich sciezek po drzewku
<@Gynvael> http://www.alvestrand.no/objectid/1.3.6.1.2.1.6.13.1.html
<@Gynvael> dzieki temu zapytaniu
<@Gynvael> uzyskujemy dostep do listy stworzonych polaczen
<@Gynvael> potem pozostaje je wylistowac
<@Gynvael> troche juz przedluzylem wyklad, wiec kodu tez nie bede dokladnie analizowac, kto chce niech sie pobawi
<@Gynvael> ;>
<@Gynvael> kompilacja i odpalenie wyglada tak:
<@Gynvael> 22:21:40 >g++ snmp3.cpp -lsnmpapi -lws2_32
<@Gynvael> 22:21:57 >a
<@Gynvael> SNMP #3 R&amp;D "gNetStat" by Gynvael Coldwind of The Vexillium
<@Gynvael> Winsock Max Sockets : 0
<@Gynvael> Winsock Max UDP Datagram: 0
<@Gynvael> TCP 0.0.0.0:135 0.0.0.0:0 LISTENING
<@Gynvael> TCP 0.0.0.0:445 0.0.0.0:0 LISTENING
<@Gynvael> TCP 0.0.0.0:3690 0.0.0.0:0 LISTENING
<@Gynvael> TCP 0.0.0.0:8010 0.0.0.0:0 LISTENING
<@Gynvael> [...]
<@Gynvael> jeszcze dwa przykladowe programy z SNMP zeby bylo wiecej do analizy
<@Gynvael> sock/snmp2.cpp - wypisanie info o ilosci pakietow ICMP odebranych i wyslanych przez winde
<@Gynvael> sock/snmp1.cpp - wypisanie wszystkich interfejsow sieciowych i MACow
<@Gynvael> dorzuce jeszcze dwa przykladowe kody do winsocka
<@Gynvael> sock/cnetsock.zip - to jest przykladowa wmiare intuicyjna klasa pod winde do socketow ;>
<@Gynvael> sock/tunel.tar.gz - a tam jest modul w C do socketow i przykladowy kod tunelu (bouncera) ;>
<@Gynvael> dostalem info ze kodu z SNMP nie mozna na starym mingw skompilowac
<@Gynvael> 22:22:02 >gcc --version
<@Gynvael> gcc (GCC) 3.4.5 (mingw special)
<@Gynvael> na tym mozna
<@Gynvael> ;>
<@Gynvael> dobra w sumie tyle
<@Gynvael> dzieki za czas
<@Gynvael> http://forum.wyklady.net/index.php?topic=69.0
<@Gynvael> tam mozna wyklad komentowac i oceniac
<@Gynvael> przepraszam ze nie ze wszystkim zdarzylem... coz ;<
<@Gynvael> dzieki ;>