Lentidão em socket

61 respostas
thiagofesta

Boa tarde,

Estou com um problema em meus sockets, em resumo: lentidão.
Eu faço conexão com ele, e envio básicamente imagens, e alguns comandos curtos do protocolo que criei, acredito que a lentidão está na hora de enviar a imagem, derrepente é muito dado.

Estou utilizando socket TCP. Até mesmo na rede local fica lento, o que vocês me sugerem? Alterar meu protocolo para UDP(queria fazer isso somente se não existisse outra forma mesmo)? ou existe outra forma de compactar melhor as imagens? eu já compactei usando o gzip, eu gero um png, gero o base64 e zipo, ele diminui cerca de 50%.

Obrigado desde já!

61 Respostas

T

a) Qual é a velocidade da sua rede local?
b) Você já checou se os pacotes TCP estão vindo “cheios” ou não (use um analisador de protocolo, como o Network Monitor no Windows ou o Ethereal no Linux?) Isso pode estar ocorrendo porque você está enviando os dados 1 byte de cada vez (por exemplo), em vez de usar buffers maiores.
c) E só em último caso é que é desejável ficar efetuando compressão de dados. O correto é não enviar dados que você já enviou (ou seja, áreas da tela que não foram modificadas não devem ser reenviadas).

T

E não, UDP não vai deixar seu processamento mais rápido. Ele pode ser útil se você precisa fazer um broadcast, por exemplo, mas se você simplesmente vai usar uma conexão ponto-a-ponto, é melhor usar TCP, para não ter de se matar com mensagens perdidas.

thiagofesta

thiagol:
Então, minha rede local é normal, cabeada, 10/100/1000.
Só que a conexão não é ponto a ponto, na verdade tenho uma espécia de HUB que gerencia isso, todos se conectam ao hub, depois envio os dados para o cara certo.
Usando o tcp pelo que pude notar ele envia tudo de uma vez não em byte por byte.
Na hora de enviar, eu dou um println no canal de saída enviando uma string. na hora da leitura eu faço um loop percorrendo todos o que veio do canal de entrada, e adicionando em uma string. Seria dessa forma mesmo, ou tem como otimizar isto?
Agora não se minha imagem que quero enviar é grande demais, ou se o protocolo é lento, eu só sei que UDP seria mais rápido, só que com perdas, mas tu dissestes que não é bom, que seria bom pra broadcast, mas e agora?
Obrigado

king_of_gods

thiagofesta:
Seria dessa forma mesmo, ou tem como otimizar isto?

Coloca uma thread escutando o canal, vai evitar da sua aplicação ficar em loop. E é mais seguro.

Começe enviando mensagens de textos como Oi, Olá, Tudo bem. Se houver uma diferença de tempo brutal, pode ser alguma coisa na rede ou na aplicação.

thiagofesta

King_of_gods:
Eu uso uma thread para ler o socket.

eu ja testei pelo telnet o meu soket como você disse, é instântaneo, não da para notar nenhuma lentidão.

Acho que é na hora de enviar as imagens…

Obrigado!

T

Pelamordedeus, não use sockets em “modo texto”.
Repita comigo e prometa para mim: nunca mais vou usar sockets em “modo texto”.

Use um DataInput/OutputStream e envie o comprimento dos dados, depois a mensagem.

Só com o fato de você ter de mandar em modo texto (e passando para Base-64) já está desperdiçando um monte de banda.

Adicionar em uma string? Com “+=” ? Isso está comendo 110% da sua CPU já que isso é um verdadeiro veneno. Use uma StringBuilder ou StringBuffer se você ainda insistir em usar strings.

thiagofesta

thiagol:
De uma olhada como é meu metódo de enviar e de receber dados:

Escrevendo no socket:
BufferedReader input = new BufferedReader(new InputStreamReader(socketCliente.getInputStream()));
PrintWriter output = new PrintWriter(socketCliente.getOutputStream(), true);
Lendo o socket:
StringBuffer instr = new StringBuffer();
                            BufferedInputStream bis = new BufferedInputStream(socketCliente.getInputStream());
                            InputStreamReader isr = new InputStreamReader(bis);

                            int c;
                            

                                    while( (c = isr.read()) != 10 )
                                    {
                                            instr.append( (char) c);
                                    }


                            PureScreen.debug("Entrada << " + instr,2);

                            return String.valueOf(instr);

O problema então está no meu socket, não em enviar minha imagem em base64?

T

a) Ler um caracter de cada vez? Argh
b) Uma forma besta de enviar dados via socket é encapsulá-lo em um DataOutputStream/DataInputStream. Se você vai mandar uma imagem (um array de bytes de 1 MB por exemplo), é muito mais rápido fazer algo como:

  • Mandar 4 bytes de comprimento (com writeInt
  • Mandar os dados (com write

e para receber, ler os dados com readInt e read

thiagofesta

thiagol:
Certo, mas como ficaria o meu envio?
Vou ter que dar um loop, e ir mandando cada byte? não fica muito lento assim?
E para ler seria a mesma coisa (basicamente, faz um loop e lê todos, e vai deixando em uma string?)

Obrigado pela ajuda!

T

Não é para mandar um byte de cada vez, nem para usar essas classes que terminam com “Reader” ou “Writer”.
Até indiquei quais são os métodos que você tem de usar. Não é para mandar um byte de cada vez; mande a imagem completa de uma vez.

KWill

Apóio a sugestão do thingol, melhor parar de usar um protocolo de controle orientado a comandos de texto. Bote um DataOutputStream numa ponta e um DataInputStream na outra ponta e fique mandando/recebendo comandos via um protocolo de controle através de writeInt, writeShort, writeLong, readInt, readShort, readLong, etc.

Inté.

thiagofesta

Agora entendi…
eu dou la um DataOutputStream(socket.getOutu…).write(Arraay de bytes da minha String).

Mas e a leitura, como seria? o read retorna um inteiro.

Desculpem minha ignorância, sou leigo ainda em java, sei muito pouco.

Obrigado

T

Você lê a quantidade de bytes a serem lidos com readInt, e lê os bytes restantes com read. Por favor, leia a documentação que lhe indiquei.

thiagofesta

thiagol:
Obrigado, mas usei o metódo readLine do inputStream para capturar o que vem, pode ser?

Obrigado

T

NÃO NÃO NÃO USE READLINE - EU JÁ FALEI PARA NÃO USAR MODO TEXTO EM SOCKETS

T

Tá bom, vou explicar mais ou menos a minha idéia.

Ambas as partes devem trocar mensagens, e cada mensagem pode consistir dos seguintes dados:

  • Um indicador de comprimento (mais fácil usar exatamente 4 bytes, para ficar fácil usar writeInt e readInt)
  • Uma indicação do que a mensagem é (por exemplo, 1 = imagem, 2 = controle etc… Se for um código numérico, use writeInt para escrevê-lo, e readInt para lê-lo.)
  • E os dados em si, como um array de bytes (use a versão de write que requer um array de bytes para enviar esses dados.)
ViniGodoy

Um detalhe muitíssimo importante:
O UDP na transferência de arquivos não é mais rápido que TCP.

Então, elimine esse pré-conceito. Há vários motivos para isso:

  1. Transferência de arquivos exige exatamente o que o TCP faz: controle de fluxo, garantia de qualidade, etc. Você teria que implementar esses mesmos serviços no UDP, e inserir o overhead que o TCP já insere;
  2. Os algoritmos usados no TCP já foram muitíssimo otimizados: tanto na performance das operações, quanto no uso de banda;
  3. Os SOs rodam TCP em modo núcleo. Mesmo que vc repita exatamente os mesmos algoritmos sobre o SO, terá um funcionamento mais lento;
  4. Muitas placas de rede implementam vários serviços do TCP em firmware (checksum, por exemplo), para não dizer a pilha TCP básica inteira.

Quando o UDP é mais rápido?
Somente quando vc não precisar dos serviços prestados pelo TCP, como no caso de streaming de vídeo ou voz (onde perdas de pacotes podem ser admitidas), jogos de computadores (que costumam a enviar informação a cada N frames de tela, e portanto, admitem perda de informação intermediária), envio de mensagens simples ou de pânico (um roteador "gritar na rede que está caindo, por exemplo) ou broadcast e multicast.

No resto, o Thingol já te deu o caminho. Não passe dados binários como String.

thiagofesta

thiagol:
Obrigado pela atenção, agora consegui começar a enteder, porém ainda estou com problemas.

int tam = entrada.readInt();
saida.writeInt(3);

saida.write("abc".getBytes());
String texto = "";
for(int i = 0; i < tam; i++)
{
        texto += (char) entrada.read();
        System.out.println(i);
}

System.out.println(texto);

Olha só isso, fiz açgo bem tosco, e rodo isso ai, ae vou no telnet, e do uns enter, ae o i aumenta, se fecho o telnet ae vai num loop infinito.
não sei o que se está ocorrendo :?

ViniGodoy:
Obrigado por está excelente explicação, notei que a idéia minha sobre socket UDP estava muito errada.

T

Quando você usar o protocolo binário, esqueça de usar o telnet para testar. Com certeza vai dar algum pau federal se você mandar dados texto (via telnet) para algo que está atendendo com o protocolo binário.

T

Outra coisa:não posso xingar aqui que senão vou ter problemas. Mas eu já vou avisando: concatenar dados usando uma String e ainda mais com “+=” é pedir para alguém que o leve e para outro alguém que o derrube, se você quer ter desempenho. Não faça isso; leia o raio da documentação e veja que mandar um simples “write” é muito mais simples.

thiagofesta

thiagol:
Não consegui entender muito bem o que você quis dizer, mas ficaria lento pois ia ler caracter por caracter?

olha só o que eu fiz, seria essa a solução, não entendi muito bem o seu "write", não seria read? como eu fiz?
int s = entrada.readInt();
System.out.println(s);

byte[] b = new byte[s];
entrada.read(b, 0, s);
System.out.println( new String(b) );
T

Vou ser obrigado a postar aqui um programinha que faz exatamente o que estou sugerindo. Espere um pouco - e você vai ter de corrigir um monte de erros de socket; vou mostrar apenas o básico do básico.

thiagofesta

Certo, estou no aguardo. e obrigado!

thiagofesta

Estou enviando daquela minha forma, porém, ele só est´[a enviando uma parte da imagem, ai fui ver quanto dava, copiei e colei no notepad e salvei, e deu 64,0kb (65.536 bytes)!

o write bloqueia isso?ou o que será que ta ocorrendo?

thiagofesta

Era aquilo mesmo, diminui a qualidade da imagem, e ela foi… está aparecendo como antigamente, porém a lentidão continua, o server do socket e o cliente estão na minha maquina, naõ pode demorar tanto assim, da um delay muito grande, 60kb não é muito para trafegar, eu envio 2 imagens por segundo, da 120k…

Não sei o que posso fazer, vocês imaginam outra solução? ou é meu socket ainda?

T

Puxa, eu não tive tempo ainda de terminar o exemplo. Você pode esperar mais um pouquinho só?

thiagofesta

Claro, sem problemas!

T

Dê uma olhada neste código.

import java.net.*;
import java.io.*;
import java.util.*;

/**
 * Este é um exemplo bem simples que mostra como transferir um arquivo de uma máquina para outra. 
 * Em uma das máquinas, rode: java -cp . Server
 * Na outra máquina (ou na mesma, se preferir) , rode: java -cp . Client ip arquivo
 * A máquina server irá receber o arquivo, e então gravá-lo em um arquivo "teste.bin" com nome fixo.
 * Deixamos o exemplo bitolado para usar a porta 12345; mude este fonte para usar uma outra porta.
 * Para encerrar o programa Server, é necessário usar Ctrl+Break.
 * O protocolo aqui usado será muito simples: cada solicitação de transferência de arquivo
 * do cliente para o servidor irá ter os seguintes dados:
 * 4 bytes - comprimento
 * 4 bytes - código (iremos usar o código 0 para significar "transferência de arquivo")
 * dados restantes, cujo comprimento foi dado no começo.
 */


class Server {
    private ServerSocket ss;
    private int port;
    public Server (int port) {
        this.port = port;
    }
    public void start() {
        try {
            ss = new ServerSocket (port);
            while (true) {
                final Socket sck = ss.accept();
                new Thread(new Runnable() {
                    public void run() {
                        DataInputStream dis = null;
                            BufferedOutputStream bos = null;
                        try {
                            dis = new DataInputStream (new BufferedInputStream (sck.getInputStream()));
                            int tam = dis.readInt ();
                            int codigo = dis.readInt ();
                            System.out.printf ("Tentando ler %d bytes%n", tam);
                            if (tam < 20000000) { // só para evitar problemas de mensagens muito grandes demais
                                // vamos ler de 1024 em 1024 bytes, até chegar ao tamanho do arquivo desejado. 
                                // Estamos ignorando o código por enquanto
                                byte[] buf = new byte[1024];
                                int bytesRestantes = tam;
                                int bytesLidos;
                                try {
                                    bos = new BufferedOutputStream (new FileOutputStream ("teste.bin"));
                                    while (bytesRestantes > 0) {
                                        bytesLidos = dis.read (buf, 0, Math.min (bytesRestantes, buf.length)); 
                                        if (bytesLidos > 0) {
                                            bytesRestantes -= bytesLidos;
                                            bos.write (buf, 0, bytesLidos);                                        
                                        } else {
                                            break; // na verdade não está muito bom este tratamento de erros...
                                        }
                                    }
                                } catch (IOException ex) {
                                    ex.printStackTrace();
                                } finally {
                                    if (bos != null) try { bos.close(); } catch (IOException ex2) {}
                                }
                            }
                        } catch (IOException ex2) {
                            ex2.printStackTrace();
                        } finally {
                            if (dis != null) try { dis.close(); } catch (IOException ex) {}
                            if (bos != null) try { bos.close(); } catch (IOException ex) {}
                        }
                    }
                }).start();
            }
        } catch (Exception ex3) {
            ex3.printStackTrace();
        }
    }
    public static void main(String[] args) {
        (new Server (12345)).start();
    }
}

class Client {
    int port;
    String ip;
    String arquivo;

    public Client (int port, String ip, String arquivo) {
        this.port = port; this.ip = ip; this.arquivo = arquivo;
    }
    public void start() {
        Socket sck = null;
        BufferedInputStream bis = null;
        DataOutputStream dos = null;
        try {
            sck = new Socket (ip, port);
            bis = new BufferedInputStream (new FileInputStream (arquivo));
            byte[] buffer = new byte[1024];
            dos = new DataOutputStream (new BufferedOutputStream (sck.getOutputStream()));
            dos.writeInt ((int) new File (arquivo).length());
            dos.writeInt (0); // não estou usando o código por enquanto
            int nBytesLidos;
            while (true) {
                nBytesLidos = bis.read (buffer, 0, buffer.length);
                if (nBytesLidos <= 0)
                    break;
                dos.write (buffer, 0, nBytesLidos);
            }
       } catch (IOException ex) {
        } finally {            
            if (dos != null) try { dos.close(); } catch (IOException ex) {}
            if (bis != null) try { bis.close(); } catch (IOException ex) {}
        }
    }
    public static void main(String[] args) {
        (new Client (12345, args[0], args[1])).start();
    }
}
thiagofesta

thiagol:
Obrigado ai, isso com certeza não quebrou só esse meu problema, resolveu outros 2 de uma vez :slight_smile: sou grato demais!

certo, tenho outra dúvida, eu estou usando um JLabel para exibir as imagens recebidas, cerca de 2 por segundo, mas pretendo colocar mais, caso consiga compactar elas futuramente.
Você sabe me dizer se usando JLbal tem muito delay? é muito lento para ficar fazendo está troca de imagem? se sim, qual a melhor forma?

ViniGodoy

A forma mais rápida de desenhar imagens na tela sucessivamente é através da pintura direta, através da classe BufferStrategy, usando a API Graphics2D. Essa forma atualiza a tela imediatamente, dispensando o mecanismo de eventos do Swing.

Agora, 2 vezes por segundo é uma taxa muitíssimo baixa. Aliás, até umas 10 vezes por segundo é baixa. Não precisa de uma classe super otimizada para chegar nisso.

Pode usar o JLabel tranquilo, ou o JImagePanel.

thiagofesta

ViniGodoy:
Então a posibilidade de ser a lentidão de exibição na tela está descartada? caso seja duas imagens por segundo? ou até mesmo dez?

Então, supondo que eu tenha que enviar 2 imagens por segundo, cada imagem de 60kb, ou seja, 120kb por segundo. a internet com um up de 500kbits não ia ser lento? pois o envio é de ~50kb/s. ai pensando nisso, vira uma salada tratando-se de envio em tempo real das imagens, pois pensem comigo:

Exemplo:
1 - enviei 10 imagens em 5 segundos, porém o cara recebeu nesses 10 segundos apenas 5 imagens, mas existe ainda 5 imagens para receber, dando um delay de 2,5 segundo (correto?)

Agora imagine com o passar do tempo, isso vai virando um amaranhado de informações atrasada. Será que é isso que ocorre comigo?

eu fiz um teste anteriormente, enviando 10 imagens por segundo (isto local, no meu pc rodando o server e o cliente), e exibia a hora que ele recebia.
notei que recebia no máximo 4 imagens por segundo, algumas vezes 3, ou seja, acredito que as outras 6 iriam receber posteriormente, ocasionando o delay.
Pensando nisto, botei 4, ah, se com 10 mandou 4, vou mandar 4 e naão vai dar problema. Fiz isto, ai enviava cerca de 3 por segundo, dando 1 de delay. Porém ainda demora muito para exibir, acho que cerca de 1/2 segundo para exibir o dado real. Mas como estou no meu pc, não poderia existir isto.

Fiz até um teste com o código que o thiagol mandou, testei com 19mb ±, foi um tiro, na hora, e porque será que com as imagens fica está lentidão?
o socket meu está igual, porém não divido por partes de 1024 bytes, pois 60kb não é muito.

O que me dizem?

ViniGodoy

Eu estava falando em termos gráficos. Para a placa de vídeo mais vagaba hoje em dia, 10 FPS é uma taxa baixíssima.

Não em termos da rede. No caso da rede, você terá que trabalhar com buffers para esconder esse delay. É o mesmo que o seu explorer faz… carrega umas 20 imagens antes de começar a exibir.

Então, enquanto o cara assiste o vídeo, você carrega o resto das imagens que faltam… e torce para dar tempo de carregar tudo antes que ele termine de assistir.

dedspr

thingol

E se eu quiser em vez de enviar bytes para o socket enviar string do byte tem como por aquele código que tu postou…
Eu sei que tem no post que é ruim enviar string para o socket.,… só que meu socket é em php e não tem como enviar byte… ou seja estou no osso…kkkkk

Tem como fazer isso, pois eu não consegui… tentei enviar assim mais não deu muito certo…

ByteArrayOutputStream bout = new ByteArrayOutputStream();    
  
bis = new BufferedInputStream (new FileInputStream (arquivo));  
byte[] buffer = new byte[1024];  
   
ByteArrayOutputStream bout = new ByteArrayOutputStream();    
DataOutputStream dout = new DataOutputStream( bout );    
               
int nBytesLidos;  
while (true) {  
nBytesLidos = bis.read (buffer, 0, buffer.length);  
if (nBytesLidos <= 0)  
     break;  
   
dout.write(buffer, 0, nBytesLidos);  
String meubyte = Base64.encode(bout.toByteArray());  
                  
 //ENVIA MEU BYTE STRING PARA O SOCKET  
 }

SOCKET SERVER NO PHP É MUITO RUIM MAS EU NÂO POSSO TROCAR TEM QUE SER NO PHP

ViniGodoy

De acordo com a documentação do PHP, você pode transmitir dados normalmente via socket. Só vai precisar mudar o tipo da leitura para PHP_BINARY_READ.

Eu nunca fiz isso pessoalmente, mas já fiz um programa Java que enviava dados para o PHP exatamente como o Thingol comentou. E funcionava perfeitamente.

dedspr

Certo ViniGodoy tem como sim eu não tinha visto… mas acontece o seguinte no meu socket php não é igual no socket do java que para cada conexão cria uma thread, no php ele tem um somente loop para todos os clientes conectados… e guardo os socket dos clientes em sessões de id’s e identifico para quem tenho que mandar apartir do id que o cliente tem, ou seja passo sempre para o socket um XML contendo os dados e por isso não consigo passar os bytes… para mim tem que ser string pois senão não sei para quem enviar a mensagem do socket… você conhece algum outro jeito de fazer socket no PHP? porque já sofri e estou sofrendo com esse socket…

ViniGodoy

A maneira ideal de usar sockets no java é através do java.nio.channels, com selector e uma única thread.

Na verdade, você precisa estabelecer um protocolo de comunicação. Nada mais é do que o que o Thingol fez, quando definiu que o primeiro int definia o tamanho da mensagem e o segundo o tipo.

No seu caso, vc também teria que passar o ID do cliente.

Mas, se vc basear em XML, nesse caso, use as classes de reader e writer de textos comum. Você só terá que descobrir o encoding do lado do PHP. Isso é extremamente ineficiente em termos de banda, dependendo do tipo de mensagem que vc troque. Mas, se até a sinalização na telefonia SIP pode ser baseada em texto, por que não as mensagens de uma aplicação não poderiam?

dedspr

Essa é a minha dificuldade ViniGodoy…

Como eu vou transferir um arquivo de um socket client em java para um socket server em php baseado em string…

ViniGodoy

O primeiro passo é encapsular o seu socket num PrintWriter:

PrintWriter pw = new PrintWriter(socket.getOutputStream());

Depois, dê println nas strings que quer enviar:

pw.println("Enviando...");

Finalmente, dê flush() e feche o seu socket:

pw.flush(); pw.close();

dedspr

Certo só que minha dúvida é como transformar um byte lido em string par aenvia-lo…

tentei fazer assim, mas não deu certo

ByteArrayOutputStream bout = new ByteArrayOutputStream();      
     
 bis = new BufferedInputStream (new FileInputStream (arquivo));    
 byte[] buffer = new byte[1024];    
      
 ByteArrayOutputStream bout = new ByteArrayOutputStream();      
 DataOutputStream dout = new DataOutputStream( bout );      
                  
 int nBytesLidos;    
 while (true) {    
 nBytesLidos = bis.read (buffer, 0, buffer.length);    
 if (nBytesLidos <= 0)    
      break;    
      
 dout.write(buffer, 0, nBytesLidos);    
 String meubyte = Base64.encode(bout.toByteArray());    
                     
 //ENVIA MEU BYTE STRING PARA O SOCKET    
 }
KWill

thiagofesta:
ViniGodoy:
Então a posibilidade de ser a lentidão de exibição na tela está descartada? caso seja duas imagens por segundo? ou até mesmo dez?

Então, supondo que eu tenha que enviar 2 imagens por segundo, cada imagem de 60kb, ou seja, 120kb por segundo. a internet com um up de 500kbits não ia ser lento? pois o envio é de ~50kb/s. ai pensando nisso, vira uma salada tratando-se de envio em tempo real das imagens, pois pensem comigo:

Exemplo:
1 - enviei 10 imagens em 5 segundos, porém o cara recebeu nesses 10 segundos apenas 5 imagens, mas existe ainda 5 imagens para receber, dando um delay de 2,5 segundo (correto?)

Agora imagine com o passar do tempo, isso vai virando um amaranhado de informações atrasada. Será que é isso que ocorre comigo?

eu fiz um teste anteriormente, enviando 10 imagens por segundo (isto local, no meu pc rodando o server e o cliente), e exibia a hora que ele recebia.
notei que recebia no máximo 4 imagens por segundo, algumas vezes 3, ou seja, acredito que as outras 6 iriam receber posteriormente, ocasionando o delay.
Pensando nisto, botei 4, ah, se com 10 mandou 4, vou mandar 4 e naão vai dar problema. Fiz isto, ai enviava cerca de 3 por segundo, dando 1 de delay. Porém ainda demora muito para exibir, acho que cerca de 1/2 segundo para exibir o dado real. Mas como estou no meu pc, não poderia existir isto.

Fiz até um teste com o código que o thiagol mandou, testei com 19mb ±, foi um tiro, na hora, e porque será que com as imagens fica está lentidão?
o socket meu está igual, porém não divido por partes de 1024 bytes, pois 60kb não é muito.

O que me dizem?

Para mim isso está parecendo problema de sincronismo. Sugiro que você controle o envio e recebimento de imagens, de forma que só seja enviada uma imagem quando necessário, ou melhor, quando uma das pontas “pedir” por uma atualização de imagem.

Inté.

T

De qualquer maneira, é interessante ler este artigo, escrito pelos autores do VNC.

Ele explica quais são os eventuais problemas que você pode ter ao fazer essa sua solução de controle remoto.

C

ViniGodoy e Thingol,

Vcs acham possivel fazer um jogo de acao simples (tipo ping pong com graficos do atari) onde os jogadores sao sincronizados (via socket) ao jogo que se passa num processo do servidor?

Essa thread no servidor processa o estado do jogo; a cada iteracao atualiza o estado de acordo com os eventos dos jogadores obtidos via inputstream, checa colisao e por fim (da iteracao) escreve no ouputstream a posicao dos elementos do jogo para serem renderizados localmente; Cada iteracao um frame, tudo sincronizado.

Neste caso um jogo bem simples graficicamente numa rede local conseguiria o minimo de jogabilidade que um jogo de acao necessita?

thiagofesta

KWill:
Eu já faço uma verificação se a imagem alterou e tal, mas sei que ainda é muito simples, vou criar mais meios de enviar o quando menos possivel.

thingol:
Obrigado, era isto mesmo o que eu estava procurando ontem no final da tarde, mas não encontrava.
Vou dar uma lida e entender.

Obrigado!

cmoscoso:
Olha só, não sou muito bom a falar sobre isto, mas vou chutar hehe, pode ser que eu fale abóbrinha. Mas acho que é possivel sim, desde que você envie muito pouco dado, acredito que sim, mas não deve ser muito simples.

Ahh, outra perguntinha: Vocês conhecem uma API que faça tratamento de imagens? preciso de algo que deixe a imagem com 256 cores, sei lá, diminua o depth para 16 bits, ou 8 bits (não entendo muito bem disso).

thiagofesta

Segundo o texto:

The lowest common denominator is the so-called raw encoding, where the pixel data for a rectangle is simply sent in left-to-right scanline order. All VNC clients and servers must support this encoding. However, the encodings actually used on a given connection can be negotiated according to the capabilities of the server and client and the connection
between them.

For example, copy-rectangle encoding is very simple and efficient, and can be used when the client already has the same pixel data elsewhere in its framebuffer. The encoding on the wire is simply an x, y coordinate. This gives a position in the framebuffer from which the client can copy the rectangle of pixel data. This encoding is typically used when the user moves a window across the screen or scrolls a window?s contents.

Não tenho um inglês muito bom, e o Google tradutor não ajudou muito hhehe, mas pelo que pude notar, ele envia a imagem inteira, depois só envia as partes que alterou, passando as coordenadas? seria isto?

KWill

thingol:
De qualquer maneira, é interessante ler este artigo, escrito pelos autores do VNC.

Ele explica quais são os eventuais problemas que você pode ter ao fazer essa sua solução de controle remoto.


Caramba, esse aí eu também estava procurando loucamente. Pretendo dar uma olhada e ver o que consigo otimizar no meu aplicativo de administração remota.

Inté.

KWill

thiagofesta:
KWill:
Eu já faço uma verificação se a imagem alterou e tal, mas sei que ainda é muito simples, vou criar mais meios de enviar o quando menos possivel.

Lendo sobre a forma como o protocolo VNC trabalha, descobri que o aplicativo que fiz seguia um pouco as idéias do VNC, sendo que implementei de uma forma parecida com VNC no meu aplicativo sem ter conhecimento sobre como o protocolo VNC trabalha. Sugiro você implemente um modelo de funcionamento em que as atualizações só sejam feitas e enviadas pelo servidor ao cliente caso o cliente requisite essas atualizações. Não estou falando apenas de checar se houve alterações de imagem no servidor para enviar atualizações, sugiro que tais alterações só sejam checadas e enviadas caso o cliente requisite ao servidor.

Inté.

T

Há mais coisas sobre o VNC. mas é questão de procurar. Eu citei esse artigo porque ele é bem curtinho. (Os outros artigos eu até li, mas não estou mais encontrando. Mas se você procurar pelos autores do artigo, provavelmente irá achá-los.)

thiagofesta

KWill, eu vi o seu SATAN Anywhere…
Show de bola, muito bom mesmo, espero que o meu fique como ele, achei muito complexo os códigos, demoraria mais para mim tentar entender do que criar algo parecido.

thingol:
Valeu pelo artigo lá, vou procurar e entender muito bem isto.

Vou me matar aqui nos códigos :slight_smile: logo em breve volto para dizer a solução e o que está de errado, não sei quanto tempo vou demorar, mas logo logo volto :slight_smile:

thiagofesta

Voltei…

Fiz um projeto novo para teste, onde criei um socket server e cliente…

Em meu pc ele funciona normal, mas quando coloco o server ou o cliente em outro pc ele não funciona :?

Bem, na verdade ele só funciona ese eu for debugando ai ele funciona :?

Alguém faz idéia do motivo?

KWill

thiagofesta:
Voltei…

Fiz um projeto novo para teste, onde criei um socket server e cliente…

Em meu pc ele funciona normal, mas quando coloco o server ou o cliente em outro pc ele não funciona :?

Bem, na verdade ele só funciona ese eu for debugando ai ele funciona :?

Alguém faz idéia do motivo?

Esse seu comentário “no meu pc funciona normal, em outro não funciona” tá muito “raso”. Primeiro, tu deve tentar estabelecer ou descobrir a partir de que ponto a coisa pára de funcionar. Por exemplo, as duas máquinas podem nem estar enxergando uma à outra na rede ou por alguma razão tu anda errando as portas TCP de escuta/estabelecimento de conexão e por aí vai.

Veja se dá para postar algum código para darmos uma olhada.

Inté.

thiagofesta

Certo, fiz ele bem tosco, tosco mesmo!!!

Notei que certa hora ele para de enviar, fica lento o envio, não sei bem o motivo, vejam o código:

Servidor:
if(this.imagemOld != null)
{
        WritableRaster raster, rasterOld;
        int[] linha = new int[WIDTH*3], linhaOld = new int[WIDTH*3];

        raster = this.imagem.getRaster();
        rasterOld = this.imagemOld.getRaster();


        // Percorrendo linha por linha
        for(int i = 0; i < HEIGHT; i++)
        {
                raster.getPixels(0, i, WIDTH, 1, linha);
                rasterOld.getPixels(0, i, WIDTH, 1, linhaOld);

                // Alteração nesta linha
                if(!Arrays.equals(linha, linhaOld))
                {
                        // Envia a linha alterada...
                        System.out.println("Alterou a linha: " + i);

                        this.enviaInt(1);
                        this.enviaInt(WIDTH*3);
                        this.enviaInt(i);
                        this.enviaArrayInt(linha);


                        //raster.setPixels(0, i, WIDTH, 1, linha);
                }

        }

        this.imagem.setData(raster);

        this.enviaInt(2);
        System.out.println("SAIU");
}
Agora a parte do cliente:
DataInputStream entrada = new DataInputStream(socketCliente.getInputStream());
DataOutputStream saida = new DataOutputStream(socketCliente.getOutputStream());

WritableRaster raster = this.imagem.getRaster();

while(true)
{
        if(entrada.readInt() != 1)
        {
            break;
        }

        int tam = entrada.readInt();
        int line = entrada.readInt();

        System.out.println("Alterou a linha: " + line);
        System.out.println("Tamanho da linha: " + tam);

        byte[] b = new byte[tam];
        entrada.read(b, 0, tam);


        int[] linha = byteArrToIntArr(b);


        raster = this.imagem.getRaster();
        raster.setPixels(0, line, tam/3, 1, linha); 

}

this.imagem.setData(raster);

this.label.setIcon( new ImageIcon(this.imagem) );

Me desculpem antes criar o post meio sem noção.

KWill

Tu tá se lembrando de chamar “flush()” nos OutputStream’s?

Inté.

T

Outra coisa legal para você usar, se seu protocolo é “half-duplex” (ou seja, você espera uma resposta do servidor para enviar mais coisas) é usar a opção “TCP_NODELAY” do socket (setTcpNoDelay ao criá-lo.

thiagofesta

thingol:
Estou usando esse setTcpNoDelay como true :slight_smile:

KWill:
Nem lembrei dos flush(), vou fazer isto.

Obrigado!

thiagofesta

Olha só,
coloquei os flush(), mas mesmo assim não houve sucesso, nem local está 100%

As vezes fica uma lentindão na hora do envio, e recebimento, dei uns System println, pra ver o que era, e é isso…
será que é meu while true?

thiagofesta

Eu notei uma coisa, quando eu envio os bytes da imagem, o problema ocorre, se eu enviar somente os numeros que uso para controle, funciona perfeitamente, mas seu eu mandar a imagem da pau, o erro é aqui:

this.enviaArrayInt(linha);

//EnviaArrayInt é essa função:

    public void enviaArrayInt(int[] buffer)
    {
            try
            {
                    this.saida.write( intArrToByteArr(buffer) );
                    this.saida.flush();
            }
            catch(Exception e)
            {
                    e.printStackTrace();
            }
    }

// A função intArrToByteArr
private byte[] intArrToByteArr(int[] buffer)
{
       byte bytePixels[] = new byte[buffer.length];
       
       for(int i = 0; i < buffer.length; i++)
       {
               bytePixels[i] = (byte)buffer[i];
       }
       return bytePixels;
}

Ele para em um ponto e não envia mais, não faço idéia do motivo :?
Alguém sabe?

T
private byte[] intArrToByteArr(int[] buffer)  
 {  
        byte bytePixels[] = new byte[buffer.length];  
          
        for(int i = 0; i < buffer.length; i++)  
        {  
                bytePixels[i] = (byte)buffer[i];  
        }  
        return bytePixels;  
 }

Esta função cheira super-mal - tem certeza que cada pixel é só de 8 bits, e você pode descartar os outros 16 ou 24 bits?

thiagofesta

thingol, agora você me pegou, eu não entendo muito bem sobre byte.
Como sei se um pixel tem mais que 8 bits?

eu sei que meus inteiros não passam de 255

KWill

thiagofesta:
thingol, agora você me pegou, eu não entendo muito bem sobre byte.
Como sei se um pixel tem mais que 8 bits?

eu sei que meus inteiros não passam de 255

No código do SATAN-ANYWHERE, veja na classe “SAWAWTControlProvider” como é que eu implementei formas de se fazer screenshots com paleta de cores de 8 bits indexada com 216 cores ou com paleta de cores de 32 bits não-indexada. Depois de instanciar a classe “SAWAWTControlProvider”, chame algum dos métodos inicializadores de recursos como “initializeLowQualityScreenshot()” para screenshots com baixa qualidade de cor e/ou “initializeHighQualityScreenshot()” para screeenshots com alta qualidade de cor. “createLowQualityScreenCapture(boolean drawPointer)” cria screenshots de baixa qualidade de cor, sendo que os dados dos pixels no BufferedImage retornado estão guardados na forma de array de bytes e cada posição do array de bytes representa um pixel, enquanto “createHighQualityScreenCapture(boolean drawPointer)” cria screenshots de alta qualidade de cor, sendo que os dados dos pixels no BufferedImage retornado estão guardados na forma de array de ints e cada posição do array de ints representa um pixel.
Lembre-se de chamar “dispose()” quando a instância do “SAWAWTControlProvider” não for mais necessária.

Inté.

thiagofesta

KWill, obrigado, vou dar uma olhada mais pra frente nisto.

Meu problema definitivamente é com os sockets, nem cheguei a ser ainda com o tamanho da imagem.

Olhem só, fiz um socket muito simples, como mostrei antes ali em cima, mas estava cheio de bugs, debuguei ele hoje a manhã toda e até agora a tarde e descobri onde estava o desgraçado do erro, porém não descobri o motivo.

O Erro ocorre somente quando eu faço o envio de array de bytes, se vocês verem ali, eu converto meu array de int pra array de bytes e envio, o problema mora ai, no envio desses array de bytes, não faço idéia, mas é! ou na conversão também.

Pensando nisso, eu tentei fazer uma proesa, fiz algo assim:

for(int k = 0; k < WIDTH*3; k++) { this.enviaInt(linha[k]); }

Ali eu estou enviando cada inteiro da linha para o servidor, ou seja, envio número por número, quando chega no cliente, eu pego e adiciono denovo em um array de inteiros. Do lado do cliente eu botei um “cronometro”, para ver quanto tempo ele leva para ler 4440 inteiros, e o tempo foi assustador, foi de 0,36 em média, ou seja, uma tela de 1024 linhas irá demorar cerca de 6 minutos e uns quebrados.

Eu não sei o que fazer mais, tentei quase tudo, comecei do zero várias vezes, fiz de várias formas, e não consigo.

o que posso fazer?

thiagofesta

Bugs resolvidos…

Bem na verdade não todos, estou com um problema agora.
O socket funciona perfeitamente na minha maquina (eu como server e cliente), se eu mando o cliente para um pc na minha rede local cabeada/wireless não funciona, bem, na verdade funciona, só que não normal, fica cheio de bug, ele para de enviar.

O que pode ocasionar isto?
Como pode funcionar na minha maquina, eu uso essa faixa de ips: 192.168.150.12 (minha maquina, como uso para o cliente acessar) funciona normal se rodo do meu pc. quanto tento acessar de qualquer IP, como por exemplo: 192.168.150.14 já na funciona.
Notei que o problema está do lado do server, quando uso telnet, ele conecta tudo, mas chega a um ponto e trava.

Obrigado!

Criado 19 de agosto de 2008
Ultima resposta 27 de ago. de 2008
Respostas 61
Participantes 7