客户端与服务端--(基于TCP/IP的群聊系统的设计与实现)

简介: 接上期 *Swing组件组合使用--登录界面源码(仿QQ)*,这期分享客户端与服务端源码

*接上期 Swing组件组合使用--登录界面源码(仿QQ)*,这期分享客户端与服务端源码

首先是服务端源码:**


import jdk.internal.icu.text.UnicodeSet;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;


public class ServerChat {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) throws Exception {
        ServerJframe serverJframe = new ServerJframe();
        serverJframe.init();
    }
}

class ServerJframe extends JFrame {
    //GUI相关属性
    JTextArea serverTa = new JTextArea();
    int sum = 0;
    JScrollPane scrollPane = new JScrollPane(serverTa);
    JPanel btnTool = new JPanel();
    JButton startBtn = new JButton("上班");
    JButton stopBtn = new JButton("下班");
    private Set<String> set = new HashSet<>();
    //端口
    private static final int PORT = 8888;
    //ServerSocket
    private ServerSocket serverSocket = null;
    private Socket socket = null;

    //Server接受数据
    private DataInputStream dataInputStream = null;

    // 多个客户端访问时,客户端对象存放入List中
    private ArrayList<ClientCoon> ccList = new ArrayList<ClientCoon>();

    // 服务器启动的标志 (其实ServerSocket ss 初始化出来时以为者服务器的启动)
    private boolean isStart = false;

    public void init() throws Exception {
        this.setTitle("畅聊服务器端");
        this.add(scrollPane, BorderLayout.CENTER);
        btnTool.add(startBtn);
        btnTool.add(stopBtn);
        this.add(btnTool, BorderLayout.SOUTH);
        this.setBounds(0, 0, 500, 500);

        //判断服务器是否已经开启
        if (isStart) {
            System.out.println("服务器已经上班 了\n");
        } else {
            System.out.println("服务器还没有上班,请点击上班催催服务器上班!\n");
        }
        //按钮监听监听服务器开启,置开始位false
        startBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    if (serverSocket == null) {
                        serverSocket = new ServerSocket(PORT);
                    }
                    isStart = true;
                    //startServer();
                    serverTa.append("服务器已经上班啦! \n");
                    System.out.println("服务器上班啦!!\n");
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        });
        // 终止按钮监听停止服务器,置开始位true
        stopBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    if (serverSocket != null) {
                        serverSocket.close();
                        isStart = false;
                    }
                    System.exit(0);
                    serverTa.append("服务器下班啦!!\n");
                    System.out.println("服务器下班啦!!\n");

                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        });

        /**
         * 服务器窗口关闭应该停止服务器,需改进的代码
         */
        // this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // serverTa.setEditable(false);
        this.setVisible(true);
        startServer();
    }

    /**
     * 服务器启动代码
     */
    public void startServer() throws Exception {

        try {
            try {
                serverSocket = new ServerSocket(PORT);
                isStart = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 可以接受多个客户端的连接
            // 接每一個信息时,服务器不可以终断,所以将其写入while()中,判断符为服务器开关的判断符

            while (isStart) {

                socket = serverSocket.accept();
                ccList.add(new ClientCoon(socket));
                System.out.println("\n" + "有新的老板加入聊天室啦" + socket.getInetAddress() + "/" + socket.getPort() + "\n" + "当前在线人数:" + set.size());
               // serverTa.append("\n" + "有新的老板加入聊天室啦" + socket.getInetAddress() + "/" + socket.getPort() + "\n" + "当前在线人数:" + set.size());

            }
            //服务器接受客户端一句话
            /*receiveStream();*/
        } catch (SocketException e) {
            System.out.println("服务器下班干饭去啦!!!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 服务器停止代码
     * */

    /**
     * 服务器接受数据的方法(客户端传送一句话),不适用多个客户端进行通话
     * */
    /*public void receiveStream(){
        try {
            dataInputStream = new DataInputStream(socket.getInputStream());
            String str = dataInputStream.readUTF();
            System.out.println(str);
            serverTa.append(str);
        }catch (Exception e){
            e.printStackTrace();
        }
    }*/

    /**
     * @deprecated 内部类声明 对象 这个对象是属于服务器端的一个连接对象
     */
    class ClientCoon implements Runnable {
        Socket socket = null;

        public ClientCoon(Socket socket) {
            this.socket = socket;
            /**
             * 线程启动在这里:
             * 初始化方法里 初始化一个线程 ,线程中封装的是自己,做整个线程的调用
             */
            (new Thread(this)).start();
        }

        //接受客户端信息(多线程run()方法)
        @Override
        public void run() {
            try {
                DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
                // 为了让服务器能够接受到每个客户端的多句话
                while (isStart) {
                    //readUTF()是一种阻塞方法,接一句就执行完了,所以循环中
                    String str = dataInputStream.readUTF();
                    if (str.indexOf("3010151410") == 0) {
                        int point = str.indexOf("上线啦");
                        String s = str.substring(10, point);
                        set.add(s);


                        str = "服务器:" + str.substring(10)+"当前在线人数:"+set.size();
                    }
                    if (str.indexOf("closed") == 0) {

                        int point = str.indexOf("下线了");
                        String s = str.substring(6, point);
                        for (String ss : set) {
                            if (ss.equals(s)) {
                                set.remove(s);
                                break;
                            }
                        }
                        String members = "";
                        str = "服务器:" + str.substring(6)+"当前在线人数:"+set.size();
                    }
                    System.out.println("\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n");
                    serverTa.append("\n" + str + "\n");
                    //服务器向每个客户端发送别的客户端发来的信息
                    // 遍历ccList,调用send方法,在客户端里接受应该是多线程的接受
                    String strSend = "\n" + str + "\n";
                    Iterator<ClientCoon> iterator = ccList.iterator();
                    while (iterator.hasNext()) {
                        ClientCoon clientCoon = iterator.next();
                        clientCoon.send(strSend);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 服务器向每個连接对象发送数据的方法
        public void send(String str) {
            try {
                DataOutputStream dataOutputStream = new DataOutputStream(this.socket.getOutputStream());
                dataOutputStream.writeUTF(str);
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}



客户端源码:


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

public class ClientChat {
    //这是一个main方法,是程序的入口:
    public ClientChat(String name) {
        ClientJframe clientJframe = new ClientJframe();
        clientJframe.init(name);
    }
}

class ClientJframe extends JFrame {
    //GUI布局

    //聊天记录显示区
    private String name = "";
    private JTextArea ta = new JTextArea(10, 20);

    //聊天记录输入区
    JScrollPane scrollPane = new JScrollPane(ta);
    private JTextField tf = new JTextField(20);
    //发送按钮
    private JButton jb = new JButton("发送");
    private JPanel jp = new JPanel();

    //端口
    // 静态常量主机端口号
    private static final String CONNSTR = "127.0.0.1";
    // 静态常量服务器端口号
    private static final int CONNPORT = 8888;
    private Socket socket = null;

    //Client发送数据
    private DataOutputStream dataOutputStream = null;

    //客户端连接上服务器判断符号
    private boolean isConn = false;

    /**
     * 无参的构造方法 throws HeadlessException
     */
    public ClientJframe() throws HeadlessException {
        super();
    }

    public void init(String name) {
        this.setTitle("Boos洽谈室");
        this.add(scrollPane,BorderLayout.CENTER);
        jp.setLayout(new FlowLayout(FlowLayout.LEFT));
        //设置底部面板
        jp.setBounds(800, 800, 100, 100);
        jp.setForeground(new Color(199, 58, 231));
        //添加输入框和发送按钮
        jp.add(tf);
        jp.add(jb);

        ta.setFont(new Font("方正粗黑宋简体",0,15));
        ta.setForeground(new Color(222, 126, 39));

        tf.setFont(new Font("黑体",0,13));
        tf.setForeground(new Color(39, 42, 222));

        jb.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String strSend = tf.getText();
                // 去掉空格判断长度是否为空
                if (strSend.trim().length() == 0) {
                    return;
                }
                //客户端信息strSend发送到服务器上
                send(name+"说:"+strSend);
                tf.setText("");
//                ta.append(strSend + "\n");
            }
        });
        this.add(jp,BorderLayout.SOUTH);
        //this.add(tf, BorderLayout.SOUTH);
        this.name = name;
        this.setBounds(300, 300, 315, 500);

        // 添加监听,使回车键可以输入数据(判断数据合法性),
        // 并輸入到聊天框,换行
        tf.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                String strSend = tf.getText();
                // 去掉空格判断长度是否为空
                if (strSend.trim().length() == 0) {
                    return;
                }
                //客户端信息strSend发送到服务器上
                send(name+"说:"+strSend);
                tf.setText("");
                //ta.append(strSend + "\n");
            }
        });

        //关闭事件
        this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowListener() {
            @Override
            public void windowOpened(WindowEvent e) {

            }

            @Override
            public void windowClosing(WindowEvent e) {
                send("closed"+name+"下线了");
                dispose();
            }

            @Override
            public void windowClosed(WindowEvent e) {

            }

            @Override
            public void windowIconified(WindowEvent e) {

            }

            @Override
            public void windowDeiconified(WindowEvent e) {

            }

            @Override
            public void windowActivated(WindowEvent e) {

            }

            @Override
            public void windowDeactivated(WindowEvent e) {

            }
        });
        ta.setEditable(false);//聊天区域不可以输入
        tf.requestFocus();//光标聚焦

        try {
            socket = new Socket(CONNSTR, CONNPORT);
            isConn = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        send("3010151410"+name+"上线啦!");
        // 启动多线程
        new Thread(new Receive()).start();

        this.setVisible(true);
    }

    /**
     * 客户端发送信息到服务器上的方法
     */
    public void send(String str) {
        try {
            dataOutputStream = new DataOutputStream(socket.getOutputStream());
            dataOutputStream.writeUTF(str);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**

     * @deprecated 多线程的类,实现了Runnable接口的类
     */
    class Receive implements Runnable {
        @Override
        public void run() {
            try {
                while (isConn) {
                    DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
                    String str = dataInputStream.readUTF();
                    //通讯消息
                   ta.append(str);
                }
            } catch (SocketException e) {
                System.out.println("服务器意外提前下班了!");
                ta.append("服务器意外提前下班了!");
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
相关文章
|
7月前
|
机器学习/深度学习 人工智能 网络协议
TCP/IP五层(或四层)模型,IP和TCP到底在哪层?
TCP/IP五层(或四层)模型,IP和TCP到底在哪层?
135 4
|
1月前
|
网络协议 网络安全 网络虚拟化
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算。通过这些术语的详细解释,帮助读者更好地理解和应用网络技术,应对数字化时代的挑战和机遇。
101 3
|
7月前
|
网络协议 Java 数据安全/隐私保护
如何使用Java实现基于TCP/IP协议的即时通讯系统
TCP/IP协议是现代计算机网络通信中最常用的协议之一,而即时通讯系统则是当前互联网应用中非常普遍的一种场景。本文将介绍如何使用Java编程语言来实现基于TCP/IP协议的即时通讯系统,包括建立服务端和客户端、发送和接收消息等方面的详细步骤和示例。
|
网络协议 算法 人机交互
TCP/IP、OSI七层模型---开放式系统互联模型
一、OSI七层模型—开放式系统互联模型
117 0
|
监控 网络协议 网络架构
IP协议【图解TCP/IP(笔记九)】
IP协议【图解TCP/IP(笔记九)】
158 0
|
传感器 数据采集 人工智能
LabVIEW Arduino TCP/IP远程智能家居系统(项目篇—5)
智能家居是以家为平台,兼备建筑化于一体的高效、舒适、安全、便利的家居环境。它是采用家庭控制总线、互联网、通信、人工智能、单片机、传感器等一系列高新技术将家居设备控制,安防管理生活信息及资讯管理,家居互联网通信等与我们日常生活息息相关的常用生活元素全面并缜密地结合在一起,能够高度地提升我们的日常生活质量、便利性、安全性、舒适性和丰富性,是实现真正意义上的数字化、网络化、信息化和智能化家庭的一种全新的系统。它是依托于住宅这个平台,能够科学统筹管理家居生活的各个方面,让家居生活“"智慧"起来。这个管理过程的实现要依赖于计算机技术、网络技术、通信技术和综合布线技术等关键技术。
|
域名解析 网络协议
IP协议, TCP协议 和DNS 服务分别是干什么的?
大家好,我是阿萨。昨天讲解了网络四层协议[TCP/IP协议族分为哪4层?]今天我们学习下IP 协议, TCP 协议和DNS 协议分别是干什么的。
308 0
IP协议, TCP协议 和DNS 服务分别是干什么的?
|
网络协议
ACK的累加规则-wireshark抓包分析-不包含tcp头部、ip头部、数据链路层头部等。
ACK的累加规则-wireshark抓包分析-不包含tcp头部、ip头部、数据链路层头部等。
ACK的累加规则-wireshark抓包分析-不包含tcp头部、ip头部、数据链路层头部等。
|
网络协议 网络架构
六、TCP/IP模型 和 5层参考模型
六、TCP/IP模型 和 5层参考模型
六、TCP/IP模型 和 5层参考模型
|
网络协议 Java
Java BIO tcp服务端向客户端消息群发代码教程实战
java BIO tcp服务端向客户端消息群发代码教程实战
170 0