java网络编程TCP通信实战:共享聊天室

news2024/11/22 16:00:30

目录

创建服务端 建立ServerSocket服务端。

接下来就是服务端线程的编写

前端ui登录界面

客户端线程

群聊界面


package server;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class Server {
    //定义一个集合容器存储所有登录进来的客户端管道,以便将来群发消息给他们
    // 定义一个Map集合,键是存储客户端的管道,值是这个管道的用户名称。
    public static  final Map<Socket,String> onlinesSockets=new HashMap<>();
    public static void main(String[] args)  {
        System.out.println("启动服务端程序...");
        try {
            ServerSocket serverSocket = new ServerSocket(Constant.SERVER_PORT);
            while (true) {
                System.out.println("等待客户端连接...");
                Socket socket = serverSocket.accept();
                System.out.println("一个客户端连接成功...");
                new ServerReaderThread(socket).start();
            }
        }catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

创建服务端 建立ServerSocket服务端。

使用while循环不断接受客户端发来的请求,再将获得的管道交给一个线程独立完成各自的任务,实现多个客户端访问。将所有管道放在一个map集合中,以便将来群发消息给他们。

接下来就是服务端线程的编写

package server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;

public class ServerReaderThread extends Thread {
    private Socket s;
    public ServerReaderThread(Socket s)
    {
        this.s = s;//设置有参构造器,将socket传入
    }
    @Override
    public void run() {
        try {

            //接收的消息可能有很多中类型:1登录消息(包含名称)2 群聊消息 3 私聊消息
            //所以客户端必须声明协议发送消息,
            //客户端发1、2、3代表登录消息、群聊消息、私聊消息
            //先从socket管道中接受客户端发送来的消息类型编号
            DataInputStream dis = new DataInputStream(s.getInputStream());
           while (true) {
               int type = dis.readInt();
               switch (type) {
                   case 1:
                       //登录消息,接下来接收名称数据,在更新全部在线客户端的在线人数列表。
                       String nickname = dis.readUTF();
                       Server.onlinesSockets.put(s, nickname);
                       //更新在线人数列表
                       updateClientOnlineCUserslist();
                       break;
                   case 2:
                       //群聊消息 接下来接受群聊消息内容,再把群聊消息发送给全部在线客户端。
                       String msg = dis.readUTF();
                       sendMsgAll(msg);
                       break;
                   case 3:
                       //私聊消息 接受私聊消息内容,再把私聊消息发送给指定的客户端。
                       break;

               }
           }
        } catch (Exception e) {
            System.out.println("客户端下线了:" + s.getInetAddress().getHostAddress());
            Server.onlinesSockets.remove(s);
            updateClientOnlineCUserslist();
        }

    }
    private void updateClientOnlineCUserslist()
    {

        Collection<String>onLineUsers = Server.onlinesSockets.values();
        for(Socket socket:Server.onlinesSockets.keySet())
        {
            try {
                DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
               dos.writeInt(1);//告诉群聊列表消息的类型
                dos.writeInt(onLineUsers.size());
                for(String nickname:onLineUsers)
                {
                    dos.writeUTF(nickname);
                }
                dos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    private void sendMsgAll(String msg)
    {
       StringBuilder sb=new StringBuilder();
       String name=Server.onlinesSockets.get(s);
       //获取当前时间
       LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss EEE a");
        String time = dtf.format(now);
        String sb1=sb.append(name).append(" ").append(time).append("\r\n").append(msg).append("\r\n").toString();
        for(Socket socket:Server.onlinesSockets.keySet())
        {
            try {
                DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                dos.writeInt(2);//告诉群聊消息的类型
                dos.writeUTF(sb1);
                dos.flush();
            }catch (Exception e)
                {
                e.printStackTrace();
            }
        }
    }
}

前端ui登录界面

package com.acheng.ui;

import Client.ConstantClient;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataOutputStream;
import java.net.Socket;

public class ChatEntryFrame extends JFrame {
    private JTextField nicknameField;
    private JButton enterButton;
    private JButton cancelButton;
    private Socket socket;

    public ChatEntryFrame() {
        // 设置窗口标题
        setTitle("局域网聊天 - 进入界面");
        setSize(300, 150);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null); // 窗口居中

        // 创建面板
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(3, 1)); // 3行1列的网格布局

        // 创建昵称输入框
        nicknameField = new JTextField();
        nicknameField.setBorder(BorderFactory.createTitledBorder("昵称"));
        panel.add(nicknameField);

        // 创建按钮面板
        JPanel buttonPanel = new JPanel();
        enterButton = new JButton("登录");
        cancelButton = new JButton("取消");

        // 添加按钮事件
        enterButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String nickname = nicknameField.getText();//获取昵称
                if (!nickname.isEmpty()) {
                    // TODO: 处理进入聊天逻辑
                    //立即发送登录消息给服务端程序。
                    //请求一个socket管道,建立与服务端的连接。
                   try {
                       login(nickname);
                       new ChatApp(nickname, socket);
                   }catch (Exception e1)
                       {
                       e1.printStackTrace();
                   }
                    System.out.println("进入聊天,昵称: " + nickname);
                    dispose(); // 关闭当前窗口
                } else {
                    JOptionPane.showMessageDialog(null, "请输入昵称!", "错误", JOptionPane.ERROR_MESSAGE);
                }
            }
        });

        cancelButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0); // 退出程序
            }
        });

        buttonPanel.add(enterButton);
        buttonPanel.add(cancelButton);
        panel.add(buttonPanel);

        // 将面板添加到窗口
        add(panel);
        setVisible(true);

    }
 public void login(String nickname) throws Exception
 {

      socket=new Socket(ConstantClient.CLIENT_IP, ConstantClient.CLIENT_PORT);
     DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
     dos.writeInt(1);
     dos.writeUTF(nickname);
     dos.flush();



 }

 

}

客户端线程

package com.acheng.ui;

import java.io.DataInputStream;
import java.net.Socket;

public class ClientReaderThread extends Thread {
    private Socket s;
    private ChatApp frame;
    private DataInputStream dis;
    public ClientReaderThread(Socket s, ChatApp frame )
    {

        this.s = s;
        this.frame = frame;

    }
    @Override
    public void run()
    {

        try {
            dis = new DataInputStream(s.getInputStream());
            while (true) {
                //从socket管道中读取服务端发送来的消息
                //客户端发1、在线人数更新的数据 2 群聊消息
                //先从socket管道中接受客户端发送来的消息类型编号
                int type = dis.readInt();
                //根据消息类型编号,判断客户端发送的是哪种消息
                switch (type)
                {
                    case 1:
                        //服务端发来的在线人数更新消息
                        updateClientOnlineUserList(dis);
                        break;
                    case 2:
                        //群聊消息,从socket管道中读取消息内容
                        getMsgtoWin();
                        //将群聊消息显示到聊天界面上

                        break;
                    case 3:
                        //私聊消息,从socket管道中读取消息内容
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    private void updateClientOnlineUserList(DataInputStream dis)throws Exception
    {

        //1、先从socket管道中读取在线人数
        //读取有多少个在线用户
        int count =dis.readInt();
        //循环读取多个用户信息
        String[] names = new String[count];
        for(int i=0;i<count;i++)
        {
            String nickname = dis.readUTF();
            names[i] = nickname;
        }
        //将集合中的数据显示到客户端的在线用户列表中
        frame.updateOnlineUsers(names);
    }
    private void getMsgtoWin()throws Exception
    {
        String msg = dis.readUTF();
        frame.setMsgTowin(msg);
    }
}

群聊界面

package com.acheng.ui;

import javax.swing.*;
import java.awt.*;
import java.io.DataOutputStream;
import java.net.Socket;

public class ChatApp extends JFrame {
    private JFrame frame;
    private JTextArea messageDisplayArea;
    private JList<String> onlineUsersList;
    private DefaultListModel<String> onlineUsersListModel;
    private JTextField messageInputField;
    private JButton sendButton;
    private Socket socket;
    private String name;

    public ChatApp(String name, Socket socket) {
        // 创建主框架
        this.socket = socket;
        this.name = name; // 保存昵称

        frame = new JFrame(name+"的群聊界面");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(600, 400);
        frame.setLayout(new BorderLayout());

        // 消息展示框
        messageDisplayArea = new JTextArea();
        messageDisplayArea.setEditable(false);
        JScrollPane messageScrollPane = new JScrollPane(messageDisplayArea);
        frame.add(messageScrollPane, BorderLayout.CENTER);

        // 在线人数展示框
        onlineUsersListModel = new DefaultListModel<>();
        onlineUsersList = new JList<>(onlineUsersListModel);
        JScrollPane onlineUsersScrollPane = new JScrollPane(onlineUsersList);
        onlineUsersScrollPane.setPreferredSize(new Dimension(150, 0));
        frame.add(onlineUsersScrollPane, BorderLayout.EAST);

        // 消息发送框和发送按钮
        JPanel inputPanel = new JPanel();
        inputPanel.setLayout(new BorderLayout());

        messageInputField = new JTextField();
        sendButton = new JButton("发送");

        inputPanel.add(messageInputField, BorderLayout.CENTER);
        inputPanel.add(sendButton, BorderLayout.EAST);

        frame.add(inputPanel, BorderLayout.SOUTH);

        // 发送按钮的事件处理
        sendButton.addActionListener(e -> {
            String message = messageInputField.getText(); // 获取输入框中的文本
            if (!message.isEmpty()) {
                sendMessage(message);
                messageInputField.setText(""); // 清空输入框
            }
        });

        // 显示框架
        frame.setVisible(true);
        new ClientReaderThread(socket, this).start();
    }

    public void sendMessage(String message) {
        try {
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            dos.writeInt(2); // 消息类型
            dos.writeUTF(message);
            dos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void updateOnlineUsers(String[] onLineNames) {
        // 更新在线用户列表
        onlineUsersList.setListData(onLineNames);
    }

    public void setMsgTowin(String msg) {
        SwingUtilities.invokeLater(() -> {
            messageDisplayArea.append(msg + "\n");
        });
    }
}
package Client;

public class ConstantClient {
    public static final int CLIENT_PORT = 6666;
    public static final String CLIENT_IP = "127.0.0.1";
}
package server;

public class Constant {
    public static final int SERVER_PORT = 6666;
}
import com.acheng.ui.ChatEntryFrame;

public class App {
    public static void main(String[] args) {
        new ChatEntryFrame();
    }
}

界面展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2118811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DBC中一种特殊的特殊的Signal—多路复用Signal

前言&#xff1a; DBC设计中一般设计Signal时其实存在三种类型&#xff0c;如下图所示&#xff1a; **1&#xff09;步骤1&#xff0c;鼠标单击展开Message&#xff0c;选中底下的Signal **2&#xff09;步骤2&#xff0c;弹出dialog中选择 map signal **3&#xff09;得到…

深入解读Docker核心原理:Cgroups资源限制机制详解

在容器化技术中&#xff0c;除了资源的隔离&#xff0c;如何有效地控制和分配系统资源同样至关重要。Cgroups&#xff08;Control Groups&#xff09; 是Linux内核提供的一个强大机制&#xff0c;允许限制、监控和隔离进程组的系统资源使用情况。Cgroups是Docker实现容器资源限…

用RNN(循环神经网络)预测股票价格

RNN&#xff08;循环神经网络&#xff09;是一种特殊类型的神经网络&#xff0c;它能够处理序列数据&#xff0c;并且具有记忆先前信息的能力。这种网络结构特别适合于处理时间序列数据、文本、语音等具有时间依赖性的问题。RNN的核心特点是它可以捕捉时间序列中的长期依赖关系…

【项目】云备份

云备份 云备份概述框架 功能演示服务端客户端 公共模块文件操作模块目录操作模块 服务端模块功能划分功能细分模块数据管理热点管理 客户端模块功能划分功能细分模块数据管理目录检查文件备份 云备份 概述 自动将本地计算机上指定文件夹中需要备份的文件上传备份到服务器中。…

【网络原理】❤️Tcp 核心机制❤️ 通晓可靠传输的秘密, 保姆式教学, 建议收藏 !!!

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

QT QxOrm CRUD增删改查mysql数据库操作

QT QxOrm CRUD增删改查mysql数据库操作 QxOrm 是一个 C 库&#xff0c;旨在为 C 用户提供对象关系映射 (ORM) 功能。 基于每个类的简单 C 设置函数&#xff08;如 Java 中的 Hibernate XML 映射文件&#xff09;&#xff0c;QxOrm 库提供以下功能&#xff1a; 持久性&#xff1…

安宝特案例 | AR如何大幅提升IC封装厂检测效率?

前言&#xff1a;如何提升IC封装厂检测效率&#xff1f; 在现代电子产品的制造过程中&#xff0c;IC封装作为核心环节&#xff0c;涉及到复杂处理流程和严格质量检测。这是一家专注于IC封装的厂商&#xff0c;负责将来自IC制造商的晶圆进行保护、散热和导通处理。整个制程繁琐…

C语言俄罗斯方块(VS2022版)

C语言俄罗斯方块 演示视频一、前置知识1.Win32 API 的使用2.宽字符的使用 二、封装核心数据与框架介绍三、核心操作介绍旋转操作检测操作水平检测竖直检测代码化简 四、源码展示在 tetris.h 中&#xff1a;在 tetris.c 中&#xff1a;在 test.c 中&#xff1a; 以下代码环境为 …

小阿轩yx-Zabbix企业级分布式监控环境部署

小阿轩yx-Zabbix企业级分布式监控环境部署 前言 “运筹帷幄之中&#xff0c;决胜千里之外”监控在 IT 运维中占据着重要地位&#xff0c;按比例说占 30% 也不为过在监控系统开源软件中有很多可选择的工具&#xff0c;但是真正符合要求的、能够真正解决业务问题的监控系统软件…

W外链微信推广短连接怎么做?

制作微信推广链接的难点分析 一、内容创作难度 制作微信推广链接时&#xff0c;首先需要创作有吸引力的内容。这不仅要求内容本身有趣、有价值&#xff0c;还要能够激起人们的分享欲望。对于许多企业和个人来说&#xff0c;尤其是那些缺乏创意和写作能力的人来说&#xff0c;…

OpenHarmony鸿蒙开发( Beta5.0)智能甲醛检测系统实践

样例简介 本项目是基于BearPi套件开发的智能甲醛检测系统Demo&#xff0c;该设备硬件部分主要由小熊派单板套件和和甲醛检测传感器组成。智能甲醛检测系统可以通过云和手机建立连接&#xff0c;可以在手机上设置甲醛浓度阈值&#xff0c;传感器感知到的甲醛浓度超过阈值之后&a…

QQ邮箱“已发送”邮件竟然无法一键清空?看我操作,怎么删除12万+已发送邮件

最近遇到了一个问题&#xff0c;QQ邮箱提示我空间已满&#xff0c;所以我就专门去看看有哪些邮件可以删除&#xff0c;释放点空间。 我直接暴力删除了很多文件夹的邮件&#xff0c;在文件夹管理界面 有“清空”按钮&#xff0c;点一个即可清空。 但是。。。不出意外的话要出意…

南卡、韶音、墨觉:精选三款旗舰骨传导耳机全面对比评测!

在科技日新月异的今天&#xff0c;耳机作为我们日常生活中不可或缺的音频伴侣&#xff0c;正经历着前所未有的变革。特别是骨传导耳机&#xff0c;凭借其独特的声音传导方式和出色的佩戴体验&#xff0c;逐渐成为了运动爱好者和户外探索者的首选。在众多品牌中&#xff0c;南卡…

Pycharm的安装与Conda环境的配置

目录 第一步&#xff1a;下载并安装 PyCharm 社区版 第二步&#xff1a;创建新项目并配置 Python 解释器 第三步&#xff1a;配置 Conda 环境 第四步&#xff1a;验证环境 第五步&#xff1a;测试 PyTorch 第六步&#xff1a;测试基本 PyTorch 代码 第一步&#xff1a;下…

替代区块链

随着比特币的成功&#xff0c;人们逐渐意识到区块链技术的潜力&#xff0c;并随之出现了迅速的发展&#xff0c;各种区块链协议、应用程序和平台相应产生。 需要指出的是&#xff0c;在这种多元的局面下&#xff0c;很多项目迅速失去了它们的吸引力。事实上&#xff0c;有不少项…

深圳MES系统在制造业的应用与发展

深圳MES在制造业的应用与发展呈现以下几个特点&#xff1a; 应用范围广泛&#xff1a;深圳制造业涵盖了电子、通信、汽车、机械等多个领域&#xff0c;MES系统在这些领域的应用非常广泛。不同行业的企业可以根据自身的需求和特点&#xff0c;定制化地应用MES系统来实现生产管理…

测试即服务(TaaS):概念、优势及应用场景!

引言 随着数字化转型的深入发展&#xff0c;软件质量和用户体验变得愈发重要。传统的软件测试方法已经难以满足现代企业对于快速迭代和高质量交付的需求。在此背景下&#xff0c;“测试即服务”(Testing as a Service, TaaS) 模式应运而生&#xff0c;为软件测试带来了新的解决…

基于SpringBoot+Vue+MySQL的足球俱乐部管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统足球俱乐部管理…

Gtest(Google Test)使用

下面Gtest是在arm-linux下运行的 https://download.csdn.net/download/qq_31868891/89729426 一、下载编译 1.下载gtest代码 https://github.com/google/googletest 2.配置编译 vscode安装CMake Tools 将上面下载的gtest代码文件夹拖到vscode里&#xff0c;然后选择对应的…

SAP 凭证的替代传输GGB1

SAP 凭证的替代传输GGB1 之前没有留意过&#xff0c;前人一直是直接改的&#xff0c;搜索了一下是可以这样弄得 1.一般通过OBBH&#xff0c;配置的凭证替代&#xff0c;产生的请求号&#xff0c;从开发机传输不到生产机。只能通过GGB1来传输。在GGB1里面选择要传输的替代 选中…