设计网络聊天室 基于Socket套接字实现(控制台多线程输出)

太久没更博客了(我真懒)

主要是老师布置了个不是人能完成的任务

先说一下基本思路:

网络聊天室要实现四个功能

注册功能:也就是个人信息的象征,也是接下来谁给你发信息的人的信息要素

私聊功能:实现一对一线程通信,但通信不让其他线程的人看见

群聊功能:实现一对多广播通信,通信让其他人看见

统计在线人数:将个人信息实时更新并遍历

退出功能:实现下线功能,将个人信息删除掉

需要用到的概念:(如果有不理解的概念可以根据下面的自行查找解释)

操作系统的线程,多线程的,以及线程池概念 和IO 输入输出流操作

面向对象java学习中的实现Runnable方法 实现多线程操作 以及其API socket类使用 集合类

计算机网络的套接字概念 服务器的概念 TCP/IP 或者UDP/IP概念

服务端实现思路:

package chatroom;
//SWPU LQ 2021.12.17
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiThreadServer {
    public static void main(String[] args) {
        //设置线程线程池
        final ExecutorService executorService =
                Executors.newFixedThreadPool(10);
        try{
            ServerSocket serverSocket = new ServerSocket(6666);
            System.out.println("等待服务端连接...");
            while (true){
                Socket client = serverSocket.accept();
                executorService.submit(new ExecuteClient(client));
            }
        }
        catch (IOException e){
            e.printStackTrace();

        }
    }
}

/*
* 注册
* 群聊
* 退出
* 显示当前在线人数
*/

class ExecuteClient implements Runnable{
    private final Socket client;
    //存放当前注册的所有用户 新建了很多执行对象,所以我将数据类型设置为静态
    private static final Map<String,Socket> ONLINE_USER_MAP = new ConcurrentHashMap<>();
    //这里用到了哈希表的问题,为了更好的解决哈希表和并发问题
    //这里我用jdk提供的实现队列哈希表来更好的解决并发和同步锁问题,使同步锁转换成分部锁
    //简单来说这种数据结构更好地匹配了多线程执行操作

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

    public void run(){
        try{
            Scanner scanner  = new Scanner(client.getInputStream());
            scanner.useDelimiter("\n");
            while(true){
                String message =scanner.nextLine();
                /*
                 约定格式 按照格式来
                  1.注册 userName:<name>
                  2.私聊 private:<name>:<message>
                  3.群聊 group:<message>
                  4.退出 sayonara;
                 */
                if(message.startsWith("userName")){
                    String userName = message.split(":")[1];
                    //这个意思是以split分隔的字符串数组,取分隔后的子字符串
                    register(userName,client);
                    continue;
                }
                if(message.startsWith("private")){
                    String userName = message.split(":")[1];
                    String mes = message.split(":")[2];
                    privateChat(userName,mes);
                    continue;
                }
                if(message.startsWith("group")){
                    String mes = message.split(":")[1];
                    groupChat(mes);
                    continue;
                }
                if(message.equals("sayonara")){
                    quit();
                    break;
                }
            }
        }
        catch(Exception e){
            e.printStackTrace();
        }


    }
    private void register(String userName,Socket client) throws IOException{
        System.out.println("欢迎用户名为:"+userName+",地址为"+client.getRemoteSocketAddress());
        ONLINE_USER_MAP.put(userName,client);//把当前用户存到map集合里
        PrintStream sendToClient = new PrintStream(client.getOutputStream());
        sendToClient.println(userName+"注册成功!");
        onLineUser();


    }
    public void privateChat(String userName,String mes) throws IOException{
        //谁跟你说
        String currentUserName = getCurrentUserName();
        //你要和谁说
        Socket target = ONLINE_USER_MAP.get(userName);
        if(client!=null){
            PrintStream sendToClient = new PrintStream(target.getOutputStream());
            sendToClient.println(currentUserName+"悄悄对你说:"+mes);



        }


        }
    private void groupChat(String mes) throws IOException {
        for(Socket socket : ONLINE_USER_MAP.values()){
            PrintStream sentToClient = new PrintStream(socket.getOutputStream());
            if(socket.equals(client)){
                continue;
            }
            String currentUserName = getCurrentUserName();
            //发送消息
            sentToClient.println(currentUserName + " 说:"+ mes);
        }
    }

        private void quit() throws IOException{
            String currentUserName=  getCurrentUserName();
            System.out.println("用户:"+currentUserName+"已下线");
            PrintStream sendToClient = new PrintStream(client.getOutputStream());
            sendToClient.println("bye");
            ONLINE_USER_MAP.remove(currentUserName);
            onLineUser();
    }
    private void onLineUser(){
        System.out.println("当前在线的人数"+ONLINE_USER_MAP.size()+",这些整天不好好学习就在这聊天的人,名单如下");
        for (Map.Entry<String,Socket> entry:ONLINE_USER_MAP.entrySet()){
            System.out.println(entry.getKey());
            }
        }
        private String getCurrentUserName() {
            String currentUserName = "";
            for (Map.Entry<String, Socket> entry : ONLINE_USER_MAP.entrySet()) {
                if (this.client.equals(entry.getValue())) {
                    currentUserName = entry.getKey();
                    break;
                }
            }
            return currentUserName;
        }
        }





客户端实现步骤:

实现步骤1:用线程向服务端传数据
实现步骤2:用线程从服务端读取数据
package chatroom;

import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

class ReadDataToServer implements Runnable {
    private  final Socket client;

    public ReadDataToServer(Socket client) {
        this.client = client;
    }
    @Override
    public void run() {
        try {
            Scanner scanner = new Scanner(client.getInputStream());
            scanner.useDelimiter("\n");
            while (scanner.hasNext()) {
                String message = scanner.nextLine();
                System.out.println("服务器的消息:" + message);
                if (message.equals("sayonara")) break;

            }

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

}

class WriteDataToServer implements Runnable {
    private  final Socket client;

    public WriteDataToServer(Socket client) {
        this.client = client;
    }
    @Override
    public void run() {
        try {
            //从键盘上读数据
            PrintStream printStream = new PrintStream(client.getOutputStream());
            Scanner scanner = new Scanner(System.in);
            scanner.useDelimiter("\n");
            while (true) {
                System.out.println("请输入你要发的消息:");
                String message = scanner.nextLine();
                printStream.println(message);
                if (message.equals("sayonara")) {
                    //关闭客户端
                    client.close();
                    break;
                }

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

public class MultiThreadClient {
    public static void main(String[] args) {
        try{
            Socket client = new Socket("127.0.0.1",6666);
            //实现步骤1:用线程向服务端传数据,
            new Thread(new WriteDataToServer(client)).start();
            //实现步骤2:用线程从服务端读取数据
            new Thread(new ReadDataToServer(client)).start();

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

坚持本就与本性背道而驰

所以改变世界的人从不循规蹈矩

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值