socketChatRoom_Server(聊天室服务端)

用socket底层原理实现简单聊天室程序,本篇为服务器端2.0版本(完结篇)

https://gitee.com/muyiafa/socketchatroom

socketChatRoom_Client(聊天室客户端)_木易阿发的博客-CSDN博客

文章目录

* 1、启动服务器:构造方法 new ServerSocek对象并绑定程序端口
* 2、等待连接:
* 3、获取客户端发送的信息及客户端主机信息并打印在控制台
* 4、多线程支持被多个客户端连接互不干扰
* 5、广播给所有客户端其他客户端发送过来的消息
* 6、将PrintWriter数组换成集合列表优化减少代码和拓展的便捷
* 7、端口8888


package homeWork.ChatRoom220707;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * author:木易阿发
 * time:220707
 * moditime:220710
 * port:8888
 * 1、启动服务器:构造方法 new ServerSocek对象并绑定程序端口
 * 2、等待连接:
 * 3、获取客户端发送的信息及客户端主机信息并打印在控制台
 * 4、多线程支持被多个客户端连接互不干扰
 * 5、广播给所有客户端其他客户端发送过来的消息
 * 6、将PrintWriter数组换成集合列表优化减少代码和拓展的便捷性
 */
public class Servers {
    int serverNum ;
    private ServerSocket  serverSocket;
//    private PrintWriter[] pws={};//在根类创建一个输出流数组用来存放所有客户端发送过来的消息
    private Collection<PrintWriter> pws = new ArrayList<>();//在根类创建一个输出流数组用来存放所有客户端发送过来的消息
    public static void main(String[] args) {
        Servers server = new Servers();//new对象同时初始化
        //调用等待连接方法
        server.startServer();
    }

    // 1、构造器初始化启动服务器指定端口
    public Servers(){
        try {
            System.out.println("duang 瓮....");
            serverSocket = new ServerSocket(8888);
            System.out.println("服务器已成功运行.哔..哔...哔....");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 2、等待客户端连接
    public void startServer(){

        //4、多线程支持被多个客户端连接互不干扰
        try {
            while (true) {//循环不断接受用户连接从而启动新线程
                // 该线程任务是负责与指定的客户端进行交互
                Socket socket = serverSocket.accept();
//                System.out.println("ID-" + (++serverNum) + "-" + socket.getInetAddress().getHostName() + "(成功接入)");
                //没得到一个连接就启动一个线程单独交互
                Runnable clientHandler = new ClientHandler(socket, ++serverNum);//线程任务类
                Thread thread = new Thread(clientHandler);//线程对象
                thread.start();//启动线程
            }
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    //3、获取客户端发送的信息及客户端主机信息并打印在控制台
    //客户端处理程序:处理服务器发回来的消息等
   private class ClientHandler implements Runnable{
        private int serverNum;//用户编号
        private Socket socket;
        private String host;//记录访问对象的主机地址信息
        public ClientHandler(Socket socket,int serverNum) {
            this.serverNum=serverNum;
            this.socket=socket;
            host=socket.getInetAddress().getHostName();
        }
        @Override
        public void run() {//交互任务
            PrintWriter pw = null;//输出流
            try {
                InputStream is = socket.getInputStream();;//通过socket获取输入流,得到客户端发送过来的消息
                InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);//字符转换流,将is的字节流转换为字符
                BufferedReader br = new BufferedReader(isr);//缓冲字节输入流,块读取

                //通过start方法循环传输过来的socket获取输出流给每个客户端广播消息
                OutputStream os = socket.getOutputStream();//通过socket获取字节输出流
                OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8);//字节转换流,将字符转换成字节写出指定编码
                BufferedWriter bw = new BufferedWriter(osw);//缓冲输出流,保证语句的完整性
                pw = new PrintWriter(bw,true);//字符输出流(特别注意开启行刷新),按行写出控制台输入的消息
                //将输出流存入输出流数组
                synchronized (Servers.this) {//线程同步锁,避免数组出现线程安全
//                    pws = Arrays.copyOf(pws, pws.length + 1);//先对数组扩容
//                    pws[pws.length - 1] = pw;//把输出流对象存入数组的最后一个空间位置
                    pws.add(pw);
                }
                sendMessage("ID"+(serverNum)+"-IP"+host+" 接入上线,当前在线人数:"+pws.size());//上线广播
                //向所有客户端广播消息
                String message;
                while ((message = br.readLine()) != null) {
                    sendMessage("ID"+(serverNum)+"-IP"+host+"-"+message);//向每个客户端发送当前客户端发送的消息
                }
            } catch (IOException e) {
                //当客户端强行关闭,服务端这里readLine会抛出异常,这里就可以catch异常从而执行finally
            }finally {
                synchronized (Servers.this) {//线程锁
                    //如果当前pw被关闭就将当前pw从广播数组中删除并通知下线
//                    for (int i = 0; i < pws.length; i++) {
//                        if (pws[i] == pw) {//找到数组中的当前pw
//                            pws[i] = pws[pws.length - 1];//将数组中的最后一个元素赋值到被关闭的pw所在数组的位置
//                            pws = Arrays.copyOf(pws, pws.length - 1);//将数组缩容
//                            //通知下线
//                            sendMessage("ID"+serverNum+"-IP"+host+"-关闭下线了,当前在线人数:"+pws.length);
//                            break;
//                        }
//                    }
                    pws.remove(pw);
                    sendMessage("ID"+serverNum+"-IP"+host+"-关闭下线了,当前在线人数:"+pws.size());
                }
            }
        }

         //5、广播给所有客户端其他客户端发送过来的消息
        //将当前客户端发的消息发送给所有客户端
        private void sendMessage(String line){
            System.out.println(line);
            synchronized (Servers.this) {//线程锁
//                for (int i = 0; i < pws.length; i++) {
//                    pws[i].println(line);//调用每个客户端socket对象进行消息发送
//                }
                for (PrintWriter pw:pws
                     ) {
                    pw.println(line);
                }
            }
        }
    }
}

/**------------------反爬声明o(▽)咻咻咻-------------------

** 作者:木易阿发 版权声明:**

本文为博主倾情原创文章,转载请附上源文链接!

如觉得本文对你有所收获,你的请评论点赞与

转发也将是鼓励支持我继续创作的动力。

更多精彩在视频号和公众号或 百度 *木易阿发* !

Gitee:木易阿发 (muyiafa) - Gitee.com

个人网站:http://www.muyiafa.com

乐学至善 开源共享 知行合一。

-------------------咻咻咻o(▽)反爬声明--------------------*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程小马达

你的鼓励将是我创作的最大动力o

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值