Bom, neste tópico prentendo explicar, de forma bem simples, como enviar diversos tipos de dados via socket. Os dados que eu vou citar aqui serão: arquivos e blocos de memória. Não vou comentar sobre texto pois não é esse o foco, considerando que texto é mais facil de ser tratado...
1) O QUE É STREAM?
Stream, como o próprio nome sugere, é um fluxo de dados. Esse fluxo é linear, portanto contém um início e um fim e, a partir que o pc o lê, ele "navega" entre esse fluxo, mudando sua posição nessa "linha" de dados. Existem alguns tipos específicos de streams, mas os que iremos utilizar é o TMemoryStream. Poderíamos utilizar também o TFileStream, mas como o TMemoryStream é mais genérico acho que ele está de bom tamanho para aprendizagem ;D
2) ENTENDENDO A LÓGICA DE ENVIO
Bom, antes de começar a por a mão na massa vamos tentar entender por cima COMO vai funcionar esse envio. É capaz que algumas pessoas se percam nessa explicação por ser muito abstrata, mas não pulem essa parte, mesmo que não entendam por completo, pois senão, na hora do código vocês podem se deparar com uma lógica e não entender o por quê dela.
Primeiramente, o socket não envia toda a informação de uma vez, portanto para enviar um único stream, o evento onRead será ativado diversas vezes. Mas se tudo isso não será constante, como escrever tudo num stream só?? Como saber quando ja acabou um stream e começou o outro??
Como vocês podem imaginar, será necessário declarar uma variavel global TMemoryStream para que se junte toda a informação que vem dividida num stream só. Para saber quando acaba um stream é necessário o seguinte procedimento: primeiro é enviado o TAMANHO desse stream (em bytes) e só depois se envia o stream em si. Logo, no onRead, será necessário apenas checar o tamanho do stream que ja está escrito na memória com o tamanho recebido... Usaremos também uma variável global BOOLEAN, para verificar se o socket ja está recebendo o stream ou não, justamente para saber se vai receber o tamanho dele ou ele em si...
Bom, é basicamente assim que vamos transferir dados via socket... Neste exemplo eu vou mandar uma foto do ServerSocket para o ClientSocket... Adivinhem que foto será essa... Exato, uma screenshot da tela. Esse procedimento é utilizado em remote desktop, mas o foco aqui é apenas o envio de informação...
3) BOTANDO A MÃO NA MASSA
Bom, primeiro vamos fazer o Server que vai enviar a foto, que é mais simples... No Form coloquem apenas um ServerSocket. Vamos adicionar uma variável global TMemoryStream chamada Stream, para ser utilizada no envio.
Agora temos que colocar a função que tira a foto em si... Reparem que eu vou convertê-la para jpg para ficar menor e o envio demorar menos, portanto ponham no USES "jpeg"...
**código adaptado do torry.net**
Agora temos que especificar QUANDO o server vai começar a enviar a foto né? Vamos fazê-lo enviar quando o cliente pedir pelo comando "mandafoto". Portanto temos que adicionar uma rotina de verificação no onClientRead e, se o texto recebido for "mandafoto", ele cria o Stream, salva a foto numa variavel TJpegImage, salva essa TJpegImage pro stream, envia o tamanho do stream e, finalmente, envia o stream...
Código: Selecionar tudo
Notem que eu usei o null-terminator (#0) ao fim do tamanho do stream apenas como um controle para o Client, pois mesmo os textos podem ir cortados por socket...
Pronto, o Server ja está pronto, agora vamos aprender a fazer o Cliente.
Criem uma nova aplicação e coloque no form um ScrollBox (na aba Additional), uma Image DENTRO desse Scrollbox, um ClientSocket e um botão. Agora declares as seguintes variáveis globais:
Como ja expliquei pra que elas servem ali emcima, não vou ficar me detendo aqui... Agora, vamos fazer o Botao pedir a imagem pro Server. Para isso o Cliente deve mandar o comando "mandafoto", como já foi pré-estabelecido no Server...
Agora só falta o onRead do cliente... Antes de postar o código gostaria de que o stream, para ser utilizado corretamente, sua Position deve estar 0 (pois como disse antes, a leitura do fluxo de dados vai começar do começo). Agora, já explicado como funciona o cliente, vamos ao código:
Pronto!! Rodem os programas e testem... Qualquer dúvida postem aqui!!
PS: Lembrando que ao invés de uma imagem, poderia ser utilizado qualquer outro tipo de dados, como um arquivo, apenas utilizem Stream.LoadFromFile('ashdauhsdua.exe'); E lembrem-se que para poder abrir o arquivo é necessário liberar o stream da memória!!
1) O QUE É STREAM?
Stream, como o próprio nome sugere, é um fluxo de dados. Esse fluxo é linear, portanto contém um início e um fim e, a partir que o pc o lê, ele "navega" entre esse fluxo, mudando sua posição nessa "linha" de dados. Existem alguns tipos específicos de streams, mas os que iremos utilizar é o TMemoryStream. Poderíamos utilizar também o TFileStream, mas como o TMemoryStream é mais genérico acho que ele está de bom tamanho para aprendizagem ;D
2) ENTENDENDO A LÓGICA DE ENVIO
Bom, antes de começar a por a mão na massa vamos tentar entender por cima COMO vai funcionar esse envio. É capaz que algumas pessoas se percam nessa explicação por ser muito abstrata, mas não pulem essa parte, mesmo que não entendam por completo, pois senão, na hora do código vocês podem se deparar com uma lógica e não entender o por quê dela.
Primeiramente, o socket não envia toda a informação de uma vez, portanto para enviar um único stream, o evento onRead será ativado diversas vezes. Mas se tudo isso não será constante, como escrever tudo num stream só?? Como saber quando ja acabou um stream e começou o outro??
Como vocês podem imaginar, será necessário declarar uma variavel global TMemoryStream para que se junte toda a informação que vem dividida num stream só. Para saber quando acaba um stream é necessário o seguinte procedimento: primeiro é enviado o TAMANHO desse stream (em bytes) e só depois se envia o stream em si. Logo, no onRead, será necessário apenas checar o tamanho do stream que ja está escrito na memória com o tamanho recebido... Usaremos também uma variável global BOOLEAN, para verificar se o socket ja está recebendo o stream ou não, justamente para saber se vai receber o tamanho dele ou ele em si...
Bom, é basicamente assim que vamos transferir dados via socket... Neste exemplo eu vou mandar uma foto do ServerSocket para o ClientSocket... Adivinhem que foto será essa... Exato, uma screenshot da tela. Esse procedimento é utilizado em remote desktop, mas o foco aqui é apenas o envio de informação...
3) BOTANDO A MÃO NA MASSA
Bom, primeiro vamos fazer o Server que vai enviar a foto, que é mais simples... No Form coloquem apenas um ServerSocket. Vamos adicionar uma variável global TMemoryStream chamada Stream, para ser utilizada no envio.
Agora temos que colocar a função que tira a foto em si... Reparem que eu vou convertê-la para jpg para ficar menor e o envio demorar menos, portanto ponham no USES "jpeg"...
Código PHP:
function GetScreenShot: TJPEGImage;
var
Desktop: HDC;
bmp: TBitmap;
begin
Result:=TJPEGImage.Create;
bmp := TBitmap.Create;
Desktop := GetDC(0);
try
try
bmp.PixelFormat := pf32bit;
bmp.Width := Screen.Width;
bmp.Height := Screen.Height;
BitBlt(bmp.Canvas.Handle, 0, 0, bmp.Width, bmp.Height, Desktop, 0, 0, SRCCOPY);
bmp.Modified := True;
Result.Assign(bmp);
finally
ReleaseDC(0, Desktop);
end;
except
bmp.Free;
bmp := nil;
Result.Free;
Result:=nil;
end;
end;
Agora temos que especificar QUANDO o server vai começar a enviar a foto né? Vamos fazê-lo enviar quando o cliente pedir pelo comando "mandafoto". Portanto temos que adicionar uma rotina de verificação no onClientRead e, se o texto recebido for "mandafoto", ele cria o Stream, salva a foto numa variavel TJpegImage, salva essa TJpegImage pro stream, envia o tamanho do stream e, finalmente, envia o stream...
Código: Selecionar tudo
Código PHP:
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
s: string;
Stream: TMemoryStream;
jpg: TJPEGImage;
begin
s:=Socket.ReceiveText;
if s = 'mandafoto' then
begin
Stream:=TMemoryStream.Create;
jpg:=TJPEGImage.Create;
jpg:=GetScreenShot;
jpg.SaveToStream(Stream);
Stream.Position:=0;
Socket.SendText(inttostr(Stream.Size) + #0);
Socket.SendStream(Stream);
end;
end;
Pronto, o Server ja está pronto, agora vamos aprender a fazer o Cliente.
Criem uma nova aplicação e coloque no form um ScrollBox (na aba Additional), uma Image DENTRO desse Scrollbox, um ClientSocket e um botão. Agora declares as seguintes variáveis globais:
Código PHP:
stSize: integer;
Stream: TMemoryStream;
Receiving: boolean;
jpg: TJpegImage;
Código PHP:
procedure TForm1.Button1Click(Sender: TObject);
begin
ClientSocket1.Socket.SendText('mandafoto');
end;
Código PHP:
procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
s: string;
begin
s:=Socket.ReceiveText;
if not Receiving then
begin
if pos(#0,s) > 0 then
stSize:=strtoint(copy(s,1,pos(#0,s)-1))
else
exit;
Stream:=TMemoryStream.Create;
Receiving:=True;
delete(s,1,pos(#0,s));
end;
try
Stream.Write(s[1],length(s));
if Stream.Size = stSize then
begin
Stream.Position:=0;
Receiving:=False;
jpg:=TJPEGImage.Create;
jpg.LoadFromStream(Stream);
Image1.Picture.Assign(jpg);
Stream.Free;
end;
except
Stream.Free;
end;
end;
PS: Lembrando que ao invés de uma imagem, poderia ser utilizado qualquer outro tipo de dados, como um arquivo, apenas utilizem Stream.LoadFromFile('ashdauhsdua.exe'); E lembrem-se que para poder abrir o arquivo é necessário liberar o stream da memória!!
Comment