在上一篇,利用线程使服务端实现了能够接收多客户端请求的功能,这里便需要客户端接收多客户端消息的同时还能把消息转发到每个连接的客户端,并且客户端要能在内容显示区域显示出来,从而实现简单的在线群聊。

在实现客户端转发,无非就是增加输出流;而之前客户端都只发不收,这里也需要更改客户端达到循环接收服务端消息的目的,因此也需要实现多线程。

在实现这个功能的时候,偶然想起随机生成验证码的功能,于是也灵机一动随机给每个客户端生成一个名字,从而在输出的时候看起来更加像是群聊,不仅有消息输出,还能看到是谁。

实现这些功能之后,基本上就可以几个人同时在线群聊了,因为代码中有main方法,因此可以把服务端和客户端都打成可执行jar包,可参考我的另一篇博文:使用eclipse创建java程序可执行jar包

之后在桌面双击相应的jar文件启动服务端和客户端即可,不需要再依赖eclipse运行。


修改后的客户端代码如下:
package chat.chat;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Random;

/**
 * 在线聊天客户端 步骤: 1、生成图形窗口界面轮廓 2、为轮廓添加关闭事件 3、在轮廓中加入输入区域和内容展示区域 4、为输入区域添加回车事件
 * 5、建立服务端连接并发送数据
 *
 * @author tuzongxun123
 *
 */
public class ChatClient extends Frame {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    // 用户输入区域
    private TextField tfTxt = new TextField();
    // 内容展示区域
    private TextArea tarea = new TextArea();
    private Socket socket = null;
    // 数据输出流
    private DataOutputStream dataOutputStream = null;
    // 数据输入流
    private DataInputStream dataInputStream = null;
    private boolean isConnect = false;
    Thread tReceive = new Thread(new ReceiveThread());
    String name = "";

    public static void main(String[] args) {
        ChatClient chatClient = new ChatClient();
        chatClient.createName();
        chatClient.launcFrame();

    }

    /**
     * 建立一个简单的图形化窗口
     *
     * @author:tuzongxun
     * @Title: launcFrame
     * @param
     * @return void
     * @date May 18, 2016 9:57:00 AM
     * @throws
     */
    public void launcFrame() {
        setLocation(300, 200);
        this.setSize(200, 400);
        add(tfTxt, BorderLayout.SOUTH);
        add(tarea, BorderLayout.NORTH);
        // 根据窗口里面的布局及组件的preferedSize来确定frame的最佳大小
        pack();
        // 监听图形界面窗口的关闭事件
        this.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
                disConnect();
            }
        });
        tfTxt.addActionListener(new TFLister());
        // 设置窗口可见
        setVisible(true);
        connect();
        // 启动接受消息的线程
        tReceive.start();
    }

    /**
     * 连接服务器
     *
     * @author:tuzongxun
     * @Title: connect
     * @param
     * @return void
     * @date May 18, 2016 9:56:49 AM
     * @throws
     */
    public void connect() {
        try {
            // 新建服务端连接
            socket = new Socket("127.0.0.1", 8888);
            // 获取客户端输出流
            dataOutputStream = new DataOutputStream(socket.getOutputStream());
            dataInputStream = new DataInputStream(socket.getInputStream());
            System.out.println("连上服务端");
            isConnect = true;
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 生成随机的客户端名字
    public void createName() {
        String[] str1 = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
                "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
                "w", "x", "y", "z", "1", "2", "3", "4", "5", "6", "7", "8",
                "9", "0", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
                "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
                "W", "X", "Y", "Z" };
        Random ran = new Random();

        for (int i = 0; i < 6; i++) {
            // long num = Math.round(Math.random() * (str1.length - 0) + 0);
            // int n = (int) num;
            int n = ran.nextInt(str1.length);
            if (n < str1.length) {
                String str = str1[n];
                name = name + str;
                System.out.println(name);
            } else {
                i--;
                continue;
            }

        }
        this.setTitle(name);
    }

    /**
     * 关闭客户端资源
     *
     * @author:tuzongxun
     * @Title: disConnect
     * @param
     * @return void
     * @date May 18, 2016 9:57:46 AM
     * @throws
     */
    public void disConnect() {
        try {
            isConnect = false;
            // 停止线程
            tReceive.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                if (dataOutputStream != null) {
                    dataOutputStream.close();
                }
                if (socket != null) {
                    socket.close();
                    socket = null;
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 向服务端发送消息
     *
     * @author:tuzongxun
     * @Title: sendMessage
     * @param @param text
     * @return void
     * @date May 18, 2016 9:57:56 AM
     * @throws
     */
    private void sendMessage(String text) {
        try {
            dataOutputStream.writeUTF(name + ":" + text);
            dataOutputStream.flush();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 图形窗口输入区域监听回车事件
     *
     * @author tuzongxun123
     *
     */
    private class TFLister implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            String text = tfTxt.getText().trim();
            // 清空输入区域信息
            tfTxt.setText("");
            // 回车后发送数据到服务器
            sendMessage(text);
        }

    }

    private class ReceiveThread implements Runnable {

        @Override
        public void run() {
            try {
                while (isConnect) {
                    String message = dataInputStream.readUTF();
                    System.out.println(message);
                    String txt = tarea.getText();
                    if (txt != null && !"".equals(txt.trim())) {
                        message = tarea.getText() + "\n" + message;
                    }
                    tarea.setText(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}


修改后的服务端代码如下:
package chat.chat;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

/**
 * java使用socket和awt组件以及多线程简单实现在线聊天功能服务端 :
 * 实现服务端把接收到的客户端信息转发到所有连接的客户端,并且让客户端读取到这些信息并显示在内容显示区域中。
 *
 * @author tuzongxun123
 *
 */
public class ChatServer {

    public static void main(String[] args) {
        new ChatServer().start();
    }

    // 是否成功启动服务端
    private boolean isStart = false;
    // 服务端socket
    private ServerSocket ss = null;
    // 客户端socket
    private Socket socket = null;
    // 保存客户端集合
    List<Client> clients = new ArrayList<Client>();

    public void start() {
        try {
            // 启动服务器
            ss = new ServerSocket(8888);
        } catch (BindException e) {
            System.out.println("端口已在使用中");
            // 关闭程序
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            isStart = true;
            while (isStart) {
                // 启动监听
                socket = ss.accept();
                System.out.println("one client connect");
                // 启动客户端线程
                Client client = new Client(socket);

                new Thread(client).start();
                clients.add(client);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭服务
            try {
                ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 客户端线程
     *
     * @author tuzongxun123
     *
     */
    private class Client implements Runnable {
        // 客户端socket
        private Socket socket = null;
        // 客户端输入流
        private DataInputStream dataInputStream = null;
        // 客户端输出流
        private DataOutputStream dataOutputStream = null;
        private boolean isConnect = false;

        public Client(Socket socket) {
            this.socket = socket;
            try {
                isConnect = true;
                // 获取客户端输入流
                dataInputStream = new DataInputStream(socket.getInputStream());
                // 获取客户端输出流
                dataOutputStream = new DataOutputStream(
                        socket.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 向客户端群发(转发)数据
         *
         * @author:tuzongxun
         * @Title: sendMessageToClients
         * @param @param message
         * @return void
         * @date May 18, 2016 11:28:10 AM
         * @throws
         */
        public void sendMessageToClients(String message) {
            try {
                dataOutputStream.writeUTF(message);
            } catch (SocketException e) {

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            isConnect = true;
            Client c = null;
            try {
                while (isConnect) {
                    // 读取客户端传递的数据
                    String message = dataInputStream.readUTF();
                    System.out.println("客户端说:" + message);
                    for (int i = 0; i < clients.size(); i++) {
                        c = clients.get(i);

                        c.sendMessageToClients(message);
                    }

                }
            } catch (EOFException e) {
                System.out.println("client closed!");
            } catch (SocketException e) {
                if (c != null) {
                    clients.remove(c);
                }
                System.out.println("Client is Closed!!!!");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 关闭相关资源
                try {
                    if (dataInputStream != null) {
                        dataInputStream.close();
                    }
                    if (socket != null) {
                        socket.close();
                        socket = null;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

java Socket实现简单在线聊天(三)的更多相关文章

  1. java Socket实现简单在线聊天(二)

    接<java Socket实现简单在线聊天(一)>,在单客户端连接的基础上,这里第二步需要实现多客户端的连接,也就需要使用到线程.每当有一个新的客户端连接上来,服务端便需要新启动一个线程进 ...

  2. java Socket实现简单在线聊天(一)

    最近的项目有一个在线网页交流的需求,由于很久以前做过的demo已经忘记的差不多了,因此便重新学习一下. 我计划的大致实现步骤分这样几大步: 1.使用awt组件和socket实现简单的单客户端向服务端持 ...

  3. vue实现简单在线聊天

    vue实现简单在线聊天 引用mui的ui库,ES6的 fetch做网络请求 //html <!DOCTYPE html> <html> <head> <met ...

  4. AngularJS+Node.js+socket.io 开发在线聊天室

    所有文章搬运自我的个人主页:sheilasun.me 不得不说,上手AngularJS比我想象得难多了,把官网提供的PhoneCat例子看完,又跑到慕课网把大漠穷秋的AngularJS实战系列看了一遍 ...

  5. 基于Server-Sent Event的简单在线聊天室

    Web即时通信 所谓Web即时通信,就是说我们可以通过一种机制在网页上立即通知用户一件事情的发生,是不需要用户刷新网页的.Web即时通信的用途有很多,比如实时聊天,即时推送等.如当我们在登陆浏览知乎时 ...

  6. Socket实现简单的聊天通信

    最近学习了Socket后,感觉Socket挺好玩的,在博客中看到socket在实时聊天功能的很强大,于是乎就做了一个简单的聊天功能,今天贴出来,能够与大家一起共享,有不对之处,能够给予指出,谢谢! 服 ...

  7. WinForm的Socket实现简单的聊天室 IM

    1:什么是Socket 所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象. 一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制. 从 ...

  8. java socket之多人聊天室Demo

    一.功能介绍 该功能实现了一个类似QQ的最简单多人聊天室,如下图所示. 二.目录结构 三.服务端 1)SocketServer类,该类是服务端的主类,主要负责创建聊天窗口,创建监听客户端的线程: pa ...

  9. JAVA Socket基础(简单实现)

    学习Socket需要了解的几个概念: Socket 指的是互联网连接中的各个终结点.互联网连接是怎么创建的,通过IP地址加端口号,进行互通. A电脑(192.168.3.125:80)>> ...

随机推荐

  1. 什么是NAS.什么是黑白群晖?(转)

    前言               为了让更多的新人可以简单了解什么是nas,什么是黑群晖.什么是白群晖!有什么作用?特此制作以下教学!(原文为MOMO所写).图片也大体从互联网上下载.有对原作者不便之 ...

  2. linux的定时任务服务crond(crontab)服务

    1,Crond: Crond是linux系统中用来定期执行命令或指定程序任务的一种服务或者软件.(Centos5以后默认存在) 当优化开机自启动的时候,第一个就是crond. Crond服务默认情况( ...

  3. 【socket编程】select manual page翻译

    原文: select manual page 依赖的头文件 /* According to POSIX.1-2001, POSIX.1-2008 */ #include <sys/select. ...

  4. 关于Kafka 的 consumer 消费者处理的一些见解

    前言 在上一篇 Kafka使用Java实现数据的生产和消费demo 中介绍如何简单的使用kafka进行数据传输.本篇则重点介绍kafka中的 consumer 消费者的讲解. 应用场景 在上一篇kaf ...

  5. How to create and start VirtualBox VM without GUI

    Suppose you want to create and run virtual machines (VMs) on VirtualBox. However, a host machine doe ...

  6. ABP官方文档翻译 3.7 领域事件(事件总线)

    领域事件(事件总线) 事件总线 注入IEventBus 获取默认实例 定义事件 预定义事件 处理异常 实体更改 触发事件 处理事件 处理基础事件 处理者异常 处理多个事件 注册处理者 自动 手动 取消 ...

  7. C# 语法学习整理

    1.协变与逆变的概念 文章地址:https://segmentfault.com/a/1190000007005115 **************************************** ...

  8. vsftpd安装和使用 Linux系统和window系统

    vsftpd 安装(Linux)一.安装系统环境 centos 6.9 64位二.vsftpd版本 vsftpd-2.2.2-24.el6.x86_64三.安装步骤1.安装 执行 yum -y ins ...

  9. Spring源码情操陶冶-PropertyPlaceholderBeanDefinitionParser注解配置解析器

    本文针对spring配置的context:property-placeholder作下简单的分析,承接前文Spring源码情操陶冶-自定义节点的解析 spring配置文件应用 <context: ...

  10. Spring-shiro源码陶冶-AuthorizingRealm用户认证以及授权

    阅读源码有助于陶冶情操,本文旨在简单的分析shiro在Spring中的使用 简单介绍 Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能 AuthorizingReal ...