Utilizando o Winsock em Delphi
É muito comum o uso dos componentes ClientSocket e ServerSocket quando queremos trabalhar com aplicações que utilizam sockets em Delphi. Neste breve tutorial, iremos abordar o uso uso da A! A! nativa do "indo#s "indo#s para para trabalharmos com sockets $ "SA ou "insock A!. Na tentativa de simpli%icar simpli%icar e melhorar a compreens&o compreens&o do arti'o, arti'o, iremos trabalhar no modo console ao inv(s do modo 'r)%ico $ eu acho console muito estiloso **. Neste arti'o, utilizei a vers&o + do Delphi. Delphi. ara criar um novo pro'rama console, %aça o se'uinte -. ) at( o /enu 0ile 1 Ne# 1 2ther 3Arquivo, novo, outro45 6. Na aba Ne# 3Novo4 selecione Console Application Application 3Aplicaç&o Console45 7. 28 9em, ve:amos um simples pro'rama para console em Delphi pro'ram pro'rama-5 ;; Nome Nome do pro'rama <=A>?@ C2NS2@B uses Sstils5 be'in ;; CEdi'o aqui F end. Assim como nas lin'ua'ens C, CGG, erl e isual isual 9asic, por eHemplo, devemos incluir headers;mEdulos;bibliotecas headers;mEdulos;bibliotecas para podermos trabalhar com sockets. Como iremos trabalhar com o "insock, "insock, deveremos incluir todas as re%erIncias necess)rias, ve:a pro'ram pro'rama-5 <=A>?@ C2NS2@B uses Sstils,"insock5 ;; inclui$se a unit "insock para trabalharmos com a A! do "insock be'in ;; CEdi'o aqui F end. 2 primeiro passo a ser tomado para que possamos trabalhar com o "insock, "insock, ( inicializar sua biblioteca $ "S2C876.D $ atrav(s da %unç&o "SAStartup pro'ram pro'rama-5 <=A>?@ C2NS2@B
uses Sstils,"insock5 var #sa "SADA>A5 be'in i%3"SAStartup3=-J-,#sa4 K $-4 then be'in #riteln3L2correu um erro ao inicializar o "insock.L45 eHit5 end5 end. >emos nossa primeira vari)vel var #sa "SADA>A5 ma vari)vel do tipo "SADA>A armazena in%ormações sobre a inicializaç&o do "insock. @sta ( passada como se'undo ar'umento da %unç&o "SAStartup i%3"SAStartup3=-J-,#sa4 K $-4 then ;; Se ocorrer um erro be'in #riteln3L2correu um erro ao inicializar o "insock.L45 ;; /ostra mensa'em eHit5 ;; @ncerra end5 No trecho acima, tentamos tentamos inicializar a vers&o vers&o -.- do "insock "insock 3o sMmbolo = ( utilizado para nmeros heHadecimais. heHadecimais. =-J=-J- K 6O+4. e:a a sintaHe "SAStartup3@PSQ2 "2PD,var AP!R@"SA "SADA>A45 @PSQ2 vers&o do #insock a ser inicializada5 AP!R@"SA vari)vel do tipo "SADA>A. A %unç&o ir) retornar o valor $- se %alhar. Do contr)rio, retornar) T@P2. A %unç&o "SAStartup34, "SAStartup34, na verdade, requer um ponteiro para uma vari)vel do tipo "SADA>A como se'undo parUmetro, no entanto, sE ( necess)rio passar o nome da vari)vel. !sso se eHplica devido V declaraç&o do parUmetro var AP!R@"SA "SADA>A45 Wuando temos XvarX antes do parUmetro, si'ni%ica que ser) passado o endereço 3ponteiro4 da vari)vel, e n&o o seu valor K4 /uito bem, apEs inicializarmos o "insock, podemos utilizar as %unções contidas na "insock A!. >emos a'ora que criar um socket pro'ram pro'rama-5
<=A>?@ C2NS2@B uses Sstils,"insock5 var #sa "SADA>A5 sock inte'er5 be'in i%3"SAStartup3=-J-,#sa4 K $-4 then be'in #riteln3L2correu um erro ao inicializar o "insock.L45 eHit5 end5 sock K socket3A0!N@>,S2C8S>P@A/,J45 i%3sock K $-4 then be'in #riteln3L2correu um erro ao criar o socket.L45 eHit5 end5 end. >emos mais uma vari)vel declarada sock inte'er5 @sta vari)vel ir) armazenar a identi%icaç&o do nosso socket criado. Yeralmente, ao inv(s do tipo !nte'er 3inteiro4, vari)veis deste tipo s&o declaradas como S2C8@>. @ntretanto, XS2C8@>X ( apenas um Xnovo nomeX para o tipo inteiro, ou se:a, o tipo XS2C8@>X (, na verdade, o tipo inteiro K4 e:amos a criaç&o do socket sock K socket3A0!N@>,S2C8S>P@A/,J45 i%3sock K $-4 then be'in #riteln3L2correu um erro ao criar o socket.L45 eHit5 end5 2 trecho acima tenta criar um socket para ser utilizado com o protocolo >C. !sto ( de%inido pelo se'undo parUmetro S2C8S>P@A/. SintaHe sock K socket30A/Z!A !nte'er,P2>2C22 !nte'er,>!2 !nte'er45 sock vari)vel do tipo inteiro que ir) identi%icar o socket5 0A/Z!A
%amMlia do socket. @mbora eHistam diversas constantes, use$se a A0!N@> K !N>@PN@>. P2>2C22 protocolo com o qual o socket ir) trabalhar S2C8S>P@A/ K >C S2C8DYPA/ K D S2C8PA" K PA" >!2 opcional, de%ine opções relacionados ao tipo do protocolo !P2>2! K protocolo !5 !P2>2>C K protocolo >C5 !P2>2D K protocolo D5 !P2>2PA" K protocolo PA"5 !P2>2>C K protocolo !C/5 2 parUmetro sE ( obri'atErio quando estamos trabalhando com ra# sockets, podendo ser passado como T@P2, caso contr)rio. A %unç&o ir) retornar o valor $- se %alhar. Do contr)rio, retornar) T@P2. !remos começar pelo protocolo >C por ser mais utilizado ApEs termos criado o socket, iremos de%inir uma tare%a para este atuar como cliente ou servidor. ara tal, teremos que con%i'urar este socket de acordo com uma estrutura denominada XS2C8ADDP!NX. amos começar de%inindo o socket para trabalhar com servidor pro'ram pro'rama-5 <=A>?@ C2NS2@B uses Sstils,"insock5 var #sa "SADA>A5 sock inte'er5 addr sockaddrin5 be'in i%3"SAStartup3=-J-,#sa4 K $-4 then be'in #riteln3L2correu um erro ao inicializar o "insock.L45 eHit5 end5
sock K socket3A0!N@>,S2C8S>P@A/,J45 i%3sock K $-4 then be'in #riteln3L2correu um erro ao criar o socket.L45 eHit5 end5 addr.sin%amil K A0!N@>5 addr.sinport K htons3-67[45 addr.sinaddr.Saddr K !NADDPAN?5 i%3bind3sock,addr,sizeo%3addr44 K $-4 then be'in #riteln3L2correu um erro ao con%i'urar o socket.L45 eHit5 end5 i%3listen3sock,-4 K $-4 then be'in #riteln3L2correu um erro ao colocar o socket na escuta.L45 eHit5 end5 sock K accept3sock,nil,nil45 i%3sock K $-4 then be'in #riteln3L2correu um erro ao aceitar uma coneH&o.L45 eHit5 end5 #riteln3Lm cliente conectou$seFL45 closesocket3sock45 "SACleanup345 end. Antes de tudo, para con%i'urarmos um socket, devemos declarar uma estrutura do tipo S2C8ADDP!N addr sockaddrin5 amos ver como o socket ( con%i'urado addr.sin%amil K A0!N@>5 ;; Corresponde V %amMlia a qual o socket pertence addr.sinport K htons3-67[45 ;; Corresponde V porta na qual o socket ir) a'uardar coneHões addr.sinaddr.Saddr K !NADDPAN?5 ;; ermite que o socket aceite coneH&o de qualquer host \) dois detalhes que devem ser observados addr.sinport K htons3-67[45 ;; Correto 2 membro XsinportX, da estrutura XaddrX, ( um inteiro de 6 btes e requer um valor eHpresso em net#ork bte. ara convertemos um valor para tal, utilizamos a %unç&o htons34. A %orma abaiHo estaria incorreta
addr.sinport K -67[5 ;; !NC2PP@>2 K3 2 membro XsinaddrX armazena o endereço ! do host remoto. @ste membro, na verdade, pertence a uma estrutura chamada XinaddrX que armazena endereços !. Wuando queremos trans%ormar um ! para net#ork bte, utilizamos a %unç&o inetaddr34 addr.sinaddr.Saddr K inetaddr3L-6+.J.J.-L45 ;; >rans%orma o ! -6+.J.J.- para net#ork bte. Se vocI observar bem, utilizamos um membro dentro desta estrutura XSaddrX. Note ainda que seria incorreto %azer addr.sinaddr K inetaddr3L-6+.J.J.-L45 ;; @rrado 53 amos ver o por quI. addr $] estrutura sockaddrin5 sinaddr $] estrutura inaddr dentro de XaddrX5 Saddr $] membro dentro de XsinaddrX, um inteiro de [ btes. A %unç&o Xinetaddr34X retorna um valor inteiro 3tamb(m de [ btes4, eHpresso em net#ork bte, de um respectivo endereço !. Wuando tentamos %azer addr.sinaddr K inetaddr3L-6+.J.J.-L45 ;; @rrado 53 @stamos querendo atribuir um valor inteiro de [ btes de %orma direta a uma estrutura, por isso ocorreria o erro. ^) no outro eHemplo addr.sinaddr.Saddr K inetaddr3L-6+.J.J.-L45 ;; C@P>2 2 membro XSaddrX 3dentro de XsinaddrX4 ( um inteiro de [ btes e a %unç&o inetaddr34 tamb(m retorna um inteiro de [ btes, por isso a atribuiç&o ( v)lida É importante notar que sE podemos utilizar a %unç&o inetaddr34 com endereços ! addr.sinaddr.Saddr K inetaddr3L###.'oo'le.com.brL45 ;; !NC2PP@>2F eremos como obter o endereço ! de um host pelo seu nome mais adiante. ApEs con%i'uramos o socket, devemos chamar a %unç&o bind34 para prepar)$lo no computador local, permitindo$o a'uardar coneHões i%3bind3sock,addr,sizeo%3addr44 K $-4 then be'in #riteln3L2correu um erro ao con%i'urar o socket.L45 eHit5 end5 e:amos a sintaHe bind3sock !nte'er5 var addr sockaddrin5 tamanho !nte'er45 sock nome do nosso socket5 addr vari)vel pertencente V estrutura XsockaddrinX5 tamanho tamanho da estrutura XsockaddrinX.
A %unç&o ir) retornar o valor $- se %alhar. Do contr)rio, retornar) T@P2. Como :) havia dito antes, um socket ( identi%icado por uma vari)vel do tipo inteiro. !sso pode ser constatado observando o tipo do primeiro parUmetro X!nte'erX \avia dito tamb(m que, quando temos XvarX antes de um parUmetro, si'n%ica que iremos passar o endereço de uma vari)vel e n&o seu valor $ ( :ustamente o endereço da vari)vel XaddrX que temos que passar. e:a que, para passar o tamanho da estrutura XsockaddrinX, utilizamos o operador sizeo%34. >anto %az escrever Xsizeo%3addr4X ou Xsizeo%3sockaddrin4X $ embora esta ltima %orma se:a mais adequada. ApEs con%i'urado localmente, podemos colocar o socket em modo de escuta i%3listen3sock,-4 K $-4 then be'in #riteln3L2correu um erro ao colocar o socket na escuta.L45 eHit5 end5 No eHemplo acima, %azemos com que o socket a'uarde uma coneH&o na porta con%i'urada previamente. e:amos a sintaHe da %unç&o listen3sock !nte'er5 num !nte'er45 sock nome do nosso socket5 num nmero de coneHões que podem ser aceitas5 Assim como as outras %unções, se a %unç&o listen34 tiver IHito, esta retorna T@P2, sen&o, $- ( retornado. 2 trecho abaiHo %az com que o pro'rama %ique a'uardando at( que um pedido de coneH&o se:a %eito sock K accept3sock,nil,nil45 i%3sock K $-4 then be'in #riteln3L2correu um erro ao aceitar uma coneH&o.L45 eHit5 end5 Se, apEs recebido o pedido de coneH&o, o valor retornado %or $-, ( sinal que houve um erro ao aceitar esta coneH&o. e:a a sintaHe novosock K accept3sock !nte'er5 var ptaddr S2C8ADDP5 pttamanho !nte'er45 novosock
uma nova vari)vel 3inteiro4 que ir) armazenar a identi%icaç&o do novo socket5 se o nome do prEprio socket %or utilizado, n&o ser) possMvel aceitar novas coneHões5 sock nome do nosso socket5 ptaddr ponteiro para uma vari)vel do tipo XS2C8ADDPX que ir) armazenar in%ormações sobre o cliente que se conectou5 tamanho ponteiro para uma vari)vel do tipo inteiro que armazena o tamanho da estrutura XS2C8ADDPX5 A %unç&o ir) retornar o valor $- se %alhar. Do contr)rio, retornar) T@P2. No nosso eHemplo, usamos sock K accept3sock,nil,nil45 No caso, a vari)vel XsockX ser) utilizada para armazenar a identi%icaç&o do novo socket apEs um pedido de coneH&o ter sido %eito. 2bserve que, como n&o iremos armazenar in%ormações sobre o cliente, passamos os dois ltimos ar'umentos como N2S, isto (, passando o ponteiro nulo XnilX. e:amos a'ora closesocket3sock45 "SACleanup345 S&o duas %unções ainda n&o vistas anteriormente. tiliza$se a %unç&o closesocket34 para %echar um socket apEs seu uso e "SACleanup34 para %inalizar o uso do "insock. SintaHe closesocket3sock45 sock nome do socket. A %unç&o "SACleanup34 n&o possui parUmetros. /ais adiante, quando abordaremos al'umas %unções, veremos como obter in%ormações do cliente conectado. No eHemplo acima, criamos um socket para atuar com servidor. A se'uir, iremos criar um socket para atuar como cliente pro'ram pro'rama65 <=A>?@ C2NS2@B uses Sstils,"insock5 var #sa "SADA>A5 sock inte'er5
addr sockaddrin5 be'in i%3"SAStartup3=-J-,#sa4 K $-4 then be'in #riteln3L2correu um erro ao inicializar o "insock.L45 eHit5 end5 sock K socket3A0!N@>,S2C8S>P@A/,J45 i%3sock K $-4 then be'in #riteln3L2correu um erro ao criar o socket.L45 eHit5 end5 addr.sin%amil K A0!N@>5 addr.sinport K htons3-67[45 addr.sinaddr.Saddr K inetaddr3L-6+.J.J.-L45 i%3connect3sock,addr,sizeo%3addr44 K $-4 then be'in #riteln3L2correu um erro ao conectar$se.L45 eHit5 end5 #riteln3LConectadoFL45 closesocket3sock45 "SACleanup345 end. Como vocI pode observar, o cEdi'o necess)rio para tal ( bem menor e:amos as di%erenças addr.sinaddr.Saddr K inetaddr3L-6+.J.J.-L45 A linha acima con%i'ura o socket para se conectar no ! X-6+.J.J.-X. @m se'uida, %azemos com que o socket tente conectar$se i%3connect3sock,addr,sizeo%3addr44 K $-4 then be'in #riteln3L2correu um erro ao conectar$se.L45 eHit5 end5 A sintaHe da %unç&o connect34 ( similar V da %unç&o bind34 $ que n&o ( necess)ria quando estamos trabalhando com um socket cliente $ ve:a connect3sock !nte'er5 var addr sockaddrin5 tamanho !nte'er45 sock nome do nosso socket5 addr
vari)vel pertencente V estrutura XsockaddrinX5 tamanho tamanho da estrutura XsockaddrinX. A %unç&o ir) retornar o valor $- se %alhar. Do contr)rio, retornar) T@P2. Novamente, %echamos o socket e %inalizamos o #insock 3respectivamente4 closesocket3sock45 "SACleanup345 2 prEHimo passo ( trabalhar com o envio e recebimento de dados atrav(s do socket. Wuando estamos trabalhando com o protocolo >C, utilizamos as %unções recv34 e send34, respectivamente @nviando dados var bu%%er arra_J..`` o% char5 be'in bu%%er K LApenas um eHemploFL5 send3sock,bu%%er,strlen3bu%%er4,J45 end. No eHemplo acima, declaramos um arra de caracteres 3uma strin' em C4 com capacidade de armazenar -JJ elementos, isto (, -JJ caracteres bu%%er. @screvemos XApenas um eHemploFX neste arra e o enviamos. A sintaHe ( send3sock !nte'er5 var 9u%5 tambu%%er !nte'er5 0la's !nte'er45 sock nome do nosso socket5 9u% arra de caracteres ou um ponteiro para char que cont(m os dados que ser&o enviados5 tambu%%er nmero de btes que ser&o enviados5 %la's valores opcionais que especi%icam o modo de envio. A %unç&o, em situaç&o normal, retorna o nmero de btes enviados. Se al'um erro ocorrer, a %unç&o retorna $-. Note que utilizamos a %unç&o Xstrlen34X para retornar o tamanho do bu%%er strlen3bu%%er45 Pecebendo dados
var bu%%er arra_J..`` o% char5 be'in Tero/emor3bu%%er,-JJ45 ;; impa o bu%%er, ( necess)rio incluir X"indo#sX na cl)sula XsesX. recv3sock,bu%%er,-JJ,J45 end. A sintaHe ( recv3sock !nte'er5 var 9u%5 tambu%%er !nte'er5 0la's !nte'er45 sock nome do nosso socket5 9u% bu%%er que ir) armazenar os dados recebidos5 tambu%%er tamanho do bu%%er5 %la's valores opcionais que especi%icam o modo de recebimento. A %unç&o, em situaç&o normal, retorna o nmero de btes recebidos. Se al'um erro ocorrer, a %unç&o retorna $-. e:amos um eHemplo servidor pro'ram servidor5 <=A>?@ C2NS2@B uses Sstils,"insock, "indo#s5 ;; "indo#s $] para usar Tero/emor34 var #sa "SADA>A5 sock inte'er5 addr sockaddrin5 bu%%er arra_J..-JJ o% char5 ;; 9u%%er para enviar;receber dados envia strin'5 ;; Armazenar uma strin' di'itada btes inte'er5 ;; Nmero de btes recebidos be'in i%3"SAStartup3=-J-,#sa4 K $-4 then be'in #riteln3L2correu um erro ao inicializar o "insock.L45
eHit5 end5 sock K socket3A0!N@>,S2C8S>P@A/,J45 i%3sock K $-4 then be'in #riteln3L2correu um erro ao criar o socket.L45 eHit5 end5 addr.sin%amil K A0!N@>5 addr.sinport K htons3-67[45 addr.sinaddr.Saddr K !NADDPAN?5 i%3bind3sock,addr,sizeo%3addr44 K $-4 then be'in #riteln3L2correu um erro ao con%i'urar o socket.L45 eHit5 end5 i%3listen3sock,-4 K $-4 then be'in #riteln3L2correu um erro ao colocar o socket na escuta.L45 eHit5 end5 sock K accept3sock,nil,nil45 i%3sock K $-4 then be'in #riteln3L2correu um erro ao aceitar uma coneH&o.L45 eHit5 end5 btes K J5 ;; J bte recebido #hile3btes ] $-4 do ;; @nquanto o nmero de btes retornando %or di%erente de $- K conectado be'in Tero/emor3bu%%er,-JJ45 ;; Tera bu%%er recv3sock,bu%%er,-JJ,J45 ;; Pecebe dados do cliente #riteln3bu%%er45 ;; /ostra$os Tero/emor3bu%%er,-JJ45 ;; impa o bu%%er novamente readln3envia45 ;; I uma strin' StrCop3bu%%er,Char3envia4,-JJ45 ;; Copia at( -JJ caracteres para o bu%%er send3sock,bu%%er,strlen3bu%%er4,J45 ;; @nvia os dados end5 ;; @ncerra closesocket3sock45 "SACleanup345
end. cliente pro'ram cliente5 <=A>?@ C2NS2@B uses Sstils,"insock, "indo#s5 ;; "indo#s $] para usar Tero/emor34 var #sa "SADA>A5 sock inte'er5 addr sockaddrin5 bu%%er arra_J..-JJ o% char5 ;; 9u%%er para enviar;receber dados envia strin'5 ;; Armazenar uma strin' di'itada btes inte'er5 ;; Nmero de btes recebidos be'in i%3"SAStartup3=-J-,#sa4 K $-4 then be'in #riteln3L2correu um erro ao inicializar o "insock.L45 eHit5 end5 sock K socket3A0!N@>,S2C8S>P@A/,J45 i%3sock K $-4 then be'in #riteln3L2correu um erro ao criar o socket.L45 eHit5 end5 addr.sin%amil K A0!N@>5 addr.sinport K htons3-67[45 addr.sinaddr.Saddr K inetaddr3L-6+.J.J.-L45 i%3connect3sock,addr,sizeo%3addr44 K $-4 then be'in #riteln3L2correu um erro ao conectar$se.L45 eHit5 end5 btes K J5 ;; J bte recebido #hile3btes ] $-4 do ;; @nquanto o nmero de btes retornando %or di%erente de $- K conectado be'in Tero/emor3bu%%er,-JJ45 ;; impa o bu%%er
readln3envia45 ;; I uma strin' StrCop3bu%%er,Char3envia4,-JJ45 ;; Copia at( -JJ caracteres para o bu%%er send3sock,bu%%er,strlen3bu%%er4,J45 ;; @nvia os dados Tero/emor3bu%%er,-JJ45 ;; impa o bu%%er novamente recv3sock,bu%%er,-JJ,J45 ;; Pecebe dados do cliente #riteln3bu%%er45 ;; /ostra$os end5 ;; @ncerra closesocket3sock45 "SACleanup345 closesocket3sock45 "SACleanup345 end. 2 pro'rama acima ( o cl)ssico chat cliente$servidor. Yeralmente, eHemplos como este s&o apresentados quando estamos estudando sockets eremos a'ora duas %unções para trabalharmos com endereços ! e hostnameLs -4 'ethostbname34 amos retomar addr.sinaddr.Saddr K inetaddr3L###.'oo'le.com.brL45 ;; !NC2PP@>2F 2correria um erro acima pois X###.'oo'le.com.brX n&o ( um endereço !, e sim um hostname. Antes de podermos con%i'urar o socket para se conectar neste host, devemos obter o seu ! utilizando a %unç&o 'ethostbname34 var ... host \ost@nt5 ;; \ost@nt K ponteiro para a estrutura hostent addr sockaddrin5 be'in ... host K 'ethostbname3L###.'oo'le.com.brL45 i%3host K nil4 then #riteln3L@rro ao resolver o hostFL4 else addr.sinaddr K !nAddr3host.haddr*4*5 Declaramos um ponteiro para a estrutura XhostentX que ir) armazenar as in%ormações sobre o computador remoto XhostX. A %unç&o retorna um ponteiro nulo 3nil4 caso n&o consi'a resolver o hostname. 2 endereço deste host ( armazenando no membro XhaddrX desta estrutura, eHpresso em net#ork bte. e:a addr.sinaddr K !nAddr3host.haddr*4*5 A linha acima parece ser um pouco complicada, mas n&o ( . Sabemos que XhostX ( um
ponteiro para a estrutura XhostentX. 2 membro Xhost.haddrX ( um ponteiro para C\AP que aponta para o endereço ! do host, eHpresso em net#ork bte. ara acessar o valor de um membro apontado por um pointeiro, %azemos ponteiro.membro* que equivale a host.haddr*5 ;; retorna o endereço apontado pelo membro XhaddrX5 No caso acima, como o membro XhaddrX ( um ponteiro e n&o uma simples vari)vel, o seu valor ( o endereço para onde ele aponta. !nAddr ( um ponteiro 'lobal para a estrutura XinaddrX3mesmo tipo do membro XsinaddrX4. Wuando temos X!nAddr3host.haddr*4X, estamos %azendo com que o ponteiro X!nAddrX aponte para o mesmo endereço que Xhost.haddrX aponta, isto (, aquele endereço que armazena o endereço ! do host. A di%erença ( que o valor deste endereço ser) obtido como prEprio para a estrutura XinaddrX e n&o mais como XC\APX. At( aM, temos addr.sinaddr K @ND@P@2 apontado por Xhost.haddrX :) obtido como XinaddrX5 No entanto, XsinaddrX requer um valor XinaddrX e n&o um ponteiro, por isso %azemos !nAddr3host.haddr*4*5 ;; alor XinaddrX do endereço apontado ortanto, quando temos addr.sinaddr K !nAddr3host.haddr*4*5 @stamos atribuindo ao membro XsinaddrX o valor apontado pelo membro Xhost.haddrX convertido para XinaddrX. ara compreender melhor o processo, ( recomend)vel o estudo de ponteiros 64 inetntoa34 Com esta %unç&o, podemos trans%ormar um ! eHpresso em net#ork bte para strin'. @sta %unç&o %az o processo inverso da %unç&o inetaddr34. e:a var ... addr sockaddrin5 be'in ... addr.sinaddr.Saddr K inetaddr3X-6+.J.J.-X45 ;; Converte o ! de strin' para net#ork bte #riteln3L2 ! e L G inetntoa3addr.sinaddr445 ;; Converte de net#ork bte para strin' É importante notar que, para utilizar a %unç&o inetntoa34, devemos passar um valor do tipo XinaddrX como parUmetro. e:a outro eHemplo var ... host \ost@nt5 ;; \ost@nt K ponteiro para a estrutura hostent
addr sockaddrin5 be'in ... host K 'ethostbname3L###.'oo'le.com.brL45 i%3host K nil4 then #riteln3L@rro ao resolver o hostFL4 else #riteln3L2 ! e L G inetntoa3!nAddr3host.haddr*4*445 No eHemplo acima, tentamos resolver o host X###.'oo'le.com.brX e mostrar o seu respectivo !. Como vimos anteriormente, a eHpress&o X!nAddr3host.haddr*4*X retorna o endereço ! de um host como valor XinaddrX $ :ustamente o tipo de valor requirido pela %unç&o inetntoa34. 0!/ da primeira parte Na continuaç&o do arti'o, veremos melhor como trabalhar com a %unç&o 'ethostbname34 e outras %unções. eremos ainda o uso do protocolo D. Pois bem, continuemos com o nosso artigo. Abaixo segue uma pequena lista de aspectos abordados no artigo anterior: 1) Vimos o que é necessário para trabalhar com a API do insoc! "#A) no $elphi% &) 'oram mostrados os passos que de(em ser seguidos para criar um soc!et simples, abordando, exclusi(amene, o protocolo *P% +) Ainda com base no protocolo *P, oram ilustrados exemplos de um programa que atua(a como cliente e um outro ser(idor% -) tili/ando as un0es send") e rec("), aprendemos a como en(iar e receber dados, respecti(amente atra(és de um soc!et% 2) Algumas no0es de (ariá(eis, estruturas, un0es e con(erses% 3este 4ltimo item, icou pendente a explica05o do uso de un05o gethostb6name") aplicada em outras situa0es, além de outras un0es do ramo, como getser(b6port"), que também será aborda nesta parte do artigo. Para come0ar, (amos retomar o 4ltimo exemplo da primeira parte do tutorial: (ar ... host: P7ost8nt% 99 P7ost8nt ponteiro para a estrutura hostent addr: soc!addr;in% begin ... host : gethostb6name"<===.google.com.br<)% i"host nil) then =riteln"<8rro ao resol(er o host><) else =riteln" IP e: < @ inet;ntoa"PInAddr"host.h;addr)))% 3o exemplo acima, tentamos resol(er o host B===.google.com.brB e mostrar o seu respecti(o IP. *omo (imos anteriormente, a express5o BPInAddr"host.h;addr)B retorna o endere0o IP de um host como (alor Bin;addrB C Dustamente o tipo de (alor requirido pela un05o inet;ntoa").
*omo se sabe, de(emos especiicar dados sobre um host remoto para que seDa possE(el uma conex5o entre o computador local e este. #abemos ainda que, para tanto, de(emos utili/ar uma estrutura denominada Bsoc!addr;inB, que contém trFs principais membros: sin;amil6 CG indica a amElia do soc!et% sin;port CG indica a porta na qual o soc!et irá atuar% sin;addr CG indica o endere0o utili/ado pelo soc!et, seDa este local ou remoto% $ando Fnase ao 4ltimo, sabemos que o utili/amos quando queremos, por exemplo, estabelecer uma conex5o entre o computador local e um outro remoto cuDo IP é &HH.1&+.1&+.1&+: addr.sin;addr.#;addr inet;addr"<&HH.1&+.1&+.1&+<)% *omo se sabe, com o auxElio da un05o inet;addr"), podemos con(erter um endere0o IP escrito na orma de string "<&HH.1&+.1&+.1&+<) para seu (alor correspondente na orma in;addr "net=or! b6te) que é a adequada. 8xistem casos, no entanto, em que n5o sabemos o endere0o IP do computador remoto, e pior ainda: s (e/es, até sabemos, entretanto, este IP pode ser dinJmico, isto é, n5oCixo. 8m situa0es assim, se ti(éssimos que tomar como base apenas o endere0o IP de hosts remotos, terEamos grande diiculdades de comunica05o. 'eli/mente, podemos utli/ar o BhostnameB de um computador como reerFncia, dessa orma, n5o importa qual endere0o IP que este computador esteDa utli/ando, pois, ao contrário de endere0os IPs, o hostname é ixo, ou seDa, mesmo que o endere0o IP de um ser(idor seDa alterado, atra(és do seu hostname podemos acessáClo. Kas a quest5o é: como aplicar isso em nossos programasL M para responder a essa pergunta que este artigo oi come0ado com 4ltimo exemplo da parte 1 do tutorial x$ Vamos a/er uma análise rápida no seguinte trecho: (ar =sa: #A$AA% 99 Para iniciali/ar o =insoc! host: P7ost8nt% 99 P7ost8nt ponteiro para a estrutura hostent begin i"#A#tartup"N1H1,=sa) C1) then exit% 99 8ncerra se alhar host : gethostb6name"<===.google.com.br<)% end. M um exemplo um pouco que repetiti(o, eu diria. Kas continuemos: (ar =sa: #A$AA% host: P7ost8nt% 99 P7ost8nt ponteiro para a estrutura hostent begin i"#A#tartup"N1H1,=sa) C1) then exit% host : gethostb6name"<===.google.com.br<)% i"host nil) then 99 8rro else 99 7ost resol(ido com sucesso x$
end. Ouando a un05o gethostb6name") alha em resol(er um hostname, um ponteiro nulo "nil) é retornado. 'a/emos o tratamento de erros com base nessa propriedade. (ar =sa: #A$AA% host: P7ost8nt% 99 P7ost8nt ponteiro para a estrutura hostent addr: soc!addr;in% 99 ma estrutura do tipo soc!addr;in begin i"#A#tartup"N1H1,=sa) C1) then exit% host : gethostb6name"<===.google.com.br<)% i"host nil) then exit 99 8ncerra se alhar else addr.sin;addr : PInAddr"host.h;addr)% end. *om base no cdigo acima, (ocF consegue responder a quest5o inicial x$ L ? que exatamente ocorre é que, ao utili/ar a un05o gethostb6name") passando como parJmetro o hostname <===.google.com.br<, esta un05o tentará resol(er este hostname, isto é, obter seu endere0o IP, seu nome oicial, dentre outros dados. *omo (imos na parte anterior do texto "e no inEcio desta), a combina05o PInAddr"host.h;addr) nos retorna o endere0o IP de um host Dá con(ertido para Bin;addrB que nos possibilita usáClo como (alor para o membro Bsin;addrB da estrutura Bsoc!addr;inB. A partir deste ponto, Dá se pode utili/ar a un05o connect"), por exemplo, para criar uma conex5o com o computador cuDo hostname utli/amos. VeDa o exemplo: program exemplo% QNAPPRP8 *?3#?S8T uses #6stils,insoc!% (ar =sa: #A$AA% soc!: integer% addr: soc!addr;in% host: P7ost8nt% begin i"#A#tartup"N1H1,=sa) C1) then begin =riteln"
i"soc! C1) then begin =riteln"
begin =riteln"
T ser( : getser(b6port"htons"WH),
else i"ser( nil) and "#AXetSast8rror") YG H) then begin =riteln"<8rro na uncao getser(b6port"). I$: < @ Into#tr"#AXetSast8rror")))% exit end else =riteln"<#er(ico: < @ ser(.s;name)% #A*leanup")% end. Agora, o programa n5o mais mostra que o ser(i0o é desconhecido, mas sim que um erro ocorreu e a I$ deste erro. Ouando a un05o #AXetSast8rror") retorna Z8?, é sinal que nenhum erro ocorreu até o ponto em que oi chamada. 8nt5o, a lgica seria: #e Bser(B nil e B#ASast8rror")B H CG n5o ocorreram erros no =insoc!, no entanto, a un05o getser(b6port") n5o conseguiu obter o ser(i0o% #e Bser(B nil e B#ASast8rrorB n5o or Z8?, ocorreu um erro no uso do insoc!% #e Bser(< n5o retornar um ponteiro nulo, o ser(i0o oi obtido com sucesso. 3o exemplo, oram utili/ados i, elsei e else, mas (ocF pode reorgani/ar a lgica tornando o cdigo mais legE(el e bonito x$ ma curiosidade: existe um arqui(o no qual existem todos os ser(i0os9portas reconhecidos pelo sistema. 8ste arqui(o é acessado pela un05o getser(b6port") para nos retornar um ser(i0o deseDado. 3o indo=s, sua locali/a05o é: *:[I3$?#[#R#8K+&[$ri(ers[etc[ser(ices. Ainda alando sobre erros gerados durante o uso do =insoc!, a un05o #AXetSast8rror") possui uma outra oposta: #A#etSast8rror. 8sta 4ltima, por sua (e/, é utili/ada para deinir a I$ do 4ltimo erro ocorrido. 8xemplo: #A#etSast8rror"H)% 3a linha acima, simplesmente /eramos o status de erro. 3o inal desta página, postarei uma tabela contendo os principais cdigos de erro, suas constantes e seus respecti(os signiicados. Voltando a alar um pouco sobre hostname
=sa: #A$AA% nome;local: arra6\H..]]^ o char% begin i"#A#tartup"N1H1,=sa) C1) then begin =riteln"
luxo de dados. ma (e/ em que o protocolo $P n5o é orientado a conex5o, podemos dedu/ir que o uso da un0es connect") e listen")9accept") s5o desnecessárias em sua aplica05o. *omo oi dito anteriormente, basta saber o endere0o remoto de um host e sua porta para que possamos en(iar dados para este, utili/ando o protocolo $P. ? mesmo se aplica entrada de dados. *om o protocolo *P, utitli/amos, respecti(amente, as un0es send") e rec("). 3o protocolo $P, as coisas mudam um pouco: utili/aCse sendto") para o en(io de dados, e rec(rom") para recebermos dados. #egue abaixo um cdigo que mostra a cria05o de um soc!et que trabalhe sob o protocolo $P: program exemplo% QNAPPRP8 *?3#?S8T uses #6stils,insoc!% (ar =sa: #A$AA% soc!: integer% addr: soc!addr;in% buer: arra6 \H..]]^ o char% begin i"#A#tartup"N1H1,=sa) C1) then begin =riteln"<% i"sendto"soc!,buer,strlen"buer), H,addr,si/eo"addr)) C1) then begin =riteln"<8rro ao en(iar dados><)% exit% end% closesoc!et"soc!)% #A*leanup")% end. ? primeiro ponto a ser obser(ado no cdigo acima é que, ao in(és de utili/armos #?*U;#8AK como segundo parJmetro da un05o soc!et"), utili/amos #?*U;$XAK que é a constante correta para o protocolo $P.
3otaCse que, no cdigo, apenas especiicamos as inorma0es do host e utili/amos a un05o sendto") para en(iar a string contida no arra6 BbuerB para este computador "&HH.1&+.1&+.1&+). A sintaxe da un05o sendto") é bastante semelhante da un05o send"), com exce05o apenas dos dois 4ltimos parJmetros: send"soc!: Integer% (ar `u% tam;buer: Integer% 'lags: Integer, addr: soc!addr;in% tam;addr: integer)% soc!: nome do nosso soc!et% `u: arra6 de caracteres ou um ponteiro para char que contém os dados que ser5o en(iados% tam;buer: n4mero de b6tes que ser5o en(iados% lags: (alores opcionais que especiicam o modo de en(io. addr: (ariá(el do tipo soc!addr;in que contém as inorma0es de endere0o9porta do host remoto% tam;addr: tamanho da estrutura soc!addr;in% Assim como a un05o send"), sendto") também retorna o n4mero de b6tes en(iados. #e algum erro ocorrer, a un05o retorna C1. VeDamos um exemplo de um ser(idor $P: program exemplo% QNAPPRP8 *?3#?S8T uses #6stils,insoc!,indo=s% 99 ZeroKemor6 CG deinida em indo=s (ar =sa: #A$AA% soc!: integer% addr,addr;remoto: soc!addr;in% buer: arra6 \H..]]^ o char% tam;addr: integer% begin i"#A#tartup"N1H1,=sa) C1) then begin =riteln"
=riteln"< @ 1H)% =riteln"
3ada de no(o. Apenas coniguraCse localmente o soc!et de tal orma que este utili/e a porta 1&+- para receber9en(iados dados. ZeroKemor6"buer,1HH)% tam;addr : si/eo"soc!addr;in)% i"rec(rom"soc!,buer,1HH,H,addr;remoto,tam;addr ) C1) then begin =riteln"<8rro na uncao rec(rom")<)% exit% end% 3este trecho, preencheCse com /ero todo o buer, ou podemos simplesmente di/er que limpaCse o BbuerB para que ele possa arma/enar ocasionais dados. 3a prxima linha, estamos atribuindo (ariá(el Btam;addrB o tamanho, em b6tes, da estrutura Bsoc!addr;inB. 8, desmembrando um pouco mais o cdigo, temos: i"rec(rom"soc!,buer,1HH,H,addr;remoto,tam;addr ) C1) then begin =riteln"<8rro na uncao rec(rom")<)% exit% end% M no trecho acima que, de ato, aguardamos por dados (indos de algum cliente. VeDamos a sintaxe: rec(rom"soc!: Integer% (ar `u% tam;buer: Integer% lags: Integer, addr:soc!addr;in, (ar tam)% soc!: nome do nosso soc!et% `u: buer que irá arma/enar os dados recebidos% tam;buer: tamanho do buer% lags: (alores opcionais que especiicam o modo de recebimento. addr: (ariá(el do tipo soc!addr;in que contém as inorma0es de endere0o9porta do host remoto% tam;addr: (ariá(el que arma/ena o tamanho da estrutura soc!addr;in% A un05o, assim como rec("), retornará o n4mero de b6tes recebidos, exceto quando algum erro ocorrer, onde o (alor C1 é retornado. Para inali/ar: =riteln"<$ados recebidos>< @ 1H)% =riteln"
3esta parte, apenas s5o mostrados inorma0es sobre um cliente remoto que en(iou determinados dados: ip, porta e os dados em si. A un05o inet;ntoa") (ocF Dá conhece: é responsá(el por transormar um IP expresso em net=or! b6te para string. ma un05o que ainda n5o oi abordada é ntohs"). 8sta a/ o trabalho in(erso ao da un05o htons"). 8nquanto esta 4ltima transorma um (alor denominado host b6te order para net=or! b6te order, a un05o ntohs") obtém um (alor em net=or! b6te e o transorma em host b6te order. 3et=or! b6te order é o tipo de (alor utili/ado para comunica05o, sobretudo, nas estruturas de soc!ets, no qual o b6te mais signiicati(o "também chamado octeto) é o primeiro. 8m host b6te order, o b6te menos signiicati(o é o primeiro. 8xemplo: Valor em net=or! b6te order: 2+ Valor em host b6te order: &1 Vale lembrar que as un0es htons") e ntohs") retornam (alores numéricos "de & b6tes). Por esta ra/5o, utili/amos Into#tr") C para con(eter de inteiro para string C na seguinte linha: =riteln"