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

目录

创建服务端 建立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();
    }
}

界面展示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值