千家信息网

怎么用Java Socket实现聊天室

发表于:2024-11-25 作者:千家信息网编辑
千家信息网最后更新 2024年11月25日,这篇文章主要介绍"怎么用Java Socket实现聊天室",在日常操作中,相信很多人在怎么用Java Socket实现聊天室问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答
千家信息网最后更新 2024年11月25日怎么用Java Socket实现聊天室

这篇文章主要介绍"怎么用Java Socket实现聊天室",在日常操作中,相信很多人在怎么用Java Socket实现聊天室问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"怎么用Java Socket实现聊天室"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

项目需求分析

需要完成一个简单聊天工具的界面及功能,实现服务器中转下的多客户端之间的通信,系统完成的功能有

  • 程序启动后能看到当前有那些机器上线,可弹出对话聊天框,可以在其中编辑要发送的聊天信息,并进行发送

  • 一旦某个网内的机器上线了,可即时通知,并能更新用户界面的用户列表

  • 双击某个列表项时,可弹出对话聊天框,可以在其中编辑要发送的信息并发送

  • 聊天界面人性化,下面时发送框,上面有已有聊天记录,并借助滚动条看到当次所有聊天记录

  • 当有人向本机器发送消息时,可显示用户接收到的信息,并且显示是谁所发,同时进行信息的回复

基础分析

首先这是一个聊天工具,使用的是C/S结构,要模拟就要使用net的Scocket和ServerSocket模拟客户端和服务端

这里综合运用了多种知识,已经不再是简单的java SE知识,其中界面编程占据主要代码,这里可以贴几张图看看效果,这是我肝了2天才肝完的,这里已经可以实现多态设备的连接

分为3个包

Sever包主要是服务器的相关代码,主要是实现与用户的交互

Dao包是模拟的数据库包,存储所有的用户信息,实现增删改的操作

Client是客户代码包,只要在电脑上运行这里的代码,就可以出现客户端界面,约定好ip和端口号就可以通信了。这里就真正实现了客户端型软件,只是软件功能简单,可以使用web编程实现另外一种架构
可以来看一下界面

再来看一下客户端和服务端的交流

项目部分代码摘要

Dao的链表存储实现
package Dao;/** * 演示程序为了简化就不用数据库存储,使用单链表完成数据库各项功能 * 这里一定要写测试代码检查各项功能是否可用 * 最开开始我测试了add,del,find功能,却没有测试getCount功能,结果存在问题,后面突然放开测试才发现错误 */public class UserLinkList {        private  Node head;        private int count;        public boolean addUser(Node client)        {                if(head == null)                {//头节点也存储数据                        head = client;                        count++;                        return true;                }                else {                        Node p = head;                        for(;p.next != null;p = p.next);                        {                                p.next = client;                                count++;                                return true;                        }                }        }                public int getCount() {                return count;        }                public Node findUser(String name)        {                Node p = head;                while(p != null )//p.next != null没有包含最后一个结点                {                        if(p.username.equals(name))                        {                                return p;                        }                        p = p.next;                }                return null;        }                public Node findUser(int index)        {                int pos = 0;                Node p = head;                while(p != null&& pos < index)                {                        p = p.next;                        pos++;                }                if(p != null&& pos == index)                {                        return p;                }                return null;        }                public boolean delUser(Node client)        {//删除后长度也要减少                Node p = head;                if(p.username.equals(client.username))                {//删除头结点                        head = head.next;                        count--;                        return true;                }                while(p != null)                {//忘记循环了                        if(p.next.username.equals(client.username))                        {                                p.next = p.next.next;                                count--;                                return true;                        }                        p = p.next;                }                return false;        }                /**         * 这里可以设置一个显示的方法,供检查使用         */        public void display() {                Node p = head;                int pos = 1;                while(p != null)                {                        System.out.println("第"+pos + "个用户"+p.username);                        p = p.next;                        pos++;                }        }}/*              public static void main(String[] args) {//经过测试发现没有问题,可以正常使用                Node client1 = new Node();                client1.username = "张三";                Node client2 = new Node();                client2.username = "李四";                Node client3 = new Node();                client3.username = "王五";                //其他的就不测试了,反正该项就可以测试了                UserLinkList userLinkList = new UserLinkList();//自动初始化                userLinkList.addUser(client1);                userLinkList.addUser(client2);                userLinkList.addUser(client3);//              userLinkList.display();                Node node = userLinkList.findUser(0);                userLinkList.delUser(node);                userLinkList.display();                System.out.println(userLinkList.getCount());        }*/

现在编写这段代码应当是非常简单的,注意一定要测试

ServerListen

简单看一下这个监听线程,可以监听用户是否上线

package Server;/** * @author OMEY-PC *本程序的作用是实现服务器侦听的线程化,其中run方法通过client = new Node();创建一个客户端对象,通过client.socket = server.accept来设定接口,通过client.input *output来建立输入输出流 */import java.io.*;import java.net.*;import Dao.*; //连接数据import javax.swing.*;public class ServerListen extends Thread{        ServerSocket server;        JComboBox combobox;        JTextArea textarea;        JTextField textfield;        UserLinkList userLinkList;        Node client;        ServerReceive recvThread;        public boolean isStop;        /**         * 聊天服务端的用户上下线侦听类         */        public ServerListen(ServerSocket server,JComboBox combobox,JTextArea textarea,JTextField textField,UserLinkList userLinkList) {                this.server = server;                this.combobox = combobox;                this.textarea = textarea;                this.textfield = textField;                this.userLinkList = userLinkList;                isStop = false;        }        @Override        public void run() {                while(!isStop && !server.isClosed())//没有停止服务                {                        try {                                client = new Node();                                client.socket = server.accept();//用来指代所连接的客户端                                client.output = new ObjectOutputStream(client.socket.getOutputStream());                                client.output.flush();                                client.input = new ObjectInputStream(client.socket.getInputStream());                                client.username = (String)client.input.readObject();                                //显示提示信息                            combobox.addItem(client.username);//改成用户名                            userLinkList.addUser(client);                            textarea.append("用户" + client.username+"上线"+"\n");                            textfield.setText("在线用户"+ userLinkList.getCount()+"人\n");                                                        recvThread = new ServerReceive(textarea,textfield,combobox,client,userLinkList);                            recvThread.start();//启动线程                        }catch (Exception e) {                                e.printStackTrace();                        }                }        }}
ServerReceive

该线程实现服务器与用户之间的信息交互

package Server;/** * @author OMEY-PC *服务器收发消息的类 */import java.net.ServerSocket;import javax.swing.*;import Dao.*;public class ServerReceive extends Thread{        JTextArea textarea;//消息展示域        JTextField textfield;//文本输入域        JComboBox combobox; //复选框        Node client;//用户        UserLinkList userLinkList;        public boolean isStop;        public ServerReceive(JTextArea textarea, JTextField textfield, JComboBox combobox, Node client,                        UserLinkList userLinkList) {                this.textarea = textarea;                this.textfield = textfield;                this.combobox = combobox;                this.client = client;                this.userLinkList = userLinkList;                isStop = false;        }                @Override        public void run()        {                //向所有人发送用户的列表                sendUserList();                while(!isStop && !client.socket.isClosed())                {                        try {//类型,对谁,状况,行为,信息                                String type = (String)client.input.readObject();                                if(type.equalsIgnoreCase("聊天信息"))                                {                                        String toSomebody =(String)client.input.readObject();//从客户端接收信息                                        String status = (String)client.input.readObject();                                        String action = (String)client.input.readObject();                                        String message = (String)client.input.readObject();                                        String msg = client.username+" "+ action + "对"+ toSomebody +" 说 " + message + "\n";//接收的消息                                        if(status.equalsIgnoreCase("悄悄话"))                                        {                                                msg = "[悄悄话]" + msg; //若为悄悄话,就在前面加上标识                                        }                                        textarea.append(msg);                                        if(toSomebody.equalsIgnoreCase("所有人"))                                        {                                                sendToAll(msg);//这里是接受的用户消息,和之前的向所有人发消息不一样                                        }                                        else {//向用户发消息                                                try {                                                        client.output.writeObject("聊天信息");                                                        client.output.flush();//刷新流                                                        client.output.writeObject(msg);                                                        client.output.flush();                                                }catch (Exception e) {                                                        e.printStackTrace();                                                }                                                Node node = userLinkList.findUser(toSomebody);                                                if(node != null)                                                {                                                        node.output.writeObject("聊天信息");                                                        node.output.flush();                                                        node.output.writeObject(msg);//向选定信息发送信息                                                        node.output.flush();//刷新输出流缓冲区中的信息                                                }                                        }                            }                                else if(type.equalsIgnoreCase("用户下线"))                                {                                        Node node = userLinkList.findUser(client.username);                                        userLinkList.delUser(node);                                        String msg = "用户"+ client.username +"下线\n";                                        int count = userLinkList.getCount();                                        combobox.removeAllItems();                                        combobox.addItem("所有人");                                        int i = 0;                                    while(i < count)                                    {                                            node = userLinkList.findUser(i);                                            if(node == null)                                            {                                                    i++;                                                    continue;                                            }                                            combobox.addItem(node.username);                                            i++;                                    }                                        combobox.setSelectedIndex(0);//选择第一个,所有人                                        textarea.append(msg);                                        textfield.setText("在线用户"+ userLinkList.getCount() +"人\n");                                                                                sendToAll(msg);                                        sendUserList();//重新发送用户列表                                        break;                                }                    }catch (Exception e) {                                e.printStackTrace();                        }            }        }        /**         * 向所有人发送消息         */        public void sendToAll(String msg)        {                int count = userLinkList.getCount();                int i = 0;                while(i < count)                {//给用户列表中的每一个人都发送消息                        Node node = userLinkList.findUser(i);                        if(node == null)                        {                                i++;                                continue;                        }                        try {//输出流                                node.output.writeObject("聊天信息");                                node.output.flush();                                node.output.writeObject(msg);//聊天消息写入输出流(to client)                                node.output.flush();                        }catch (Exception e) {                                e.printStackTrace();                        }                        i++;                }        }        /**         * 向所有人发送用户列表         */        public void sendUserList() {                String userList = "";                int count = userLinkList.getCount();                int i = 0;                while(i < count)                {                        Node node = userLinkList.findUser(i);                        if(node == null)                        {                                i++;                                continue;                        }                        userList += node.username;                        userList += "\n";                        i++;                }                i = 0; //给每个人发送消息                while(i < count)                {                        Node node = userLinkList.findUser(i);                        if(node == null)                        {                                i++;                                continue;                        }                        try {                                node.output.writeObject("用户列表");                                node.output.flush();                                node.output.writeObject(userList);                                node.output.flush();                        }catch (Exception e) {                                e.printStackTrace();                        }                }                i++;        }       }/** * 本程序可以实现通过线程向所有人发送消息,用户列表,以及向选定的人发送聊天消息等,主要是是实现服务端收发消息的线程化,其中sendUserList()发送列表, * client.input.redObject()获取客户端发送到服务端的消息,通sendToAll(),将发送到发送到所有人的信息发送到各个客户端 */
再看一下客户端的ClientReceive

该线程是实现客户端与系统之间的信息交互,注解丰富

package Client;import java.io.*;import java.net.*;import javax.swing.*;public class ClientReceive extends Thread{        private JComboBox combobox;        private JTextArea textarea;        Socket socket;        ObjectOutputStream output;        ObjectInputStream input;        JTextField showStatus;        public ClientReceive(JComboBox combobox, JTextArea textarea, Socket socket, ObjectOutputStream output,                        ObjectInputStream input, JTextField showStatus) {                this.combobox = combobox;                this.textarea = textarea;                this.socket = socket;                this.output = output;                this.input = input;                this.showStatus = showStatus;        }                @Override        public void run() {//从服务端获得消息                while(!socket.isClosed())                {                        try {                                String type = (String)input.readObject();//获得流,read读取信息                                if(type.equalsIgnoreCase("系统信息"))                                {                                        String sysmsg = (String)input.readObject();                                        textarea.append("系统信息" + sysmsg);                                }                                else if(type.equalsIgnoreCase("服务关闭"))                                {                                        output.close();                                        input.close();                                        socket.close();                                        textarea.append("服务器已经关闭!\n");                                        break;                                }                                else if(type.equalsIgnoreCase("聊天信息"))                                {                                        String message = (String)input.readObject();                                        textarea.append(message);                                }                                else if(type.equalsIgnoreCase("用户列表"))                                {                                        String userlist = (String)input.readObject();                                        String[] usernames = userlist.split("\n"); //用换行符分隔                                        combobox.removeAll();//先移出去                                        int i = 0;                                        combobox.addItem("所有人");                                        while(i < usernames.length)                                        {                                                combobox.addItem(usernames[i]);                                                i++;                                        }                                        combobox.setSelectedIndex(0);                                        showStatus.setText("在线用户"+ usernames.length +" 人");                                }                        }catch (Exception e) {                                e.printStackTrace();                        }                }        }}

其余的界面的部分就不放出来了,代码太长,每个都有400多行,如果有兴趣,就到我的gitee上去浏览,后面会放上地址

项目问题

选择框中出现的不是用户名

查找相应模块发现是因为addItem中添加的时结点,而不是结点中的username,修改后正常

服务端点击消息发送按钮没有反应

查找监听器部分,发现监听器监听该部分代码写错,将button又写成sysMessage

不能显示在线人数

查找侦听线程,启动客户端发现抛出异常

Cannot invoke "javax.swing.JTextField.setText(String)" because "this.textfield" is null

textfield为空,查找问题源头;发现在构造方法中:the assignmen to variable has no effect;这是因为单词拼写错误,编译器并没有报错

服务端退出时没有消息

系统报错

Cannot read field "input" because "node" is null

意识到问题出在链表上,系统要求从0开始,而链表中的序号是从1开始的,修该链表中的findUser中的pos为0就解决

到此,关于"怎么用Java Socket实现聊天室"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0