Java 多人聊天室

Java实训做的多人聊天室,效果如图:

 功能:

  • 能够实现多个客户端之间的互相聊天,服务端来负责接收数据和转发数据。
  • 每个客户端可以自己设置和修改自己的名称,并传给服务器让其他客户端知道。
  • 当有客户端加入和退出时,群聊里每个客户端都有提示。(优化了异常检测,意外断开终止时也能不报错并提示用户xxx退出聊天室)
  • 当服务器异常断开或正常退出,客户端都可以提示并且程序不报错。
  • 服务端能够实时的显示在线的人数、  

关键技术: 

  • 集合(服务端并不是对一个客户端进行操作,是多个客户端之间来回通信充当转发数据的功能。服务端需要创建一个集合用来存放服务端连接对象。这样只要监听到客户端发送过来的数据,就可以遍历整个集合,转发给每一个客户端)
  • 多线程(客户端通过主线程来发送数据,还需要再启动一个线程来不断循环接收服务端转发过来的消息。服务端主线程使用while循环来不停的接收socket连接,然后为每个客户端的socket通道分配一个子线程接收)
  • 网络TCP传输的嵌套字socket(实现客户端和服务端之间的数据传输,选择TCP更安全可靠)
  • IO流(通道间的数据传输需要用到io流)

服务端代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Iterator;

public class ServerChar extends JFrame {
    private static final int PORT = 8888;
    JTextArea jta = new JTextArea();
    JScrollPane jsp = new JScrollPane(jta);
    private JPanel south = new JPanel(); //面板
    private JLabel jb = new JLabel();
    private JButton stopBtn = new JButton("停止服务器");

    private boolean isStart = false;//服务器是否启动

    private ServerSocket serverSocket = null;

    private ArrayList<ClientConn> clientConn = new ArrayList<>();
    private int peopleOnline = 0;

    public ServerChar(){
        this.setTitle("服务端");
        this.add(jsp, BorderLayout.CENTER);
        jta.setEditable(false); //不能输入
        south.add(jb);
        jb.setText("聊天室在线人数: "+peopleOnline+"   ");
        south.add(stopBtn);
        this.add(south,BorderLayout.SOUTH);

        stopBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                isStart = false;
                    try {
                        if (serverSocket != null){
                            serverSocket.close();
                            isStart = false;
                        }
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }finally {
                        System.exit(0);
                    }
            }
        });

        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                isStart = false;
                try {
                    if (serverSocket != null){
                        serverSocket.close();
                        isStart = false;
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }finally {
                    System.exit(0);
                }
            }
        });
        this.setBounds(200,100,500,500);
        this.setVisible(true);
        startServer();
    }

    //服务器启动的方法
    public void startServer()  {
        try{
            try {
                serverSocket = new ServerSocket(PORT);
                isStart = true;
                jta.append("服务器已启动,等待客户端连接..."+System.lineSeparator());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            while(isStart){
                Socket socket = serverSocket.accept();
                clientConn.add(new ClientConn(socket));
                jta.append("客户端"+"["+socket.getInetAddress()+"-"+socket.getPort()+"]"+"加入聊天室"+System.lineSeparator());
                jb.setText("聊天室在线人数: "+(++peopleOnline)+"   ");
            }
        }catch (SocketException e){
            System.out.println("服务器中断");
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    //这个对象属于服务器端的连接对象
    class ClientConn implements Runnable{
        Socket socket = null;
        //构造器 传入socket
        public ClientConn(Socket socket){
            this.socket=socket;
            (new Thread(this)).start();
        }

        @Override//同时接收客户端信息
        public void run() {
            String name = socket.getInetAddress()+"-"+socket.getPort();
            try {
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            while (isStart){
                //读过来
                String str = dis.readUTF();
                if (str.startsWith("##")){
                    name = str.substring("##".length(),str.length());
                    jta.append("客户端"+"["+socket.getInetAddress()+"-"+socket.getPort()+"]"+"将名称设置为"+"["+name+"]"+System.lineSeparator());
                    Iterator<ClientConn> it = clientConn.iterator();
                    while(it.hasNext()) {
                        ClientConn o = it.next();
                        o.send(name+"加入了聊天室"+System.lineSeparator());
                    }
                }else{
                    jta.append("["+name+"]"+"说:"+str+System.lineSeparator());
                    //发过去
                    String strSend = "["+name+"]"+"说:"+str+System.lineSeparator();
                    //要遍历所有的客户端来发送 遍历ccList 调用send方法
                    Iterator<ClientConn> it = clientConn.iterator();
                    while(it.hasNext()) {
                        ClientConn o = it.next();
                        o.send(strSend);
                    }
                }
                }
            } catch (SocketException e){
                jta.append("客户端"+"["+name+"]"+"下线了"+System.lineSeparator());
                Iterator<ClientConn> it = clientConn.iterator();
                while(it.hasNext()) {
                    ClientConn o = it.next();
                    o.send(name+"退出了聊天室"+System.lineSeparator());
                }
                jb.setText("聊天室在线人数: "+(--peopleOnline)+"   ");
            } catch (IOException e) {
            e.printStackTrace();
            }
    }

        //对每个连接发送信息的方法
        public void send(String str){
            try {
                DataOutputStream dos = new DataOutputStream(this.socket.getOutputStream());
                dos.writeUTF(str);
                dos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new ServerChar();
    }

}

 客户端代码: 

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;

public class ClientChar extends JFrame implements KeyListener{
    public static void main(String[] args) throws IOException {
        new ClientChar().init();
    }
    //属性
    private boolean isConn = false;
    String name = "";

    private JTextArea jta;//文本域
    private JScrollPane jsp;//滚动条
    private JPanel jp;//面板
    private JTextField jtf;//文本框
    private JButton jb1;//发送信息
    //通道
    private Socket socket = null;
    private static final String CONNSTR = "127.0.0.1";
    private static final int CONNPORT = 8888;
    //流
    private DataOutputStream dos = null;

    //构造器
    public ClientChar(){
        //初始化组件
        jta = new JTextArea();
        jta.setEditable(false);//设置为不可编辑
        jsp = new JScrollPane(jta); //让文本域实现滚动效果
        //面板
        jp = new JPanel();
        jtf = new JTextField(15);
        jb1 = new JButton("发送信息");
    }

    public void init(){
        //将文本框和按钮添加到面板中
        jp.add(jtf);
        jtf.addKeyListener(this);
        jp.add(jb1);
        jb1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取文本框内容
                String text = jtf.getText();
                sendMessage(text);
                if (text.length()==0){
                    return;
                }
                //拼接内容并发送
                jtf.setText("");
            }
        });

        //将滚动条与面板全部添加到窗口中
        this.add(jsp, BorderLayout.CENTER);
        this.add(jp, BorderLayout.SOUTH);

        //设置窗口
        this.setBounds(700, 300, 300, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);

        try {
            socket = new Socket(CONNSTR,CONNPORT);
            isConn = true;
            name = JOptionPane.showInputDialog(this.getContentPane(), "请输入你的用户名:");
            dos = new DataOutputStream(socket.getOutputStream());
            dos.writeUTF("##"+name);
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        this.setTitle("聊天室-"+name);
        //启动线程类接收
        new Thread(new Receive()).start();

    }

    //发送信息到服务器的方法
    private void sendMessage(String str) {
        try {
            if(jtf.getText().length()==0){
                return;
            }
            dos = new DataOutputStream(socket.getOutputStream());
            dos.writeUTF(str);
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode()==KeyEvent.VK_ENTER){
            //回去输入框的内容
            String text = jtf.getText();
            //调用发送信息的方法
            sendMessage(text);
            //发送完后让输入框为空
            jtf.setText("");
        }
    }

    //多线程的类,实现了Runnable接口 用来接收
    class Receive implements Runnable{
        @Override
        public void run() {
            try {
                while (isConn) {
                    DataInputStream dis = new DataInputStream(socket.getInputStream());
                    String str = dis.readUTF(); //从管道读出服务端转发的数据 放到文本框里面
                    jta.append(str);
                    }
                } catch (SocketException e) {
                    System.out.println("服务器终止");
                    jta.append("服务器终止"+System.lineSeparator());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }
}

如果有帮助的话麻烦点赞收藏,谢谢。你的支持是对我最大的鼓励。

  • 48
    点赞
  • 247
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
Java多人聊天室相对来说并不太难。Java是一种适用于网络应用程序的高级编程语言,具有简洁、可靠、跨平台等特点,非常适合用于开发多人聊天室。以下是一些常见的开发要点: 首先,需要了解Java的网络编程知识。Java提供了丰富的库和类,例如Socket、ServerSocket等,利用这些类可以实现网络通信的各种功能。 其次,需要理解服务器和客户端的工作原理。在多人聊天室中,服务器起到中转的作用,接收客户端发来的消息并转发给其他客户端。客户端则负责发送消息和接收其他客户端的消息。 再次,需要考虑多线程编程。在聊天室中,服务器需要同时处理多个客户端的连接和消息发送。为了实现这一点,可以使用多线程技术,使得服务器能够并发处理客户端的请求。 还要考虑一些安全性问题,例如用户身份验证和消息加密。可以使用用户名和密码来验证用户身份,并使用加密算法对消息进行加密,确保用户信息的安全性。 此外,为了用户体验,还可以考虑使用图形界面(GUI)来实现聊天室的操作界面,增加用户交互性。 总之,相对于其他复杂的网络应用程序,Java多人聊天室开发相对来说并不太难。需要掌握一些基本的网络编程知识和多线程编程技术,同时考虑一些安全性和用户体验方面的问题。掌握这些要点,相信能够成功地开发出一个功能完善、稳定可靠的Java多人聊天室

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卒获有所闻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值