手写一个聊天室

4 篇文章 0 订阅
2 篇文章 0 订阅

概要

  • 由于我的破电脑同时开浏览器和IDEA会卡死,只能把正文写到代码注释里了,感兴趣可以把代码复制下来运行一下。
  • 一共有三个文件:服务器,客户端,工具类。
  • 主要知识点:
  • 1.多线程:runnable,thread
  • 2.匿名表达式lambda
  • 3.遍历容器:迭代器,foreach
  • 4.基于TCP的简单编程
  • 5.IO流的对接操作
  • 6.最重要的面向对象封装思想

**

服务器

**
package com.sxt.chatForMy;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import static com.sxt.chatForMy.Server.group;

/**
 * 服务器端:使用本地端口(8888)
 *
 * 主要功能:
 *          1.接收消息
 *          2.发送消息
 *          3.自动释放资源
 *          4.多人聊天与私聊
 *
 * 其中我们可以利用封装的思想:
 *                          1.封装服务对象(每个客户端即一个对象)
 *                              >接收发送消息
 *                              >私聊与多人聊天
 *                              >借助工具类释放资源
 *                          2.封装一个工具类(用来释放资源)
 *                              >有一个释放资源的方法
 */
public class Server {
    public static CopyOnWriteArrayList<Channel> group = new CopyOnWriteArrayList<>();
    public static void main(String[] args) throws IOException{
        //创建本地服务器,端口8888
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true){
            Socket client = serverSocket.accept();//阻塞式等待客户端连接
            System.out.println("当前有"+(group.size()+1)+"名用户连接");

            Channel current = new Channel(client);
            group.add(current);//加入容器
            new Thread(current).start();//启动线程
        }
    }
}

//封装用户对象
class Channel implements Runnable{

    private String username = "";//用户名
    private DataInputStream  dis;//从客户端输入到服务器的流
    private DataOutputStream dos;//从服务器输出到客户端的流
    private Socket        client;//客户端
    private boolean flag = true;//每个用户进来就会开辟一个线程,这个标志用来结束用户的线程

    //构造器
    public Channel(Socket client){
        this.client = client;
        //建立流
        try{
            this.dis = new DataInputStream(client.getInputStream());
            this.dos = new DataOutputStream(client.getOutputStream());
        }catch (IOException e){System.out.println("与客户端对接时错误");}
    }

    //从服务端向客户端发送消息
    public void send(String msg){
        try {
            dos.writeUTF(msg);//发送信息
            dos.flush();      //刷新缓存
        }catch (IOException e){System.out.println("从服务端向客户端发送信息时错误");}
    }

    /**
     *
     *多人聊天实现思路:
     *              >将所有客户端装入容器,遍历容器后将
     *               消息发送给除自己以外的其他所有人
     *              >对send()方法进行封装
     * 私聊实现思路:
     *              >约定以 @用户名:消息内容 对该用户发送私密消息
     *              >封装send()方法
     *  容器的选择:
     *         >见行26,有下面这条语句
     *          public static CopyOnWriteArrayList<Channel> group = new CopyOnWriteArrayList<>();
     *          >记得一定要静态导入
     */
    public void sendOthers(String msg){
        /**
         * 这里分别演示了迭代器与foreach俩种方法遍历容器
         */
        //私聊模式的实现
        if(msg.startsWith("@")){
            int flag = 0;

            //清洗数据
            msg = msg.substring(1);
            System.out.println(msg);
            String[] datas = msg.split(":");
            System.out.println(datas[0]);
            System.out.println(datas[1]);

            //利用迭代器遍历容器
            Iterator<Channel> groupList = group.iterator();
            while (groupList.hasNext()){
                Channel temp = groupList.next();
                if (temp.username.equals(datas[0])){
                    temp.send(this.username+"@"+temp.username+":"+datas[1]);
                    flag = 1;
                    return ;
                }
            }if(0 == flag){
                send("没有"+datas[0]+"这个用户");
            }
            return;
        }
        //多人聊天的实现
        //foreach遍历容器
        for (Channel temp:group) {
            if(temp.username == this.username)
                continue;
            else
                temp.send(this.username+":"+msg);//图片里这里有点问题,应该是加temp
        }
    }

    //从客户端接收消息
    //简单的IO流操作,没什么好说的
    public String recive(){
        String msg = "";
        try {
            msg = dis.readUTF();
        }catch (IOException e){
            System.out.println("接收消息错误");
            return null;
    }
        return msg;
    }

    public void moveAll(){
        new ToolClass().relaseAll(client,dis,dos);
        flag = false;
        group.remove(this);
    }

    /**
     * 让程序实现Runnable接口,重写run方法
     * 与客户端对接
     */
    @Override
    public void run() {
        getUsername();
        while (flag){
            String msg = recive();
            if(msg!=null){
                sendOthers(msg);
            }else {
                moveAll();
                break;
            }
        }
    }

    public void getUsername() {
        try {
            this.username = dis.readUTF();
        }catch (IOException e){
            System.out.println("错误的用户名");
        }
    }
}

客户端

package com.sxt.chatForMy;

import java.io.*;
import java.net.Socket;

/**
 * 客户端与服务器建立连接
 */
public class Client001 {
    public static void main(String[] args) throws IOException {
        //连接服务器
        Socket client = new Socket("localhost",8888);
        //创建流
        DataInputStream  dis = new DataInputStream(client.getInputStream());
        DataOutputStream dos = new DataOutputStream(client.getOutputStream());
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        //获取用户名
        System.out.print("请输入一个用户名:");
        String username = br.readLine();
        dos.writeUTF(username);
        dos.flush();

        //挂起接收消息的线程
        new Thread(()->{
            try {
                while (true){
                System.out.println(dis.readUTF());
                }
            } catch (IOException e) {
                System.out.println("连接服务器错误");
            }
        }).start();

        //发消息的线程
        while (true){
            String msg = "";
            msg = br.readLine();
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

工具类

package com.sxt.chatForMy;

import java.io.Closeable;
import java.io.IOException;

/**
 * 可变参数个数传入
 * 关闭时一定要判断是否为null
 */
public class ToolClass {
    public void relaseAll(Closeable... targets){
        for (Closeable temp:targets) {
            try {
                if(null!=temp){
                    temp.close();
                }
            }catch (IOException e){e.printStackTrace();}
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值