Как создать базовую программу обмена мгновенными сообщениями в чистой Java

Повторно рассмотрев этот вопрос с благодарностью! Мне нужен пример того, что остается в сети, как настоящий мессенджер! Он должен всегда быть готовым к приему или отправке сообщения на произвольный адрес через произвольный порт с использованием TCP. Программа не должна выйти после отправки/получения сообщения.

Bounty обращается к тому, кто может дать лучший пример реального, полезного мессенджера.


В онлайн-поиске все ресурсы, которые я нашел, являются бесполезными учебниками, dead темы, мертвые учебники, древние примеры, или попросите программиста использовать внешние API. Как создать базовый мессенджер с нуля, только используя Java SE?

Должен быть способ сделать это, и некоторый пример кода будет оценен. Ему просто нужно выполнить самые простые задачи: проверить, находится ли совместимый клиент на другом компьютере (IP будет предоставлен пользователем) и отправить TCP-пакет этому клиенту, который получит и отобразит его содержимое.

Ответ 1

Когда этот вопрос был впервые задан и ответил еще в 2011 году, это было просто "В режиме онлайн, все ресурсы, которые я нашел, либо бесполезны учебники, мертвые потоки или сообщить программисту использовать внешние API". Ниже предоставленные ссылки соответствуют критериям в то время. Дальнейшее обсуждение следует в комментариях.

Первые несколько результатов Google для " java socket chat":

Или из java 8 chat client":

В поиске много результатов. Выберите тот, который соответствует вашим потребностям. Вы даже можете изменить поиск Google, чтобы показывать результаты только в прошлом году, если хотите.

Ответ 2

Я сделал это, когда изучал Java, что-то около 10 лет назад. Он работает:

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

В настоящее время я не горжусь кодом, но он действительно работает.

Edit:

Как некоторые предложили, я сделал некоторые улучшения кода (рефакторинг) и разместил проект на GitHub: https://github.com/jaumzera/javasocketchat

Ответ 3

Hm, у меня возникло соблазн направить вас на Java-реализацию сервера, реализующего протокол imap (например, gavamail). Но это, конечно, также может квалифицироваться как "старый" код и наверняка убьет ваше ожидание (за нестандартное решение). Тем не менее, это правильная ссылка, выполняющая ваши (краткие) спецификации.

Что у нас есть?

Нам нужно решение, которое должно быть в java. Он должен внедрить базовую систему обмена мгновенными сообщениями.

Более поздняя проблема проблематична, поскольку она охватывает действительно широкий диапазон функциональных возможностей. Но "базовые", по-видимому, допускают минимальное решение.

Итак, какова минимальная система обмена мгновенными сообщениями? Попробуйте сделать следующее:

  • клиент, который отправляет и получает сообщения.
  • сервер, который хранит отправленные сообщения для (более позднего) поиска

Нам также понадобится политика того, как клиент будет идентифицировать правильный сервер. Наиболее тривиальное решение для более позднего аспекта - использование "центрального" сервера с хорошо известным адресом. Для более сложных случаев нам нужно, чтобы серверные и/или клиентские функции распределялись по нескольким экземплярам и разрабатывали схему или политику для идентификации надлежащего экземпляра (ов) для связи.

Мы оставляем более сложную семантику, например, если разные пользователи или сообщения связаны с системой категорий или тегов.

Теперь мы имеем дело с двумя компонентами:

Сервер, реализующий две точки входа:

  • POST_MESSAGE
    получить сообщение о клиенте и сохранить его для последующего поиска
    Это немедленно задает вопрос о том, где хранить такие сообщения (в базе данных или в файловой системе настойчивость или просто в памяти для семантики сообщений в реальном времени до тех пор, пока семантика сервера)

  • LOOKUP_MESSAGE
    выберите подходящее сообщение из сохраненных (желательно непрочитанное) и вернитесь к вызывающему.
    Это также может вернуть набор сообщений (но подумайте об ограничении такого набора для случаев, когда вызывающий абонент имеет серьезное отставание от сообщений)
    Возможно, потребуется отслеживать сообщения, которые уже были прочитаны, либо путем маркировки сообщений, либо путем отслеживания статуса клиента. Это может быть даже просто, как сохранить время или порядковый номер последнего сообщения и отправить эту информацию вместе с запросом LOOKUP_MESSAGE.

Клиент должен взаимодействовать с пользователем, с одной стороны, и службой, с другой стороны.

Появится новое сообщение от пользователя (вероятно, на явный запрос (например, кнопка отправки) и вызовет службу POST_MESSAGE на соответствующем сервере.

Он также будет (вероятно, регулярно, также может быть на явном запросе (например, пользователь запускает клиент)) опросить сервер для новых сообщений. (В качестве альтернативы вы можете создать отдельную службу уведомлений, которая используется сервером для уведомления клиента о новых сообщениях. То, что подходит вашим "потребностям", не соответствует вашему вопросу.)

Что это.

Таким образом, любой пример клиентского/серверного приложения на основе TCP станет идеальной отправной точкой для прямой реализации.

Следует также упомянуть, что вы можете сократить логику спецификации внутри клиента и делегировать взаимодействие пользователя со стандартным браузером и внедрить логику клиентского приложения в экземпляр (веб-сервер) (вместе или отдельно от серверной части). Тем не менее, вы по-прежнему будете иметь обе (клиент/сервер) логические функции в соответствии с вышеуказанной минимальной спецификацией.

Еще один аспект, о котором вы должны знать:

С некоторыми комментариями вы упомянули атрибуты "хозяин" и "гость", имеющиеся в текущих примерах мессенджеров. На самом деле это логическая структура системы тегов, предоставляемой этими мессенджерами. Сообщения по-прежнему отправляются с клиента на сервер и затем извлекаются другими клиентами. Независимо от того, видит ли клиент сообщение, клиент имеет право на конкретный тег. Например, размещение сообщения контакту от вашего пользователя (пользователя b) просто тегирует сообщение с тегом "for_user_b" и, как таковое, оно видимо только плакату и любому, кому также разрешено читать сообщения тега "for_user_b" (пользователь b в наш пример). Итак, имейте в виду, что логическая структура системы обмена сообщениями определяется политикой доступа и распространения, а не структурой физического распределения!

Ответ 4

Я даже не уверен, что этот вопрос все еще используется или что мне понравилось, и я подумал:

почему бы и нет?

Вот моя реализация, такая же простая, как и она, но не забывая о фундаментальных частях. Написанная в чистой Java, использует, среди прочих, Sockets, Threads и SynchronizedList:

SimpleChat.java(Главная)

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

Как говорит "showUsage()", используйте эту программу следующим образом:

java SimpleChat.java username listening_port target_ip target_port

Пример:

java SimpleChat.java Supuhstar 1234 127.0.0.1 1234

[Чтобы поговорить с самим собой]

Ответ 5

Я думаю, вы должны уточнить некоторые детали относительно того, что именно вы подразумеваете под "базовой программой обмена мгновенными сообщениями" и каковы ваши цели на самом деле в отношении этого проекта.

В комментарии 2011 года вы упомянули, что не должно быть "центрального центра", но в более недавнем комментарии вы говорите, что вам нужно что-то большее в соответствии со Skype или iMessage, где пользователи не должны знать, какие peer - это сервер... Технически это возможно (используя такие протоколы, как mdns, dlna или ssdp), чтобы программа прозрачно просматривала локальную сеть для потенциальных существующих узлов сервера и подключалась ли она к серверному одноранговому узлу, если таковая имеется, или установить себя как локальный сервер для подключения других узлов к нему. Это, например, как работает протокол Apple iChat Bonjour. Это, однако, довольно сложное решение для реализации права и, безусловно, не соответствует тому, что делается с помощью современных программ обмена массовыми рынками.

Также установление прямой одноранговой связи между пользователями создает несколько практических проблем (особенно из-за брандмауэров и NAT, но есть также проблемы конфиденциальности и безопасности). Таким образом, большинство протоколов передают большинство сообщений через центральный сервер и согласовывают прямое соединение только для передачи файлов и аудио/видео вызовов.

По всем этим причинам, если вы не смотрите только на пример локальной сетевой связи между двумя хостами, вам наверняка понадобятся две разные программы: сервер и клиент.

Тогда, если предположить, что мое предположение верно, есть еще два вопроса, которые необходимо уточнить. Во-первых, есть ли у вас фактическая причина самостоятельно составить протокол, или было бы приемлемо скорее реализовать существующий протокол (например, XMPP, IRC, SIMPLE... есть тонны). Несмотря на то, что эти протоколы сначала могут выглядеть очень сложными, почти всегда возможно реализовать только подмножество этих функций/сообщений протокола. Проектирование наивного сетевого протокола самостоятельно не так сложно, но есть много потенциальных ошибок (в основном неэффективность, неполнота и другие незначительные проблемы), которые вам придется пройти. Возможно, это именно то, к чему вы специально предназначаетесь (то есть приобретаете опыт при разработке сетевого протокола с нуля), но если это не так, вам следует серьезно выбрать реализацию существующего протокола. Действительно, работа с существующим протоколом не только позволит избежать таких ошибок дизайна, но еще лучше, вы получите значительные знания о том, как другие (обычно опытные разработчики протоколов) фактически разрешили проблемы, с которыми они встречались на этом пути. Использование существующего протокола также упростит и поможет вам разработать эту программу, учитывая, что вы, например, сможете самостоятельно тестировать свои клиентские и серверные программы, подключаясь к официальной реализации клиент/сервер. Вы также сможете использовать выходные протоколы-декодеры в средствах обхода трафика для отладки сообщений, проходящих через.

Второй важный вопрос: насколько реалистичен вам серверная программа, и что самое главное в отношении стойкости. Должен ли сервер поддерживать постоянный список пользователей и проверять их подлинность? Должен ли сервер хранить список разрешенных контактов для каждого пользователя? Должен ли сервер разрешать хранить сообщения, направленные на однорангового узла, который в настоящее время отключен или который не может быть достигнут в тот момент? Реальные серверы обмена сообщениями обычно делают такие вещи, и хотя внедрение таких механизмов не очень сложно, их лучше всего рассмотреть на ранних этапах разработки архитектуры программы. Например, если вы решите, что эти функции действительно желательны, тогда вам может быть гораздо интереснее сразу же создать ваш сервер вокруг механизма постоянной очереди сообщений, такого как ActiveMQ...

Я знаю, что это не те примеры, о которых вы просили, но я все еще надеюсь, что эти мысли помогут вам.

Ответ 6

Как уже было сказано, есть много вещей, которые вам нужно, чтобы поработать в реальном чате. Но я верю, что вы хотите что-то начать. И если вы знаете адрес и порт другого "клиента", это легко.
Внедрение экстремально простого "чата"

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