Este tutorial é uma introdução para a tomada de programação em Java, começando com um simples cliente-servidor de exemplo que demonstram as características básicas do Java I/O. Você vai ser apresentado a original
java.io
pacote e NIO, o non-blocking I/O (java.nio
) APIs introduzidos no Java 1.4. Finalmente, você verá um exemplo que demonstra a rede Java como implementada a partir do Java 7 forward, no NIO.2.,
Socket programming boils down to two systems communicating with one another. Geralmente, a comunicação de rede vem em dois sabores: Protocolo de controle de transporte (TCP) e Protocolo de datagrama de usuário (UDP). TCP e UDP são usados para diferentes propósitos e ambos têm restrições únicas:
- TCP é um protocolo relativamente simples e confiável que permite que um cliente faça uma conexão com um servidor e os dois sistemas se comuniquem. No TCP, cada entidade sabe que as suas cargas de comunicação foram recebidas.,
- UDP é um protocolo sem conexão e é bom para cenários onde você não precisa necessariamente de cada pacote para chegar ao seu destino, tais como streaming de mídia.
para apreciar a diferença entre TCP e UDP, considere o que aconteceria se você estivesse transmitindo o vídeo do seu site favorito e ele deixasse cair as molduras. Prefere que o cliente atrase o seu filme para receber as imagens em falta ou prefere que o vídeo continue a tocar? Protocolos de streaming de vídeo tipicamente alavancam UDP., Como o TCP garante a entrega, é o protocolo de escolha para HTTP, FTP, SMTP, POP3, e assim por diante.
neste tutorial, introduzo-o à programação de ‘socket’ em Java. Eu apresento uma série de exemplos cliente-servidor que demonstram características do framework Java I/O original, em seguida, gradualmente avançar para o uso de recursos introduzidos no NIO.2.
java sockets
em implementações anteriores a NIO, o código de Soquete cliente TCP Java é tratado pela classejava.net.Socket
., O seguinte código abre uma ligação a um servidor:
Socket socket = new Socket( server, port );
Uma vez que a nossa socket
instância está ligada ao servidor que podemos começar a obter fluxos de entrada e saída para o servidor. Fluxos de entrada são usados para ler dados do servidor, enquanto fluxos de saída são usados para escrever dados para o servidor., Podemos executar os seguintes métodos para obter fluxos de entrada e saída:
InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();
como esses são comuns fluxos, a mesma fluxos que vamos usar para ler e escrever em um arquivo, que pode convertê-los para o formato que melhor serve o nosso caso de uso. Por exemplo, poderíamos embrulhar o OutputStream
com um PrintStream
para que possamos facilmente escrever texto com métodos comoprintln()
., Para outro exemplo, poderíamos moldar o InputStream
com um BufferedReader
, através de uma InputStreamReader
, a fim de facilitar a leitura de texto com métodos como o readLine()
.
Java socket client example
Let’s work through a short example that executes an HTTP GET against an HTTP server., HTTP é mais sofisticado do que o nosso exemplo permite, mas podemos escrever código cliente para lidar com o caso mais simples: solicitar um recurso do servidor e o servidor retorna a resposta e fecha o fluxo. Este caso requer os seguintes passos:
- crie um ‘socket’ para o servidor web ouvir na porta 80.
- Obter uma
PrintStream
para o servidor e enviar o pedido deGET PATH HTTP/1.0
, ondePATH
é o recurso solicitado no servidor., Por exemplo, se quiséssemos abrir a raiz de um site web, então o caminho seria/
. - obtém um
InputStream
para o servidor, embrulhe-o com umBufferedReader
e leia a resposta linha-a-linha.
Listagem 1 mostra o código fonte para este exemplo.Lista 1. SimpleSocketClientExample.java
listar 1 Aceita dois argumentos da linha de comandos: o servidor a que se ligar (assumindo que estamos conectando ao servidor na porta 80) e o recurso a recuperar., Ele cria um Socket
que aponta para o servidor e especifica explicitamente o port 80
. Em seguida, ele executa o comando:
GET PATH HTTP/1.0
Por exemplo:
GET / HTTP/1.0
o Que aconteceu?
Quando você recuperar uma página da web de um servidor web, como o , o cliente HTTP utiliza servidores de DNS para localizar o endereço do servidor: ele começa perguntando o domínio de nível superior do servidor para o
com
domínio onde a autoridade do nome de domínio do servidor é para o ., Em seguida, ele pede que o servidor de nome de domínio para o endereço IP (ou endereços) para
. Em seguida, ele abre um socket para aquele servidor na porta 80. (Ou, se quiser definir um porto diferente, pode fazê-lo adicionando um ponto seguido pelo número do porto, por exemplo:
:8080
. Finalmente, o cliente HTTP executa o especificado método HTTP, como GET
POST
PUT
DELETE
HEAD
, ou OPTI/ONS
. Cada método tem a sua própria sintaxe., Como mostrado no código snips acima, o método GET
requer um caminho seguido por HTTP/version number
e uma linha vazia. Se quiséssemos adicionar cabeçalhos HTTP poderíamos tê-lo feito antes de entrar na nova linha.
Na Listagem 1, é obtido um OutputStream
e envolveu-o em um PrintStream
para que pudéssemos mais facilmente executar o nosso texto-base de comandos., O nosso código obtido um InputStream
, que envolto em um InputStreamReader
, que é convertido para um Reader
e, em seguida, envolto que em um BufferedReader
. Usamos o PrintStream
para executar a nossa GET
método e, em seguida, usou o BufferedReader
para ler a resposta, linha por linha, até que recebemos um null
resposta, indicando que o soquete tinha sido fechado.,
execute agora esta classe e passe – a para os seguintes argumentos:
java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com /
deverá ver um resultado semelhante ao que está abaixo:
esta saída mostra uma página de teste na página web do JavaWorld. Ele respondeu que fala a versão 1.1 HTTP e a resposta é 200 OK
.
Java socket server example
cobrimos o lado cliente e, felizmente, o aspecto de comunicação do lado servidor é igualmente fácil., De uma perspectiva simplista, o processo é o seguinte:
- criar um
ServerSocket
, especificando uma porta para ouvir. - invoque o método
ServerSocket
‘saccept()
método para ouvir no Porto configurado para uma ligação ao cliente. - When a client connects to the server, the
accept()
method returns aSocket
through which the server can communicate with the client., Este é o mesmoSocket
classe que utilizamos para o nosso cliente, para que o processo é o mesmo: obter umInputStream
para ler a partir do cliente e umaOutputStream
escrever para o cliente. - Se o servidor necessitar de ser escalável, irá querer passar o
Socket
para outro tópico a processar de modo a que o seu servidor possa continuar a ouvir as ligações adicionais. - Call the
ServerSocket
‘saccept()
method again to listen for another connection.,
Como você verá em breve, o tratamento da NIO neste cenário seria um pouco diferente., Por agora, contudo, podemos criar directamente uma ServerSocket
, passando por uma porta para escutar (mais sobre ServerSocketFactory
s na próxima seção):
ServerSocket serverSocket = new ServerSocket( port );
E agora, nós pode aceitar conexões de entrada através de accept()
método:
Socket socket = serverSocket.accept();// Handle the connection ...
de programação Multithread com Java sockets
a Listagem 2, abaixo, coloca todo o código do servidor para longe, juntos, em um pouco mais robusto exemplo que usa threads para lidar com vários pedidos., O servidor mostrado é um servidor echo, o que significa que ele ecoa qualquer mensagem que recebe.
embora o exemplo na lista 2 não seja complicado, ele antecipa alguns do que está chegando na próxima seção sobre NIO. Preste especial atenção à quantidade de código de threading que temos que escrever, a fim de construir um servidor que pode lidar com vários pedidos simultâneos.
Listagem 2. SimpleSocketServer.java
na lista 2 criamos uma nova instância SimpleSocketServer
e iniciamos o servidor., Isso é necessário porque o SimpleSocketServer
estende-se Thread
para criar uma nova thread para lidar com o bloqueio de accept()
chamada que você vê na read()
método. The run()
method sits in a loop accepting client requests and creating RequestHandler
threads to process the request. Mais uma vez, este é um código relativamente simples, mas também envolve uma quantidade razoável de programação roscada.,
Note-se também que o RequestHandler
processa a comunicação com o cliente muito parecido com o código na Listagem 1 fez: ele encapsula o OutputStream
com um PrintStream
para facilitar escreve e, da mesma forma, molda-se o InputStream
com um BufferedReader
para leituras fácil. No que diz respeito a um servidor, ele lê linhas do cliente e ecoa-as de volta para o cliente. Se o cliente enviar uma linha vazia, então a conversa acabou e o RequestHandler
fecha o socket.
NIO e NIO.,2
para muitas aplicações, o modelo base de programação de socket Java que acabamos de explorar é suficiente. Para aplicações que envolvam I/O mais intensivo ou saída assíncrona, você vai querer estar familiarizado com as APIs não-bloqueantes introduzidas em NIO Java e NIO.2.
O pacote JDK 1.4 NIO oferece as seguintes características principais:
- canais são projetados para suportar transferências por grosso de um tampão NIO para outro.os Buffers representam um bloco contíguo de memória interfaceado por um simples conjunto de operações.,
- Entrada/Saída não-bloqueante é um conjunto de classes que expõem os canais a fontes de E/S comuns, como ficheiros e ‘sockets’.
ao programar com o NIO, você abre um canal para o seu destino e, em seguida, lê os dados em um buffer a partir do Destino, escreve os dados para um buffer, e envia isso para o seu destino., Vamos mergulhar em configuração de uma tomada e a obtenção de um canal para ele em breve, mas primeiro vamos rever o processo de utilização de uma memória intermédia:
- Gravar dados em um buffer
- Chamada do buffer
flip()
método para prepará-lo para a leitura - Leia os dados do buffer
- Chamada do buffer
clear()
oucompact()
método para prepará-lo para receber mais dados
Quando os dados são gravados no buffer, buffer sabe a quantidade de dados gravados nela., Ele mantém três propriedades, cujos significados são diferentes se o buffer está no modo de leitura ou de gravação, o modo de:
- Posição: No modo de gravação, a posição inicial é 0 e mantém a posição atual a ser escrito no buffer; depois de virar um buffer para colocá-lo no modo de leitura, ele redefine a posição a 0 e mantém a posição actual no buffer a ser lida, a
- Capacidade: Fixa o tamanho do buffer
- Limite: No modo de gravação, o limite define como a quantidade de dados que pode ser escrito na memória intermédia; no modo de leitura, o limite define como a quantidade de dados que pode ser lido do buffer.,
java I / O demo: Echo server with NIO.2
NIO.2, que foi introduzido no JDK 7, estende as bibliotecas de I/O não-bloqueantes do Java para adicionar suporte para tarefas do sistema de arquivos, como a classe
pacote ejava.nio.file.Path
e expõe uma nova API do sistema de arquivos. Com esse fundo em mente, vamos escrever um novo servidor Echo usando NIO.2’sAsynchronousServerSocketChannel
.
o AsynchronousServerSocketChannel
fornece um canal assíncrono não bloqueador para soquetes de escuta orientados para fluxo., A fim de usá-lo, primeiro executamos seu método estático open()
e então bind()
ele para uma porta específica. Em seguida, vamos executar o seu método accept()
, passando para ele uma classe que implementa o CompletionHandler
interface. Na maioria das vezes, você vai encontrar esse manipulador criado como uma classe interna anônima.
Listagem 3 mostra o código fonte do nosso novo servidor Echo assíncrono.
Listagem 3. SimpleSocketServer.,java
Na Listagem 3 primeiro criamos um novo AsynchronousServerSocketChannel
e, em seguida, vinculá-lo à porta 5000:
final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
a Partir deste AsynchronousServerSocketChannel
, invocamos accept()
para dizer a ele para iniciar a escuta de ligações, passando para ele um personalizado CompletionHandler
instância. Quando invocamos accept()
, ele retorna imediatamente., Note que este exemplo é diferente de ServerSocket
classe na Listagem 1; considerando que o accept()
método bloqueados até que um cliente conectado a ele, o AsynchronousServerSocketChannel
accept()
método processa-lo por nós.
O manipulador de conclusão
o Nosso próximo responsabilidade é a de criar uma CompletionHandler
classe e fornecer uma implementação de completed()
e failed()
métodos., completed()
método é chamado quando o AsynchronousServerSocketChannel
recebe uma ligação de um cliente e ele inclui um AsynchronousSocketChannel
para o cliente. The completed()
method first accepts the connection from the AsynchronousServerSocketChannel
and then starts communicating with the client. A primeira coisa que ele faz é escrever uma mensagem de “Olá”: ele constrói uma cadeia, Converte – a em uma matriz de bytes, e então passa-a para ByteBuffer.wrap()
para construir um ByteBuffer
., ByteBuffer
pode ser transmitido AsynchronousSocketChannel
‘s write()
método.
Para ler a partir do cliente, vamos criar uma nova ByteBuffer
invocando sua allocate(4096)
(que cria um buffer de 4 k), então vamos chamar o AsynchronousSocketChannel
‘s read()
método. read()
retorna Future<Integer>
no que podemos chamar get()
para obter o número de bytes lidos a partir do cliente., Neste exemplo, nós passamos get()
um valor de tempo limite de 20 segundos: se não obter uma resposta em 20 segundos, em seguida, o get()
método irá lançar um TimeoutException
. A nossa regra para este servidor echo é que se observarmos 20 segundos de silêncio, terminaremos a conversa.em seguida, verificamos a posição do buffer, que será a localização do último byte recebido do cliente. Se o cliente enviar uma linha vazia, então nós recebemos dois bytes: um carriage return e um line feed., A verificação garante que, se o cliente enviar uma linha em branco que tomamos como um indicador de que o cliente está terminado com a conversa. Se temos dados significativos, então chamamos o método ByteBuffer
‘sflip()
para prepará-lo para leitura. Nós criamos um array de bytes temporário para manter o número de bytes lidos do cliente e, em seguida, invocar o ByteBuffer
‘s get()
para carregar dados nesse array de bytes. Finalmente, convertemos a matriz de bytes para uma string criando uma nova instância String
., Nós eco a linha de volta para o cliente por converter a string para um array de bytes, de passagem, que para o ByteBuffer.wrap()
método e invocando o AsynchronousSocketChannel
‘s write()
método. Agora nós clear()
ByteBuffer
, que relembram significa que reposiciona o position
zero e coloca o ByteBuffer
em modo de gravação e, em seguida, passamos a ler a próxima linha do cliente.,
A única coisa a estar ciente é que o método main()
, que cria o servidor, também configura um segundo temporizador para manter a aplicação em execução. Porque o AsynchronousSocketChannel
‘s accept()
método retorna imediatamente, se não temos o Thread.sleep()
então a nossa aplicação irá parar imediatamente.,
para testar isto, Inicie o servidor e ligue-se a ele usando um cliente de telnet:
telnet localhost 5000
envie algumas cadeias de caracteres para o servidor, observe que elas são ecoadas de volta para você, e então envie uma linha vazia para terminar a conversa.
in conclusion
In this article i’ve presented two approaches to socket programming with Java: the traditional approach introduced with Java 1.0 and the newer, non-blocking NIO and NIO.2 abordagens introduzidas em Java 1.4 e Java 7, respectivamente., Você já viu várias iterações de um cliente de socket Java e um exemplo de servidor de socket Java, demonstrando tanto a utilidade de I/O Java Básico e alguns cenários onde o I/O não-bloqueante melhora o modelo de programação de socket Java. Usando I/O não-bloqueante, você pode programar aplicações Java em rede para lidar com múltiplas conexões simultâneas sem ter que gerenciar várias coleções de threads. Você também pode tirar proveito da nova escalabilidade do servidor que é construída em NIO e NIO.2.