web-dev-qa-db-fra.com

Comment créer un programme de messagerie instantanée de base en Java pur

Rétablissons cette question avec une prime! J'ai besoin d'un exemple qui reste en ligne , comme une vraie messagerie instantanée! Il doit être toujours prêt à recevoir ou envoyer un message à une adresse arbitraire sur un port arbitraire, en utilisant TCP. Le programme ne doit pas quitter après l'envoi/la réception d'un message.

La prime va à celui qui peut donner le meilleur exemple d'un vrai messager instantané utilisable.


En recherchant en ligne, toutes les ressources que j'ai trouvées sont soit tutoriels inutiles _, fils morts , tutoriels morts , exemples anciens , ou dites au programmeur de utiliser des API externes. Comment créer une messagerie instantanée de base en utilisant uniquement Java SE?

Il doit y avoir un moyen de faire cela, et un exemple de code serait apprécié . Il suffit d’exécuter les tâches les plus simples: vérifier si un client compatible est en ligne sur un autre ordinateur (l’adresse IP sera fournie par l’utilisateur) et envoyer un paquet TCP à ce client, qui recevra et affichera son contenu. .

12
Supuhstar

Lorsque cette question a été posée et répondue pour la première fois en 2011, c’était simplement "En regardant en ligne, toutes les ressources que j’ai trouvées sont soit inutiles Tutoriels, fils morts, soit dire au programmeur d’utiliser des API externes.". Les liens fournis ci-dessous répondaient aux critères à l'époque. Une discussion plus approfondie suit dans les commentaires.

Premiers résultats pour Google " Java socket chat ":

Ou depuis " Java 8 Chat Client ":

Beaucoup, beaucoup de résultats suivants dans la recherche. Choisissez celui qui convient à vos besoins. Vous pouvez même modifier la recherche Google pour afficher uniquement les résultats de l'année écoulée, si vous le souhaitez.

13
ziesemer

Je l'ai fait quand j'apprenais Java, il y a environ 10 ans. Ça marche:

Constantes.Java: 

package jsc;

public interface Constantes {
    public static final String MULTICAST_IP = "224.0.0.1";

    public static final int     MULTICAST_PORTA = 3333;

    public static final String SEPARADOR = "[>>>]";

    public static final int TAMANHO_MENSAGEM = 1024;

    public static final long ESPERA = 3000;

    public static final String ESTOUONLINE = "EstouOnline";

    public static final String DESCONECTANDO = "Desconectando";

    public static final String PRIVADO = "Privado";

}

ControladorThread.Java

package jsc;

import Java.io.IOException;
import Java.net.DatagramPacket;
import Java.net.InetAddress;
import Java.net.MulticastSocket;
import Java.net.UnknownHostException;
import Java.util.Date;
import Java.util.Iterator;
import Java.util.StringTokenizer;
import Java.util.Vector;

public class ControladorThread extends Thread implements Constantes{
    private MulticastSocket mcSocket;
    private Main main;
    private Vector<Usuario> listaUsuarios;                          // lista de usuários ativos

    public ControladorThread(Main main){
        super("ReceptoraThread_" + main.getNick());
        listaUsuarios = new Vector<Usuario>();
        listaUsuarios.add(new Usuario(main.getNick(), new Date().getTime()));
        this.main = main;

        try{
            mcSocket = new MulticastSocket(MULTICAST_PORTA);
            mcSocket.joinGroup(InetAddress.getByName(MULTICAST_IP));
        } catch(IOException e){
            e.printStackTrace();
        }
    }

    public void run(){
        while(true){
            try{
                byte[] buffer = receberPacote();
                processar(buffer);
                removerUsuariosOciosos();
                atualizarListaUsuarios();
            } catch(IOException e){
                e.printStackTrace();
            }
        }
    }   

    public byte [] receberPacote() throws IOException{
        byte[] buffer = new byte[TAMANHO_MENSAGEM];
        DatagramPacket pacote = new DatagramPacket(buffer, buffer.length);
        mcSocket.receive(pacote);
        return buffer;
    }

    public void processar(byte[] buffer){
        String mensagem = new String(buffer);
        mensagem = mensagem.trim();

        StringTokenizer tokens = new StringTokenizer(mensagem, SEPARADOR);
        String t1 = tokens.nextToken();
        String t2 = tokens.nextToken();

        if(t1.equals(ESTOUONLINE))
            atualizarEstadoUsuario(t2);
        else if(t1.equals(DESCONECTANDO))
            desconectarUsuario(t2);
        else if(t1.equals(PRIVADO)){
            String t3 = tokens.nextToken();
            String t4 = tokens.nextToken();
            if(t3.equals(main.getNick())){
                receberMensagemPrivada(t2, t4);
            }
        }
        else
            main.setTextoEntrada(t1 + " diz: " + t2);
    }

    public void receberMensagemPrivada(String deUsuario, String mensagem){
        main.abrirChatPrivado(main.getNick(), deUsuario, mensagem);
    }

    public boolean atualizarEstadoUsuario(String nomeUsuario){
        int pos;
        for(Iterator i = listaUsuarios.iterator(); i.hasNext(); ){
            Usuario uAux = (Usuario) i.next();
            if(uAux.getNome().equals(nomeUsuario)){
                pos = listaUsuarios.indexOf(uAux);
                listaUsuarios.remove(uAux);
                uAux.setTempoInicio(new Date().getTime());
                listaUsuarios.add(pos, uAux);
                return true;
            }
        }
        listaUsuarios.add(new Usuario(nomeUsuario, new Date().getTime()));
        return false;
    }

    public void removerUsuariosOciosos(){
        Usuario usuario = null;
        for(Iterator i = listaUsuarios.iterator(); i.hasNext(); ){
            usuario = (Usuario) i.next();
            if(new Date().getTime() - usuario.getTempoInicio() > ESPERA){
                desconectarUsuario(usuario.getNome());
                i = listaUsuarios.iterator();
            }
        }
    }

    public void desconectarUsuario(String nomeUsuario){
        for(Iterator i = listaUsuarios.iterator(); i.hasNext(); ){
            Usuario uAux = (Usuario) i.next();
            if(uAux.getNome().equals(nomeUsuario)){
                i.remove();
                break;
            }
        }
    }

    public void atualizarListaUsuarios(){
        Vector<String> sVector = new Vector<String>();
        Usuario uAux = null;
        System.out.println("\nOnline: ");
        for(Iterator i = listaUsuarios.iterator(); i.hasNext(); ){
            uAux = (Usuario) i.next();
            System.out.print( uAux.getNome() + " ");
            sVector.add(uAux.getNome());
        }
        main.setUsuariosOnline(sVector);
    }

    private class Usuario{
        private String nome;
        private long tempoInicio;

        public Usuario(){}

        public Usuario(String nome, long tempoInicio){
            this.nome = nome;
            this.tempoInicio = tempoInicio;
        }

        public String getNome() {
            return nome;
        }
        public void setNome(String nome) {
            this.nome = nome;
        }
        public long getTempoInicio() {
            return tempoInicio;
        }
        public void setTempoInicio(long tempoInicio) {
            this.tempoInicio = tempoInicio;
        }
    }

    public void sair(){
        try {
            mcSocket.leaveGroup(InetAddress.getByName(MULTICAST_IP));
            mcSocket.close();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

EstouOnlineThread.Java

package jsc;

import Java.io.IOException;
import Java.net.DatagramPacket;
import Java.net.InetAddress;
import Java.net.MulticastSocket;

public class EstouOnlineThread extends Thread implements Constantes{
    private MulticastSocket mcSocket;
    private String nick;
    private byte[] buffer;

    public EstouOnlineThread(String nick){
        super("EstouOnlineThread_" + nick);
        this.nick = nick;
        try {
            mcSocket = new MulticastSocket();
        } catch(IOException e) {
            e.printStackTrace();
        } 
    }

    public void run(){
        String saida = ESTOUONLINE + SEPARADOR + nick;
        buffer = saida.getBytes();
        while(true){
            try{
                DatagramPacket estouOnline = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(MULTICAST_IP), MULTICAST_PORTA);
                mcSocket.send(estouOnline);

                System.out.println(saida);
                sleep(ESPERA);
            }
            catch(InterruptedException e){
                e.printStackTrace();
            }
            catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}

MensagemPrivadaFrame.Java

package jsc;
import Java.awt.BorderLayout;
import Java.awt.Frame;
import Java.awt.TextArea;
import Java.awt.TextField;
import Java.awt.Toolkit;
import Java.awt.event.ActionEvent;
import Java.awt.event.ActionListener;
import Java.awt.event.WindowAdapter;
import Java.awt.event.WindowEvent;
import Java.io.IOException;
import Java.net.DatagramPacket;
import Java.net.InetAddress;
import Java.net.MulticastSocket;
import Java.net.UnknownHostException;

public class MensagemPrivadaFrame extends Frame implements Constantes{
    private static final long serialVersionUID = 1L;    

    private TextArea entrada;
    private TextField saida;

    private String nomeJanela;
    private String nick;
    private String paraUsuario;
    private MulticastSocket mcSocket;

    private ActionListener saidaListener;
    private WindowAdapter frameListener;

    private boolean estouVivo;      // indica que a janela ainda está ativa

    public MensagemPrivadaFrame(String nick, String paraUsuario){
        super("JSC - Chat com " + paraUsuario);
        setIconImage(Toolkit.getDefaultToolkit().getImage("icone.4"));
        this.nick = nick;
        this.paraUsuario = paraUsuario;
        this.nomeJanela = nick + paraUsuario;

        try {
            mcSocket = new MulticastSocket();
        } catch (IOException e) {
            e.printStackTrace();
        }

        iniciarComponentes();
        estouVivo = true;
    }

    public void setNomeJanela(String nomeJanela){
        this.nomeJanela = nomeJanela;
    }

    public String getNomeJanela(){
        return nomeJanela;
    }

    public String getNick() {
        return nick;
    }

    public void setNick(String nick) {
        this.nick = nick;
    }

    public boolean estouVivo(){
        return estouVivo;
    }

     public void iniciarComponentes(){
        saidaListener = new ActionListener(){
                public void actionPerformed(ActionEvent e){
                    TextField origem = (TextField) e.getSource();
                    enviarMensagem(origem.getText());
                    entrada.append("\n(" + nick + " diz) " + origem.getText());
                    origem.setText("");
                }
        };

        frameListener = new WindowAdapter(){
            public void windowClosing(WindowEvent e){
                estouVivo = false;
                dispose();

            }
        };

        entrada = new TextArea("[JSC] Bate papo privado entre " + nick + " e " + paraUsuario + "\n");
        entrada.setEditable(false);

        saida = new TextField();
        saida.addActionListener(saidaListener);

        addWindowListener(frameListener);
        setLayout(new BorderLayout());
        int x = (int) (Math.random() * 500);
        int y = (int) (Math.random() * 500);
        setBounds(x, y, 400, 300);
        System.out.println(x + " " + y);
        add("Center", entrada);
        add("South", saida);

        setVisible(true);
        saida.requestFocus();
    }

    public void setTextoEntrada(String texto){
        entrada.append("\n" + texto);
        entrada.setCaretPosition(entrada.getText().length());
    }

    public void enviarMensagem(String mensagem){
        try{
            mensagem = PRIVADO + SEPARADOR + nick + SEPARADOR + paraUsuario + SEPARADOR + mensagem;
            byte[] bMensagem = mensagem.getBytes();
            DatagramPacket pacote = new DatagramPacket(bMensagem, bMensagem.length, InetAddress.getByName(MULTICAST_IP), MULTICAST_PORTA);
            mcSocket.send(pacote);
        }
        catch(UnknownHostException e){
            e.printStackTrace();
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

Main.Java

package jsc;

import Java.awt.BorderLayout;
import Java.awt.Dimension;
import Java.awt.Frame;
import Java.awt.ScrollPane;
import Java.awt.TextArea;
import Java.awt.TextField;
import Java.awt.Toolkit;
import Java.awt.event.ActionEvent;
import Java.awt.event.ActionListener;
import Java.awt.event.MouseAdapter;
import Java.awt.event.MouseEvent;
import Java.awt.event.WindowAdapter;
import Java.awt.event.WindowEvent;
import Java.io.IOException;
import Java.net.DatagramPacket;
import Java.net.InetAddress;
import Java.net.MulticastSocket;
import Java.net.UnknownHostException;
import Java.util.Iterator;
import Java.util.Vector;

import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;

public class Main extends Frame implements Constantes{
    private static final long serialVersionUID = 1L;

    private TextArea entrada;
    private TextField saida;
    private JList usuariosOnline;
    private ScrollPane usuariosOnlineScroll;

    private WindowAdapter  mainListener;
    private ActionListener saidaListener;
    private MouseAdapter   listListener;

    private MulticastSocket mcSocket;                           // soquete para multicasting
    private Vector<String> listaUsuariosOnline;                 // lista com os nomes de usuários online
    private Vector<MensagemPrivadaFrame> listaJanelasAbertas;   // janelas de conversação privadas abertas
    private String nick;                                        // nome do usuário no chat

    public void setNick(String nick){
        this.nick = nick;
    }

    public String getNick(){
        return nick;
    }

    public Main(String nick){
        super("Java Socket Chat [" + nick + "]");
        setIconImage(Toolkit.getDefaultToolkit().getImage("icone.1"));  

        this.nick = nick;

        listaUsuariosOnline = new Vector<String>();
        listaUsuariosOnline.add(nick);

        listaJanelasAbertas = new Vector<MensagemPrivadaFrame>();

        try{
            mcSocket = new MulticastSocket();
        }
        catch(IOException e){
            e.printStackTrace();
        }

        iniciarComponentes();
        new EstouOnlineThread(nick).start();
        new ControladorThread(this).start();
    }

    public void iniciarComponentes(){
        mainListener = new WindowAdapter(){
            public void windowClosing(WindowEvent e){
                sair();
            }
        };

        saidaListener = new ActionListener(){
            public void actionPerformed(ActionEvent e){
                TextField origem = (TextField) e.getSource();
                enviarMensagem(origem.getText());
                origem.setText("");
            }
        };

        listListener = new MouseAdapter(){
            public void mouseClicked(MouseEvent e){
                if( e.getClickCount() >= 2 ){
                    // abrir a janela para mensagens privadas e passar o id do usuário
                    JList jlAux = (JList) e.getSource();
                    String paraUsuario = (String) jlAux.getSelectedValue();
                    abrirChatPrivado(nick, paraUsuario, null);
                }
            }
        };

        usuariosOnline = new JList(listaUsuariosOnline);
        usuariosOnline.setSize(new Dimension(60, 280));

        usuariosOnlineScroll = new ScrollPane();
        usuariosOnlineScroll.add(usuariosOnline);

        entrada = new TextArea("Olá " + nick);
        entrada.setEditable(false);
        entrada.setSize(300,280);

        saida   = new TextField();

        saida.addActionListener(saidaListener);
        usuariosOnline.addMouseListener(listListener);
        usuariosOnline.setMinimumSize(new Dimension(60, 250));
        addWindowListener(mainListener);

        setSize(400, 300);
        setLayout(new BorderLayout());
        add("North", new JLabel("Java Socket ChatO"));
        add("Center", entrada);
        add("South", saida);
        add("East", usuariosOnlineScroll);

        setVisible(true);
        requestFocus();
    }

    public void enviarMensagem(String mensagem){
        try{
            mensagem = nick + SEPARADOR + mensagem;
            byte[] bMensagem = mensagem.getBytes();
            DatagramPacket pacote = new DatagramPacket(bMensagem, bMensagem.length, InetAddress.getByName(MULTICAST_IP), MULTICAST_PORTA);
            mcSocket.send(pacote);
        }
        catch(UnknownHostException e){
            e.printStackTrace();
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }

    private void desconectando(){
        try{
            String mensagem = "Desconectando" + SEPARADOR + nick;
            byte[] bMensagem = mensagem.getBytes();
            DatagramPacket pacote = new DatagramPacket(bMensagem, bMensagem.length, InetAddress.getByName(MULTICAST_IP), MULTICAST_PORTA);
            mcSocket.send(pacote);
        }
        catch(UnknownHostException e){
            e.printStackTrace();
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }

    public void abrirChatPrivado(String nick, String paraUsuario, String mensagem){
        removerJanelasInativas();   
        if(nick.equals(paraUsuario)){
            JOptionPane.showMessageDialog(null, "Você não pode abrir um janela de conversação para você mesmo!", "Burro!", JOptionPane.ERROR_MESSAGE);
            return;
        }
        String nome = nick + paraUsuario;
        MensagemPrivadaFrame janela = null;
        for(Iterator i = listaJanelasAbertas.iterator(); i.hasNext();){
            janela = (MensagemPrivadaFrame) i.next();
            if(nome.equals(janela.getNomeJanela())){
                System.out.println(nick + " - " + janela.getNomeJanela() + " - " + janela.toString());
                janela.setTextoEntrada("(" + paraUsuario + " diz) " + mensagem);
                //janela.requestFocus();
                return;
            }
        }

        janela = new MensagemPrivadaFrame(nick, paraUsuario);

        if(mensagem != null)
            janela.setTextoEntrada("(" + paraUsuario + " diz) " + mensagem);

        listaJanelasAbertas.add(janela);
        //janela.requestFocus();
    }

    public void removerJanelasInativas(){
        MensagemPrivadaFrame janela = null;
        for(Iterator i = listaJanelasAbertas.iterator(); i.hasNext(); ){
            janela = (MensagemPrivadaFrame) i.next();
            if( !janela.estouVivo()){
                i.remove();
            }
        }
    }

    public void setTextoEntrada(String texto){
        entrada.append("\n" + texto);
        entrada.setCaretPosition(entrada.getText().length());
    }

    public void setUsuariosOnline(Vector<String> listaUsuariosOnline){
        usuariosOnline.setListData(listaUsuariosOnline);
    }

    public void sair(){
        desconectando();
        dispose();
        System.exit(0);
    }

    public static void main(String args[]){
        String nick = JOptionPane.showInputDialog("Digite seu nome (max. 20 caracteres): ");
        if(nick != null && !nick.equals("")){
            if(nick.length() > 20)
                nick = nick.substring(0, 20);
            new Main(nick);
        }
        else
            JOptionPane.showMessageDialog(null, "É necessário informar um nome para entrar no bate-papo");
        //System.exit(0);
    }
}

De nos jours, je ne suis pas fier du code, mais cela fonctionne vraiment. 

Modifier:

Comme certains l'ont suggéré, j'ai apporté quelques améliorations au code (refactoring) et poster le projet sur GitHub: https://github.com/jaumzera/javasocketchat

8
Jaumzera

Je ne suis même pas sûr si cette question est encore utilisée ou quoi mais j'ai aimé la tâche et j'ai pensé:

pourquoi pas?

Voici ma mise en œuvre, aussi simple que cela puisse être mais sans oublier les parties fondamentales. Écrit en Java pur, utilise, entre autres, Sockets, Threads et SynchronizedList:

SimpleChat.Java (Principal)

import Java.io.BufferedReader;
import Java.io.IOException;
import Java.io.InputStreamReader;


public class SimpleChat {

private static boolean isRunning = true;
private static Sender sender;
private static Receiver receiver;

public static void main(String[] args) throws IOException {

    if(args.length < 3){
        showUsage();
    }

    try {
        receiver = new Receiver(Integer.parseInt(args[1]));
        sender = new Sender(args[0], args[2], Integer.parseInt(args[3]));
    } catch (InterruptedException e) {
        showUsage();
    }

    // Read user input
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    System.out.println("Chat started. Type '\\exit' to quit.");

    while(isRunning) {
        String input = br.readLine();

        if(input.equals("\\exit")){
            receiver.stop();
            sender.stop();
            isRunning = false;
        } else {
            sender.sendMessage(input);
        }
    }   
}

static void showUsage(){
    System.out.println("Usage: Java SimpleChat.Java listening_port target_IP target_port");
    System.exit(1);
}

}

Receiver.Java

import Java.io.BufferedReader;
import Java.io.IOException;
import Java.io.InputStreamReader;
import Java.net.ServerSocket;
import Java.net.Socket;

public class Receiver {

private boolean isRunning = true;

public Receiver(int listeningPort) throws IOException {

    Runnable receiverT = new Runnable() {
        public void run() {

            ServerSocket serverSocket;
            try {
                serverSocket = new ServerSocket(listeningPort);
                Socket clientSocket = serverSocket.accept();
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

                while(isRunning) {
                    try {
                        System.out.println(in.readLine());
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }

        }
    };

    new Thread(receiverT).start();
}

public void stop(){
    isRunning = false;
}


}

Sender.Java

import Java.io.IOException;
import Java.io.PrintWriter;
import Java.net.Socket;
import Java.net.UnknownHostException;
import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.Iterator;
import Java.util.List;

public class Sender {

private boolean isRunning = true;
private volatile List<String> msgs;

public Sender(String username, String targetIP, int targetPort) throws InterruptedException, UnknownHostException, IOException {
    msgs = Collections.synchronizedList(new ArrayList<String>());

    Runnable senderT = new Runnable() {
        public void run() {
            try {
                Socket socket = new Socket(targetIP, targetPort);
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

                while(isRunning) {
                    synchronized(msgs){
                        Iterator<String> it = msgs.iterator();

                        while(it.hasNext()){
                            out.println(username + ": " + it.next());
                        }

                        // Clear messages to send
                        msgs.clear();
                    }
                }

                out.close();
                socket.close();
            } catch (UnknownHostException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }

        }
    };

    new Thread(senderT).start();
}

public void stop(){
    isRunning = false;
}

public void sendMessage(String msg){
    synchronized(msgs){
        msgs.add(msg);  
    }
}
}

Comme l'indique 'showUsage ()', utilisez ce programme comme suit:

Java SimpleChat.Java username listening_port target_ip target_port

Exemple:

Java SimpleChat.Java Supuhstar 1234 127.0.0.1 1234

[Pour vous parler]

2
N3sh

Hm, j'ai été tenté de vous diriger vers une implémentation Java d'un serveur implémentant le protocole imap (par exemple, gavamail). Mais cela, bien sûr, pourrait aussi être considéré comme un "ancien" code et tuerait certainement votre attente (pour être une solution non standard). Néanmoins, il s'agit d'une référence appropriée répondant à vos spécifications (concises).

Qu'avons nous?

Nous avons besoin d'une solution qui devrait être en Java… .. Elle doit implémenter un système de messagerie instantanée de base.

Ce dernier est problématique car il couvre un très large éventail de fonctionnalités. Mais "basique" semble permettre une solution minimale.

Alors, quel est un système de messagerie instantanée minimum? Essayons avec ce qui suit:

  • un client qui poste et récupère des messages.
  • un serveur qui stocke les messages publiés pour une récupération (ultérieure)

Nous aurions également besoin d'une politique indiquant comment un client identifierait un serveur approprié. La solution la plus simple pour le dernier aspect consiste à utiliser un serveur "central" avec une adresse connue. Pour les cas plus complexes, il faudrait que la fonctionnalité serveur et/ou client soit répartie sur plusieurs instances et concevoir un schéma ou une politique permettant d'identifier la ou les instances appropriées pour la communication.

Nous omettons les sémantiques plus complexes telles que le fait que différents utilisateurs ou messages soient associés à un système de catégories ou de balises.

Nous avons maintenant deux composants:

Un serveur implémentant deux points d'entrée:

  1. POST_MESSAGE
    recevoir un message de la part d'un client et le stocker pour le récupérer ultérieurement
    Ceci pose immédiatement la question de savoir où stocker de tels messages (dans une base de données ou un système de fichiers pour la persistance ou simplement dans la mémoire pour un " messages en direct tant que le serveur est actif " sémantique)

  2. LOOKUP_MESSAGE
    sélectionnez un message approprié parmi ceux qui sont stockés (de préférence un message non lu) et retournez à l'appelant.
    Cela pourrait également renvoyer un ensemble de messages (mais pensez à le limiter dans les cas où un appelant a un arriéré important de messages)
    Il peut être nécessaire de garder une trace des messages déjà lus, soit en les marquant, soit en maintenant le statut affiché sur le client. Cela pourrait même être aussi simple que de garder l'heure ou l'ordinal du dernier message vu et d'envoyer cette information avec la demande LOOKUP_MESSAGE.

Un client doit interagir avec un utilisateur et le service, d’autre part.

Il faudra un nouveau message de l'utilisateur (probablement sur demande explicite (par exemple, bouton d'envoi) et appeler le service POST_MESSAGE sur le serveur associé.

Il sera également (probablement régulièrement, éventuellement sur requête explicite (par exemple, l’utilisateur est en train de démarrer le client)) d’interroger le serveur à la recherche de nouveaux messages. (Vous pouvez également concevoir un service de notification distinct utilisé par le serveur pour informer le client de nouveaux messages. Ce qui convient à vos "besoins" dépasse votre question.)

C'est tout.

Ainsi, tout exemple d'application client/serveur basé sur TCP constituera un point de départ idéal pour une implémentation simple.

Je devrais également mentionner que vous pouvez couper la logique de spécification dans le client et déléguer l'interaction utilisateur à un navigateur standard et implémenter la logique d'application client dans une instance de serveur (Web) (ensemble ou séparément de la partie serveur). Néanmoins, vous aurez toujours les deux fonctionnalités logiques (client/serveur) conformément à la spécification minimale ci-dessus. 

Un autre aspect que vous devriez connaître:

Avec certains commentaires, vous avez mentionné les attributions "hôte" et "invité" disponibles dans les exemples de messagerie actuels. Il s’agit en fait d’une structure logique d’un système de marquage fourni avec ces messagers. Les messages sont toujours envoyés d'un client à un serveur, puis récupérés par d'autres clients. Si un client peut voir un message est déterminé par le fait que le client est éligible pour la balise spécifique. Par exemple, si vous postez un message à un de vos contacts (utilisateur b), il ne marque que par le tag "for_user_b". En tant que tel, il est uniquement visible par le posteur et toute personne autorisée à lire les messages de la balise "for_user_b" (utilisateur b notre exemple). Sachez donc que la structure logique d'un système de messagerie est déterminée par la politique d'accès et de distribution et non par la structure de distribution physique!

2
rpy

Je pense que vous devriez clarifier certains détails concernant ce que vous entendez exactement par "programme de messagerie instantanée de base" et quels sont vos objectifs vis-à-vis de ce projet.

Dans un commentaire de 2011, vous avez indiqué qu'il ne devrait pas exister de "hub central", mais dans un commentaire plus récent, vous souhaiteriez quelque chose de plus similaire à Skype ou à iMessage, dans lequel les utilisateurs ne doivent pas savoir quel pair est l'homologue. serveur ... Il est techniquement possible (à l'aide de protocoles tels que mdns, dlna ou ssdp) de rechercher de manière transparente sur le réseau local des nœuds de serveur existants et de le connecter à l'homologue du serveur, le cas échéant, ou de s'établir en tant que serveur local pour que les autres nœuds puissent s'y connecter. C'est par exemple comment fonctionnait le protocole Bonjour Apple iChat. Il s’agit toutefois d’une solution assez complexe à mettre en œuvre, et elle n’est certainement pas conforme à ce que font les programmes de messagerie actuels du marché de masse.

De plus, l’établissement d’une communication directe entre homologues entre utilisateurs soulève plusieurs problèmes pratiques (notamment à cause des pare-feu et de la technologie NAT, mais il existe également des problèmes de confidentialité et de sécurité). La plupart des protocoles relaient donc la plupart des messages via le serveur central et négocient une connexion directe aux seules fins du transfert de fichiers et des appels audio/vidéo.

Pour toutes ces raisons, à moins que vous ne recherchiez simplement un exemple de communication de réseau local entre deux hôtes, vous souhaiterez certainement deux programmes distincts: un serveur et un client.

Ensuite, en supposant que mon hypothèse soit correcte, deux autres questions doivent être clarifiées. Premièrement, avez-vous une raison réelle de concevoir le protocole par vous-même, ou serait-il acceptable de mettre en œuvre un protocole existant (tel que XMPP, IRC, SIMPLE ... il y en a des tonnes). Même si ces protocoles peuvent sembler très complexes au début, il est presque toujours possible de mettre en œuvre un sous-ensemble de fonctions/messages de ce protocole. Concevoir par vous-même un protocole de réseau naïf n’est pas si difficile, mais vous devrez faire face à une multitude d’erreurs potentielles (essentiellement des inefficacités, des lacunes et d’autres problèmes mineurs). C’est peut-être bien ce que vous visez précisément (c’est-à-dire acquérir une expérience de la conception d’un protocole réseau à partir de rien), mais à moins que ce ne soit le cas, vous devriez sérieusement opter pour l’implémentation d’un protocole existant. En effet, travailler avec un protocole existant permettra non seulement d'éviter de telles erreurs de conception, mais encore mieux, vous gagnerez des connaissances importantes sur la manière dont d'autres (concepteurs de protocoles expérimentés) ont réellement résolu les problèmes qu'ils ont rencontrés en cours de route. L'utilisation d'un protocole existant rendra également beaucoup plus facile et plus intéressant le développement de ce programme, étant donné que vous pourrez par exemple tester vos programmes client et serveur de manière indépendante en vous connectant depuis/vers une implémentation client/serveur officielle. Vous pourrez également exploiter les décodeurs de protocole existants dans les outils de détection de trafic afin de déboguer les messages en transit.

La deuxième question importante concerne le réalisme du programme serveur et surtout la persistance. Le serveur doit-il maintenir une liste d'utilisateurs persistants et les authentifier? Le serveur doit-il stocker une liste de contacts autorisés pour chaque utilisateur? Le serveur doit-il autoriser les messages de magasin destinés à un pair actuellement hors ligne ou inaccessible à ce moment précis? Les vrais programmes de serveur de messagerie font généralement de telles choses, et bien que l’implémentation de tels mécanismes ne soit pas très difficile, il est préférable de l’envisager tôt dans la conception de l’architecture d’un programme. Par exemple, si vous décidez que ces fonctionnalités sont vraiment souhaitables, il peut s'avérer beaucoup plus intéressant de concevoir immédiatement votre serveur autour d'un moteur de file d'attente de messages persistant, tel qu'ActiveMQ ...

Je sais que ce ne sont pas les exemples que vous demandiez, mais j'espère quand même que ces réflexions pourront vous aider.

1
jwatkins

Comme indiqué précédemment, vous avez besoin de beaucoup de choses pour bien discuter.
Mais je crois que vous voulez que quelque chose commence. Et si vous connaissez l'adresse et le port de l'autre "client", c'est facile.
Mise en oeuvre extrêmement simple du "chat"

import Java.io.InputStream;
import Java.io.OutputStream;
import Java.net.InetSocketAddress;
import Java.net.ServerSocket;
import Java.net.Socket;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;

public class SimpleChat {
    protected boolean running = true;
    protected int port;

    private Thread server;


    public static void main(String... arg) {
        //create 2 clients in the localhost to test
        SimpleChat app1 = new SimpleChat(8989);
        SimpleChat app2 = new SimpleChat(8988);

        app1.sendMessage("localhost", 8988, "Message from app1 to app2");
        app2.sendMessage("localhost", 8989, "Message from app2 to app1");

        System.exit(0); // ugly way to kill the threads and exit
    }

    public SimpleChat(int port) {
        this.port = port;
        start();
    }

    public void start() {
        server = new Thread(new Server());
        server.start();
    }

    public boolean sendMessage(String Host, int port, String message) {
        try {
            //Connect to a server on given Host and port and "send" the message
            InetSocketAddress destination
                    = new InetSocketAddress(Host, port);
            Socket s = SocketFactory.getDefault().createSocket();
            s.connect(destination);
            OutputStream out = s.getOutputStream();
            out.write(message.getBytes());
            out.flush();
            out.close();

            s.close();

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public void messageRecived(String message) {
        System.out.println("Message recived: " + message);
    }

    public void stop() {
        this.running = false; // only stop after a socked connection
    }

    class Server implements Runnable {
        public void run() {
            try {
                //Create a server socket to recieve the connection
                ServerSocket ss = ServerSocketFactory.getDefault()
                        .createServerSocket(port);
                while (running) {
                    Socket s = ss.accept();
                    InputStream in = s.getInputStream();
                    StringBuilder message = new StringBuilder();
                    int len;
                    byte[] buf = new byte[2048];
                    while ((len = in.read(buf)) > -1) {
                        if (len > 0) {
                            message.append(new String(buf, 0, len));
                        }
                    }
                    messageRecived(message.toString());
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(-1);
            }
        }
    }
}
0
fhofmann