JavaChatRoom

项目名称:  JavaChatRoom

项目功能:用户登录、用户下线、群聊、私聊

涉及技术:

              1.网络编程(JavaSocket API)

              2.Java多线程技术

              3.使用C/S架构

项目主体

               服务器

               1.使用ServerSocket来建立起一个连接,public ServerSocket(int port): 此方法默认绑定本地ip地址 127.0.0.1 及指定端口号

               2.建立连接accept:

                 public Socket accept(): 等待客户端连接,线程阻塞,当有客户端连接时,返回客户端socket

               3.服务器与客户端通信

                 当客户端与服务器建立起连接后,通过输入输出流(客户端socket)来进行通信

                 对于服务器端,获取客户端输入流,读取客户端发来的信息

                 public InputStream getInputStream()

                 获取客户端输出流,向客户端发送信息

                 public OutputStream getOutputStream()

                 服务器端功能:

                 注册用户:userName: name

                 用户群聊:G:聊天内容

                 用户私聊:P:test-聊天内容

                 用户退出:byebye

                客户端:

                   1.Socket类:

                   使用Socket方法: public Socket(String host,int port):绑定指定域名,端口号的服务器,只要不报错就建立连接

                   2.客户端与服务器通信:

                   获取客户端输入流,读取服务器发来的信息

                   public InPutStream  getInputStream()

                   获取客户端输出流,向服务器发送信息

                   public OutputStream getOutputSteam()

主体代码实现

分为单线程多线程版本:

单线程版本

由于是单线程版本,当没有客户端连接时,服务器线程一直处于阻塞状态,直到有客户端连接时,才会返回客户端socket

服务端代码

public class SingleThreadServer {
    public static void main(String[] args) {
        try {
            // 建立服务端ServerScoket 并绑定本地 6666 端口号
            ServerSocket serverSocket = new ServerSocket(6666);
            // 等待客户端连接
            System.out.println("等待客户端连接ing...");
            // 服务器线程一直阻塞,直到有客户端连接,返回客户端连接Socket
            Socket client = serverSocket.accept();
            System.out.println("有客户端连接,客户端端口号为"+client.getPort());
            //获取客户端输出流,向客户端输出信息                            自动刷新
            PrintStream out = new PrintStream(client.getOutputStream(),true ,"UTF-8");
            //获取客户端输入流,读取客户端信息
            Scanner in = new Scanner(client.getInputStream());
            if(in.hasNextLine()){
                //读取一整行输入
                System.out.println("客户端发来的信息为:"+in.nextLine());
            }
            out.println("Server : Hello sir, I am Jarvis,may I help you?");
            //关闭流
            in.close();
            out.close();
            serverSocket.close();
        } catch (IOException e) {
            System.err.println("服务器建立连接失败,异常为 "+e);
        }
    }
}

服务端界面

 

客户端代码

1.客户端建立与服务器的连接,绑定本地ip地址和 指定端口号 6666

2.由于是单线程,服务器是先得到客户端信息,才能向客户端发送消息,为了防止线程阻塞,客户端先向服务器发送信息,再获取服务器端信息

public class SingleThreadClient {
    public static void main(String[] args) {
        try {
            // 建立客户端Socket并绑定服务器
            Socket client = new Socket("127.0.0.1",6666);
            // 获取输入、输出流与服务器通信
            // 获取输出流,向服务器发送信息
            PrintStream out = new PrintStream(client.getOutputStream(),
                    true,"UTF-8");
            out.println("Hi,I am Client");
            //获取输入流,读取服务器发送的信息
            Scanner in = new Scanner(client.getInputStream());
            if(in.hasNextLine()){
                System.out.println("服务器发来的信息为 "+in.nextLine());
            }
            in.close();
            out.close();
            client.close();
        } catch (Exception e) {
            System.err.println("客户端出现异常 :"+e);
        }
    }
}

客户端界面

 

多线程版本

多线程服务器

1.使用固定大小线程池实现多线程,这里使用的线程池大小为20

2.在这里使用内部类实现客户端的请求,每当有新的客户端请求,就新建线程处理这个请求

直到线程池满就关闭线程池,关闭服务器

3.在服务器端中,使用concurrentHashMap来存储所有连接到服务器的客户端信息

4.由于每个平台中的换行符不同,识别windows下的换行符,将其替换为 " "

// 识别windows下换行符,将 \r 替换为 ""
// pattern为模式,matcher为匹配,匹配后做一个替换
Pattern pattern = Pattern.compile("\r");
Matcher matcher = pattern.matcher(str);
matcher.replaceAll("");

5.四大功能实现

// 获取客户端输入流,读取客户端发来的信息
Scanner in = new Scanner(client.getInputStream());

根据这行代码读取客户端发来的信息

// 一行一行读取客户端信息
if(in.hasNextLine()){
     str = in.nextLine();

 1.注册功能实现

          如果识别到 输入信息中有 "userName",就说明有用户注册,使用 split()方法进行字符串分割,将用户名信息分割出来,然后使用 userRegister()方法,将用户信息,及socket保存在 map中,并且打印聊天室中人数。

if(str.startsWith("userName")){
       // userName:test
       String userName = str.split("\\:")[1];
       userRegister(userName,client);
       continue;
}
// 注册方法
        private static void userRegister(String userName,Socket socket){
            System.out.println("用户"+userName+"上线了!");
            // 将用户保存在map中
            clientMap.put(userName,socket);
            System.out.println("当前聊天室中一共有"+clientMap.size()+"人");
            // 告知客户端注册成功
            try {
                // 获取每个Socket的输出流
                PrintStream out = new PrintStream(socket.getOutputStream());
                out.println("用户注册成功!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

 用户 one two three 都上线,并且聊天室中人数随之更新

2.群聊功能实现

 如果识别到输入信息中有 "G",就说明是群聊信息,使用 split() 方法,分割出有用信息

再使用 groupChat(msg) 方法,遍历取出每个soceket,并且获取每个socket的输出流,向每个客户端发送群聊消息

// 群聊方法
        private static void GroupChat(String msg){
            Set<Map.Entry<String,Socket>> clientSet =
                    clientMap.entrySet();
            for(Map.Entry<String,Socket> entry : clientSet){
                // 遍历取出每个Socket
                Socket socket = entry.getValue();
                // 获取每个Socket 的输出流
                try {
                    PrintStream out = new PrintStream(socket.getOutputStream());
                    out.println("群聊消息为 "+msg);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

3.私聊方法实现

如果输入信息中有" P ",就说明是私聊方法,获取用户名,聊天信息,使用PrivatedChat()方法,传入用户名,信息

然后根据用户名获取指定的socekt,然后获取该socket的输出流,使用 socket.getOutputStream() 向用户发送信息

// 私聊方法
        private static void PrivateChat(String userName,String msg){
            // 根据用户名获取指定Socket
            Socket socket = clientMap.get(userName);
            try {
                // 获得指定Socket输出流
                PrintStream out = new PrintStream(socket.getOutputStream());
                out.println("私聊消息 "+msg);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

4.用户退出实现

如果输入的信息中包含 "byebye", 那么遍历map中的key,找到与client相等的 key,移除掉 username ,并同步聊天室中剩余人数

// 客户端退出
                        if(str.contains("byebye")){
                            // 遍历Map,获取userName
                            String userName = "";
                            // 获取所有的方法
                            for(String key : clientMap.keySet()){
                                //  遍历 Map中的key,找到与client相等的value
                                if(clientMap.get(key).equals(client)){
                                    userName = key;
                                }
                            }
                            System.out.println("用户"+userName+"下线了...");
                            clientMap.remove(userName);
                            System.out.println("当前聊天室一共 "+clientMap.size()+"人");
                            continue;
                        }

当有客户端下线时,服务器显示 具体哪个客户端下线,并显示聊天室中还有几人 

多线程客户端

 1.socket 绑定服务器 ip 地址及 端口号

 2.客户端使用全双工,一个读线程,一个写线程,实现信息的收发

客户端读写线程代码


public class MultiThreadClient {

    public static void main(String[] args) {
        try {
            Socket client = new Socket("127.0.0.1",6666);
            Thread readThread = new Thread(new ReadFromServer(client));
            Thread writeThread = new Thread(new WriteToServer(client));
            readThread.start();
            writeThread.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

读线程实现

    获取客户端输入流,使用 Scanner in = new Scanner(client.getInputStream()) 读取服务器发来的信息,

class ReadFromServer implements  Runnable{
    private Socket client;

    public ReadFromServer(Socket client){
        this.client = client;
    }

    @Override
    public void run() {
        try {
            // 获取客户端输入流,读取服务器发送的信息
            Scanner in = new Scanner(client.getInputStream());
            while(true){
                // 若client已经被关闭,此处自动退出了
                if(in.hasNextLine()){
                    System.out.println("从服务器发来的信息为 "+in.nextLine());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

写线程实现:

   //  获取键盘输入流,读取用户从键盘发送的信息

    Scanner in = new Scanner(system.in)

   //  获取客户端输出流,将用户键入的信息发送给服务器

   PrintStream out = new PrintStream(client.getOutputStream())

  当用户的输入信息含有 "byebye",关闭键盘输入流,输出流,及客户端

class WriteToServer implements Runnable{
    private Socket client;
    public WriteToServer(Socket client){
        this.client = client;
    }

    @Override
    public void run() {
        try {
            // 获取键盘输入流,读取用户从键盘发来的信息
            Scanner scanner = new Scanner(System.in);
            String string = "";
            // 获取客户端输入流,将用户键入的信息发送给服务器
            PrintStream out = new PrintStream(client.getOutputStream(),
                    true,"UTF-8");
            while(true){
                System.out.println("请输入向服务器发送的信息");
                if(scanner.hasNextLine()){
                    string = scanner.nextLine();
                    out.println(string);
                }
                //设置退出标志
                if(string.contains("byebye")){
                    System.out.println("客户端退出,不聊了");
                    scanner.close();
                    out.close();
                    client.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端注册界面

客户端one,two,three 都进行注册

 客户端群聊界面

客户端one发起群聊,所有客户端都可以接收到此消息,发送信息为:testtest!

 客户端 two 接收到 one发送的消息

 

客户端 three 接收到 one发送的消息

 客户端私聊界面

three 向 one 送消息

one 接收到消息

由于是私聊,two 没有接收到消息

 

客户端退出界面: 

当 one 发送信息中含有 byebye时,客户端 one 下线

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java聊天室程序 需求分析 2.1 业务需求 1. 与聊天室成员一起聊天。 2. 可以与聊天室成员私聊。 3. 可以改变聊天内容风格。 4. 用户注册(含头像)、登录。 5. 服务器监控聊天内容。 6. 服务器过滤非法内容。 7. 服务器发送通知。 8. 服务器踢人。 9. 保存服务器日志。 10.保存用户聊天信息。 2.2 系统功能模块 2.2.1 服务器端 1.处理用户注册 2.处理用户登录 3.处理用户发送信息 4.处理用户得到信息 5.处理用户退出 2.2.2 客户端 1.用户注册界面及结果 2.用户登录界面及结果 3.用户发送信息界面及结果 4.用户得到信息界面及结果 5.用户退出界面及结果 2.3 性能需求 运行环境:Windows 9x、2000、xp、2003,Linux 必要环境:JDK 1.5 以上 硬件环境:CPU 400MHz以上,内存64MB以上 3.1.2 客户端结构 ChatClient.java 为客户端程序启动类,负责客户端的启动和退出。 Login.java 为客户端程序登录界面,负责用户帐号信息的验证与反馈。 Register.java 为客户端程序注册界面,负责用户帐号信息的注册验证与反馈。 ChatRoom.java 为客户端程序聊天室主界面,负责接收、发送聊天内容与服务器端的Connection.java 亲密合作。 Windowclose 为ChatRoom.java的内部类,负责监听聊天室界面的操作,当用户退出时返回给服务器信息。 Clock.java 为客户端程序的一个小程序,实现的一个石英钟功能。 3. 2 系统实现原理 当用户聊天时,将当前用户名、聊天对象、聊天内容、聊天语气和是否私聊进行封装,然后与服务器建立Socket连接,再用对象输出流包装Socket的输出流将聊天信息对象发送给服务器端 当用户发送聊天信息时,服务端将会收到客户端用Socket传输过来的聊天信息对象,然后将其强制转换为Chat对象,并将本次用户的聊天信息对象添加到聊天对象集Message中,以供所有聊天用户访问。 接收用户的聊天信息是由多线程技术实现的,因为客户端必须时时关注更新服务器上是否有最新消息,在本程序中设定的是3秒刷新服务器一次,如果间隔时间太短将会增加客户端与服务器端的通信负担,而间隔时间长就会让人感觉没有时效性,所以经过权衡后认为3秒最佳,因为每个用户都不可能在3秒内连续发送信息。 当每次用户接收到聊天信息后将会开始分析聊天信息然后将适合自己的信息人性化地显示在聊天信息界面上。 4.1.1 问题陈述 1.接受用户注册信息并保存在一个基于文件的对象型数据库。 2.能够允许注册过的用户登陆聊天界面并可以聊天。 3.能够接受私聊信息并发送给特定的用户。 4.服务器运行在自定义的端口上#1001。 5.服务器监控用户列表和用户聊天信息(私聊除外)。 6.服务器踢人,发送通知。 7.服务器保存日志。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值