it-swarm.com.de

So erstellen Sie ein einfaches Instant Messaging-Programm in reinem Java

Wiederhole diese Frage mit einer Belohnung! Ich brauche ein Beispiel, dass online bleibt , wie ein echter Instant Messenger! Sie muss immer bereit sein, eine Nachricht zu empfangen oder zu senden an eine beliebige Adresse über einen beliebigen Port unter Verwendung von TCP. Das Programm darf nach dem Senden/Empfangen einer Nachricht nicht beendet werden.

Kopfgeld geht an jeden, der das beste Beispiel für einen echten, nutzbaren Instant Messenger geben kann.


Wenn Sie online suchen, sind alle Ressourcen, die ich gefunden habe, entweder unbrauchbare Tutorials , Dead Threads , Dead Tutorials , alte Beispiele , oder weisen Sie den Programmierer an, externe APIs zu verwenden. Wie kann ich von Grund auf einen einfachen Instant Messenger erstellen, nur mit Java SE?

Es muss einen Weg geben, dies zu tun, und etwas Beispielcode wäre willkommen . Es müssen nur die einfachsten Aufgaben ausgeführt werden: Prüfen Sie, ob ein kompatibler Client auf einem anderen Computer online ist (die IP-Adresse wird vom Benutzer bereitgestellt), und senden Sie ein TCP -Paket an diesen Client, der dessen Inhalt empfängt und anzeigt .

12
Supuhstar

Als diese Frage im Jahr 2011 zum ersten Mal gestellt und beantwortet wurde, handelte es sich einfach um "Online suchen: Alle Ressourcen, die ich gefunden habe, sind entweder nutzlos Tutorials, tote Threads oder weisen den Programmierer an, externe APIs. " Die angegebenen Links erfüllen die Kriterien zu dieser Zeit. Weitere Diskussion folgt in den Kommentaren.

Erste Google-Ergebnisse für " Java socket chat ":

Oder von " Java 8 Chat Client ":

Viele, viele Ergebnisse folgen auf die Suche. Wählen Sie eine aus, die Ihren Bedürfnissen entspricht. Sie können sogar die Google-Suche so ändern, dass nur Ergebnisse des vergangenen Jahres angezeigt werden, wenn Sie dies wünschen.

13
ziesemer

Ich habe das gemacht, als ich Java vor etwa 10 Jahren lernte. Es klappt:

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);
    }
}

Heute bin ich nicht stolz auf den Code, aber er funktioniert wirklich. 

Bearbeiten:

Wie einige vorgeschlagen haben, habe ich einige Code-Verbesserungen vorgenommen (Refactoring) und das Projekt auf GitHub veröffentlicht: https://github.com/jaumzera/javasocketchat

8
Jaumzera

Ich bin nicht einmal sicher, ob diese Frage noch verwendet wird oder was aber die Aufgabe hat mir gefallen und ich dachte:

warum nicht?

Hier ist meine Implementierung, so einfach wie möglich, aber ohne wesentliche Teile zu vergessen. In reinem Java geschrieben, verwendet unter anderem Sockets, Threads und SynchronizedList:

SimpleChat.Java (Haupt)

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);
}

}

Empfänger.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);  
    }
}
}

Wie 'showUsage ()' sagt, verwenden Sie dieses Programm wie folgt:

Java SimpleChat.Java username listening_port target_ip target_port

Beispiel:

Java SimpleChat.Java Supuhstar 1234 127.0.0.1 1234

[Um mit dir selbst zu reden]

2
N3sh

Hm, ich war versucht, Sie zu einer Java-Implementierung eines Servers zu leiten, der das Imap-Protokoll implementiert (z. B. gavamail). Dies kann jedoch auch als "alter" Code gelten und würde sicherlich Ihre Erwartungen töten (da es sich nicht um eine Standardlösung handelt). Trotzdem ist es eine richtige Referenz, die Ihre (knappe) Spezifikation erfüllt.

Was haben wir?

Wir brauchen eine Lösung, die in Java sein sollte. Sie muss ein einfaches Instant Messaging-System implementieren.

Letzteres ist problematisch, da es einen sehr breiten Funktionsumfang abdeckt. Aber "basic" scheint eine Minimallösung zuzulassen.

Was ist ein Minimum an Instant Messaging-System? Versuchen wir es mit folgendem:

  • ein Client, der Nachrichten sendet und abruft.
  • ein Server, der veröffentlichte Nachrichten für den (späteren) Abruf speichert

Wir benötigen auch eine Richtlinie, wie ein Client einen richtigen Server identifiziert. Die trivialste Lösung für den späteren Aspekt ist die Verwendung eines "zentralen" Servers mit einer bekannten Adresse. Für komplexere Fälle müssten wir die Server- und/oder Clientfunktionalität auf mehrere Instanzen verteilen und ein Schema oder eine Richtlinie zum Identifizieren der richtigen Instanz (en) für die Kommunikation entwickeln.

Wir verzichten auf eine komplexere Semantik, z. B. unterschiedliche Benutzer oder Nachrichten, die sich auf ein System von Kategorien oder Tags beziehen.

Jetzt haben wir zwei Komponenten:

Ein Server, der zwei Einstiegspunkte implementiert:

  1. POST-MELDUNG
    eine Nachricht von einem Client empfangen und zum späteren Abruf speichern
    Dies stellt sich sofort die Frage, wo diese Nachrichten gespeichert werden sollen (in einer Datenbank oder einem Dateisystem für die Beständigkeit oder einfach im Speicher eines " Nachrichten leben", solange der Server aktiv ist "Semantik).

  2. LOOKUP_MESSAGE
    Wählen Sie aus den gespeicherten Nachrichten eine geeignete (vorzugsweise ungelesene) Nachricht aus, und kehren Sie zum Anrufer zurück.
    Dies könnte auch einen Satz von Nachrichten zurückgeben (denken Sie jedoch daran, diesen Satz auf Fälle zu beschränken, in denen ein Anrufer einen erheblichen Nachrichtenrückstand hat).
    Möglicherweise müssen Sie die bereits gelesenen Nachrichten nachverfolgen, indem Sie die Nachrichten markieren oder den Status "Gesehen" beim Client beibehalten. Dies kann sogar so einfach sein wie das Speichern der Zeit oder der Ordinalzahl der zuletzt gesehenen Nachricht und das Senden dieser Informationen zusammen mit der Anforderung LOOKUP_MESSAGE.

Ein Client muss einerseits mit einem Benutzer und andererseits mit dem Dienst interagieren.

Es nimmt eine neue Nachricht vom Benutzer (wahrscheinlich auf explizite Anforderung (z. B. die Schaltfläche "Senden") und ruft den POST_MESSAGE-Dienst auf dem zugehörigen Server auf.

Es wird (wahrscheinlich regelmäßig auch auf explizite Anforderung (z. B. wenn der Client den Client startet)) den Server nach neuen Nachrichten abfragen. (Alternativ können Sie einen separaten Benachrichtigungsdienst einrichten, der vom Server verwendet wird, um den Client über neue Nachrichten zu informieren. Was Ihren "Bedürfnissen" entspricht, liegt außerhalb Ihrer Frage.)

Das ist es.

Daher ist jedes Beispiel einer auf TCP basierenden Client/Server-Anwendung ein idealer Ausgangspunkt für eine direkte Implementierung.

Ich sollte auch erwähnen, dass Sie die Spezifikationslogik innerhalb des Clients reduzieren und die Benutzerinteraktion an einen Standardbrowser delegieren und die Clientanwendungslogik in eine (Web-) Serverinstanz (zusammen oder getrennt vom Serverteil) implementieren können. Trotzdem haben Sie immer noch beide (Client/Server) logische Funktionen gemäß obiger Mindestspezifikation. 

Ein weiterer Aspekt, den Sie beachten sollten:

Mit einigen Kommentaren haben Sie "Host" - und "Gast" -Attributionen erwähnt, die in aktuellen Messenger-Beispielen verfügbar sind. Dies ist tatsächlich eine logische Struktur eines Tag-Systems, das mit diesen Messenger-Elementen bereitgestellt wird. Die Nachrichten werden weiterhin von einem Client an einen Server gesendet und von anderen Clients abgerufen. Ob ein Client eine Nachricht sehen kann, wird dadurch bestimmt, dass der Client für das bestimmte Tag berechtigt ist. Wenn Sie z. B. eine Nachricht von Ihrem (Benutzer b) an einen Kontakt senden, wird die Nachricht nur mit dem Tag "for_user_b" versehen, sodass sie nur für das Poster und alle Personen sichtbar ist, die auch die Nachricht "for_user_b" lesen dürfen (Benutzer b in unser Beispiel). Bitte beachten Sie, dass die logische Struktur eines Messagingsystems von der Zugriffs- und Verteilungsrichtlinie bestimmt wird und nicht von der physischen Verteilungsstruktur!

2
rpy

Ich denke, Sie sollten einige Details dazu erklären, was genau Sie unter einem "grundlegenden Instant Messaging-Programm" verstehen und was Ihre Ziele in Bezug auf dieses Projekt tatsächlich sind.

In einem Kommentar aus dem Jahr 2011 haben Sie erwähnt, dass es keinen "zentralen Hub" geben sollte, aber in einem neueren Kommentar sagen Sie, dass Sie etwas mehr mit Skype oder iMessage in Einklang bringen möchten, bei dem die Benutzer nicht wissen müssen, welcher Peer der ist Server ... Es ist technisch möglich (mithilfe von Protokollen wie mdns, dlna oder ssdp), dass das Programm das lokale Netzwerk transparent nach potenziell vorhandenen Serverknoten durchsucht und entweder eine Verbindung zum Server-Peer (falls vorhanden) herstellt oder sich selbst einrichtet als lokaler Server für andere Knoten, um eine Verbindung herzustellen. So hat zum Beispiel das Bonjour-Protokoll von Apple iChat funktioniert. Dies ist jedoch eine recht komplexe Lösung, die richtig implementiert werden kann, und stimmt definitiv nicht mit den aktuellen Massenmarktnachrichtenprogrammen überein.

Die Einrichtung einer direkten Peer-to-Peer-Kommunikation zwischen Benutzern wirft mehrere praktische Probleme auf (insbesondere aufgrund von Firewalls und NAT, aber es bestehen auch Bedenken hinsichtlich der Vertraulichkeit und der Sicherheit). Die meisten Protokolle leiten daher die meisten Nachrichten über den zentralen Server weiter und verhandeln eine direkte Verbindung nur zum Zwecke der Dateiübertragung und von Audio-/Videoanrufen.

Aus all diesen Gründen möchten Sie, wenn Sie nicht nur nach einem Beispiel für eine lokale Netzwerkkommunikation zwischen zwei Hosts suchen, höchstwahrscheinlich zwei verschiedene Programme wünschen: einen Server und einen Client.

Wenn ich davon ausgehe, dass meine Annahme richtig ist, müssen zwei weitere Fragen geklärt werden. Erstens, haben Sie einen Grund, sich das Protokoll selbst auszudenken, oder wäre es akzeptabel, ein vorhandenes Protokoll zu implementieren (wie XMPP, IRC, SIMPLE ... es gibt Tonnen). Obwohl diese Protokolle auf den ersten Blick sehr komplex aussehen, ist es fast immer möglich, nur eine Teilmenge dieser Protokolleigenschaften/-nachrichten zu implementieren. Das Entwerfen eines naiven Netzwerkprotokolls ist nicht so schwierig, aber es gibt Unmengen potenzieller Fehler (meist Ineffizienzen, Unvollständigkeit und andere kleinere Probleme), die Sie durchgehen müssen. Vielleicht ist das tatsächlich das, worauf Sie gezielt abzielen (dh Erfahrung beim Entwurf eines Netzwerkprotokolls von Grund auf sammeln), aber wenn dies nicht der Fall ist, sollten Sie sich ernsthaft für die Implementierung eines vorhandenen Protokolls entscheiden. Wenn Sie mit einem vorhandenen Protokoll arbeiten, werden Sie nicht nur solche Konstruktionsfehler vermeiden, sondern noch besser: Sie erhalten ein erhebliches Wissen darüber, wie andere (in der Regel erfahrene Protokolldesigner) Probleme gelöst haben, die sie unterwegs getroffen haben. Die Verwendung eines vorhandenen Protokolls macht es auch für Sie viel einfacher und interessanter, dieses Programm zu entwickeln, da Sie beispielsweise Ihre Client- und Serverprogramme unabhängig voneinander testen können, indem Sie eine Verbindung zu/von einer offiziellen Client/Server-Implementierung herstellen. Sie können auch vorhandene Protokolldecoder in Traffic Sniffing-Tools nutzen, um Nachrichten zu debuggen, die durchlaufen werden.

Die zweite wichtige Frage ist, wie realistisch das Serverprogramm sein soll und vor allem im Hinblick auf die Beständigkeit. Soll der Server eine persistente Liste von Benutzern führen und diese authentifizieren? Sollte der Server eine Liste der zulässigen Kontakte für jeden Benutzer speichern? Soll der Server das Speichern von Nachrichten zulassen, die an einen Peer gerichtet sind, der gerade offline ist oder der gerade nicht erreicht werden kann? Echte Messaging-Serverprogramme tun im Allgemeinen solche Dinge, und obwohl die Implementierung solcher Mechanismen nicht sehr schwierig ist, sollte sie frühzeitig beim Entwurf der Architektur eines Programms berücksichtigt werden. Sollten Sie beispielsweise entscheiden, dass diese Funktionen in der Tat wünschenswert sind, könnte es für Sie viel interessanter sein, Ihren Server sofort um eine permanente Nachrichtenwarteschlangen-Engine wie ActiveMQ herum zu entwerfen.

Ich weiß, dass dies nicht die Beispiele sind, nach denen Sie gefragt haben, aber ich hoffe trotzdem, dass diese Gedanken Ihnen helfen können.

1
jwatkins

Wie bereits erwähnt, gibt es viele Dinge, die Sie benötigen, um einen echten Chat ins Leben zu rufen.
Aber ich glaube, dass Sie wollen, dass etwas beginnt. Und wenn Sie die Adresse und den Port des anderen "Clients" kennen, ist das ganz einfach.
Extrem einfache "Chat" -Implementierung

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