用Java从0开始实现Socket编程视频直播通信02—————多任务服务器,实现群聊私聊

要实现多任务的服务器,就离不开线程的使用,首先通过使用Runnable接口来进行线程操作,改一改代码。

package comserver240808;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MServer {
    //自定义服务器
    public void creatServer() throws Exception{
        //创建服务器
        ServerSocket  server = new ServerSocket(8899);
        System.out.println("启动服务器。。。");
        while (true) {
            //阻塞监听连接过来的客户端
            Socket socket = server.accept();
            //获取输入输出流
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
            String msg = "Connection successful!\r\n";
            //发送数据
            outputStream.write(msg.getBytes());
            outputStream.flush();
            //启动线程跟客户端通信
            ServerThread st = new ServerThread(socket);
            new Thread(st).start();
        }

    }

    public static void main(String[] args) {
        try {
            new MServer().creatServer();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

package comserver240808;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

//保持跟客户端通信的线程
public class ServerThread implements Runnable{
    private Socket socket;
    private InputStream inputStream;
    private OutputStream outputStream;
    public ServerThread(Socket socket){
        this.socket = socket;
        try {
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public void run() {
        try {
            String msg = readMsg(inputStream);
            //群聊3个人
            System.out.println("msg:"+msg);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    //读取客户端数据
    public String readMsg(InputStream inputStream)throws Exception{
        StringBuilder stringBuilder = new StringBuilder();
        int b = 0;
        while ((b=inputStream.read())!=13){
            //拼接一条聊天消息
            stringBuilder.append((char) b);
        }
        return stringBuilder.toString().trim();
    }
}

测试一下是否可以通过这个线程,使多个输入被拦截,同时输出到运行栏里面,实现类似于群聊的效果

成功了,两不同的客户端的输入被拦截

但是这只是单向的,只有服务端可以收到两条消息,客户端只能看到自己发的消息

所以我们对代码进行一些改进,用一个列表来保存在线的客户,然后同过循环使一个人发的消息可以被所有人接收到

package comserver240808;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class MServer {
    //自定义服务器
    public void creatServer() throws Exception{
        //创建服务器
        ServerSocket  server = new ServerSocket(8899);
        System.out.println("启动服务器。。。");
        //保存所有上线好友
        List<Socket> socketList =  new ArrayList<>();
        while (true) {
            //阻塞监听连接过来的客户端
            Socket socket = server.accept();
            socketList.add(socket);
            //获取输入输出流
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
            String msg = "Connection successful!\r\n";
            //发送数据
            outputStream.write(msg.getBytes());
            outputStream.flush();
            //启动线程跟客户端通信
            ServerThread st = new ServerThread(socket,socketList);
            new Thread(st).start();
        }

    }

    public static void main(String[] args) {
        try {
            new MServer().creatServer();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

package comserver240808;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.List;

//保持跟客户端通信的线程
public class ServerThread implements Runnable{
    private Socket socket;
    private InputStream inputStream;
    private OutputStream outputStream;
    private List<Socket> socketList;
    public ServerThread(Socket socket,List<Socket> socketList){
        this.socket = socket;
        this.socketList = socketList;
        try {
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public void run() {
        while (true){
        try {
            String msg = readMsg(inputStream);
            System.out.println("msg :"+msg);
            //群聊
            //遍历所有在线好友
            for (Socket s:socketList){
                if (socket != s){
                    //在线好友输出列表
                    OutputStream outputStream1=s.getOutputStream();
                    outputStream1.write((msg+"\r\n").getBytes());
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        }
    }
    //读取客户端数据
    public String readMsg(InputStream inputStream)throws Exception{
        StringBuilder stringBuilder = new StringBuilder();
        int b = 0;
        while ((b=inputStream.read())!=13){
            //拼接一条聊天消息
            stringBuilder.append((char) b);
        }
        return stringBuilder.toString().trim();
    }
}

然后运行一下

结果是,3个客户端和1个服务端都可以收到同样的信息,达到了我们的目的。

但是其实我们并没有实现群聊和私聊的区分,所以我们建立一个map,分别用Integer和Socket来存储用户和消息,并且通过分割符来对输入内容进行辨别,然后借此达到群聊和私聊的目的

package comserver240808;

import Tetrisofcyz24124.I;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class MServer {
    // 自定义服务器方法,用于创建并启动服务器
    public void creatServer() throws Exception {
        // 创建一个服务器套接字,监听端口8899
        ServerSocket server = new ServerSocket(8899);
        System.out.println("启动服务器。。。");

        // 用于保存所有连接的客户端,使用Map进行管理,键为客户端ID,值为对应的Socket
        Map<Integer, Socket> map = new HashMap<>();
        int ID = 1; // 客户端顺序ID

        // 无限循环,持续监听客户端连接
        while (true) {
            // 阻塞式方法,等待客户端连接
            Socket socket = server.accept();
            map.put(ID, socket);

            // 获取客户端的输入输出流
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();

            // 发送消息给客户端,告知其ID
            String msg = "Client" + ID + "\r\n";
            ID++;

            // 发送数据到客户端
            outputStream.write(msg.getBytes());
            outputStream.flush();

            // 启动一个新的线程,与客户端进行通信
            ServerThread st = new ServerThread(socket, map);
            new Thread(st).start();
        }
    }

    public static void main(String[] args) {
        try {
            // 创建并启动服务器
            new MServer().creatServer();
        } catch (Exception e) {
            // 捕获异常并抛出运行时异常
            throw new RuntimeException(e);
        }
    }
}
package comserver240808;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;

// 定义一个线程类,用于保持与客户端的通信
public class ServerThread implements Runnable {
    private Socket socket; // 当前客户端的Socket
    private InputStream inputStream; // 客户端的输入流
    private OutputStream outputStream; // 客户端的输出流
    private Map<Integer, Socket> map; // 保存所有在线客户端的Map

    // 构造方法,接收客户端Socket和所有在线客户端的Map
    public ServerThread(Socket socket, Map<Integer, Socket> map) {
        this.socket = socket;
        this.map = map;
        try {
            // 初始化输入输出流
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
        } catch (IOException e) {
            // 捕获异常并抛出运行时异常
            throw new RuntimeException(e);
        }
    }

    @Override
    public void run() {
        // 持续监听客户端的消息
        while (true) {
            try {
                // 读取客户端发送的消息
                String msg = readMsg(inputStream);
                System.out.println("msg :" + msg);

                // 消息格式:消息头:消息内容。通过":"分割,提取消息头和消息内容
                String[] msgArr = msg.split(":");

                if (msgArr[0].equals("@")) {
                    // 群聊模式,消息头为"@",所有在线客户端都接收消息
                    for (Map.Entry<Integer, Socket> entry : map.entrySet()) {
                        Socket s = entry.getValue();
                        if (socket != s) {
                            // 向其他在线客户端发送消息
                            OutputStream outputStream1 = s.getOutputStream();
                            outputStream1.write((msg + "\r\n").getBytes());
                        }
                    }
                } else {
                    // 私聊模式,根据消息头中的ID查找对应的客户端
                    Socket s = map.get(Integer.parseInt(msgArr[0]));
                    if (s != null) {
                        // 向指定客户端发送消息
                        OutputStream outputStream1 = s.getOutputStream();
                        outputStream1.write((msgArr[0] + ":" + msgArr[1] + "\r\n").getBytes());
                    }
                }
            } catch (Exception e) {
                // 捕获异常并抛出运行时异常
                throw new RuntimeException(e);
            }
        }
    }

    // 读取客户端发送的消息
    public String readMsg(InputStream inputStream) throws Exception {
        StringBuilder stringBuilder = new StringBuilder();
        int b = 0;

        // 读取输入流中的数据,直到遇到回车符(ASCII 13)
        while ((b = inputStream.read()) != 13) {
            stringBuilder.append((char) b); // 将读取到的字符拼接到StringBuilder中
        }

        // 返回完整的消息,去除前后空格
        return stringBuilder.toString().trim();
    }
}

运行代码后在用户3输入对用户1的私聊,以及@对全部的群聊,能够实现预期的效果。(这里的代码增加了更多的注释便于理解)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值