java实现聊天功能(ServerSocket与Socket)

Java聊天室

使用了多线程处理发送消息与接受消息,发送消息与接受消息互不干扰

接受消息类

AcceptMsg: 用于接收消息,扩展了Thread类。它在构造函数中接受一个Socket对象,并重写Thread类的run()方法

  • 在run()方法中,它创建了一个InputStream和一个BufferedReader对象,用于从Socket的输入流中读取消息。
  • 然后,它进入一个无限循环,从输入流中连续读取消息,直到收到消息“exit”。如果消息是“退出”,它将打印一条消息,指示用户已离开聊天室并脱离循环。否则,它只是打印收到的消息。
  • 如果在读取输入流时抛出IOException,它会检查异常消息是否为“Socket closed”。如果不是,它将打印异常的堆栈跟踪。
  • 最后,它使用一个名为closeAll()的实用程序方法关闭所有输入/输出流和Socket对象。
  • 总的来说,这个类用于从Socket的输入流中读取消息,并将它们打印到控制台。

代码:

/**
*此类表示一个线程,该线程接受来自套接字连接的消息。
*它扩展了Thread类。
*/
public class AcceptMsg extends Thread {

    private final Socket accept;


    /**
*构造一个具有给定名称和套接字连接的新AcceptMsg对象。
*@param name线程的名称
*@param accept从中接受消息的套接字连接
*/
    public AcceptMsg(String name, Socket accept) {
        super(name);
        this.accept = accept;
    }

    
    /**
*线程的主要逻辑。
*它从套接字连接读取消息并将其打印到控制台。
*如果消息是“退出”,它将打印一条离开消息并中断循环。
*如果发生IOException,它将检查套接字是否已关闭,如果未关闭,则打印堆栈跟踪。
*最后,它关闭输入流、读取器和套接字连接。
*/
    @Override
    public void run() {
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = accept.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream));
            while (true) {
                String msg = reader.readLine();
                if ("exit".equals(msg)){
                    System.out.println(getName() + "已经离开聊天室");
                    break;
                }
                System.out.println(msg);
            }
        } catch (IOException e) {
            String errMsg = "Socket closed";
            if (!errMsg.equals(e.getMessage())){
                e.printStackTrace();
            }
        } finally {
            Utils.closeAll(inputStream, reader,accept);
        }
    }
}

发送消息类

SendMsg: 用于发送消息,通过套接字连接发送消息的线程。

  • 构造函数接受一个名称和一个套接字作为参数。名称用于标识线程,套接字用于建立连接。
  • run()方法被重写以定义线程的行为。在方法内部,创建了一个输出流和一个缓冲写入器,用于将消息写入套接字。还创建了一个扫描仪来读取用户输入。
  • 线程进入一个while循环,该循环无限期地继续。在循环中,系统会提示用户输入要发送的消息。如果消息是“exit”,
  • 线程将检查其名称是否为“李四(中文中“Li-Si”的意思)。如果是,则会打印一条消息,指示线程不能首先退出。
  • 否则,“退出”消息将写入套接字,循环将终止。
  • 如果消息不是“exit”,线程会将消息及其名称作为对套接字的响应写入。
  • 如果发生IOException,线程将检查错误消息是否为“Socket closed”。如果不是,则打印异常。最后,使用名
  • 为“closeAll()”的实用程序方法关闭输出流、写入程序和套接字

代码:

/**
*SendMsg类负责通过套接字向服务器发送消息。
*它扩展了Thread类以允许并发执行。
*/
public class SendMsg extends Thread {

    private final Socket socket;
    
 /**
*构造一个具有给定名称和套接字的新SendMsg对象。
*@param name线程的名称
*@param socket用于与服务器通信的套接字
*/
    public SendMsg(String name, Socket socket) {
        super(name);
        this.socket = socket;
    }

 /**
*run方法在线程启动时执行。
*它读取来自用户的消息并将其发送到服务器。
*/
    @Override
    public void run() {
        OutputStream outputStream = null;
        BufferedWriter writer = null;
        try {
            outputStream = socket.getOutputStream();
            writer = new BufferedWriter(new OutputStreamWriter(outputStream));
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.println(getName() + "发送:");
                String msg = scanner.next();
                if ("exit".equals(msg)) {
                    if ("李四".equals(getName())){
                        System.out.println("你不能先退出");
                        continue;
                    }
                    writer.write(msg);
                    writer.newLine();
                    writer.flush();
                    System.out.println("退出成功!");
                    break;
                }
                writer.write(getName() + "回复:" + msg);
                writer.newLine();
                writer.flush();
            }
        } catch (IOException e) {
            String errMsg = "Socket closed";
            if (!errMsg.equals(e.getMessage())){
                e.printStackTrace();
            }
        } finally {
            Utils.closeAll(outputStream, writer,socket);
        }
    }
}

服务器端类

ServerSocketTest: 作为服务器端类

这个类代表ServerSocket的一个测试类,它创建一个ServerSocket并等待连接,一旦建立了连接,它就会发送和接收来自客户端的消息。

public class ServerSocketTest {
/**
*ServerSocketTest类的主要方法。
*它创建一个ServerSocket并等待连接。
*一旦建立了连接,它就会发送和接收来自客户端的消息。
*
*@param args命令行参数。
*@throws IOException如果在等待连接或发送/接收消息时发生I/O错误。
*/
    public static void main(String[] args) throws IOException {
        System.out.println("==============ServerSocket端=============");
        //在端口8095上创建ServerSocke
        ServerSocket serverSocket = new ServerSocket(8095);
        System.out.println("等待链接...");
        //等待连接
        Socket accept = serverSocket.accept();
        System.out.println("链接成功");
        //获取套接字的输入和输出流
        InputStream inputStream = accept.getInputStream();
        OutputStream outputStream = accept.getOutputStream();
        //定义发送方和接收方的名称
        String sendName = "李四";
        String acceptName = "张三";
        byte[] bytes = new byte[1024];
        //从客户端读取消息
        int len = inputStream.read(bytes);
        System.out.println(new String(bytes, 0, len));
        //向客户端发送消息
        outputStream.write(("你好,Socket端,我是" + sendName).getBytes());
        //启动用于接收和发送消息的线程
        new AcceptMsg(acceptName,accept).start();
        new SendMsg(sendName, accept).start();

    }
}

客户端类

SocketTest: 作为客户端类

  • 该程序在端口8095建立到本地机器上运行的服务器的套接字连接。然后,它创建一个向服务器发送数据的输出流和
  • 一个从服务器接收数据的输入流。
  • 程序通过将消息写入输出流来向服务器发送消息。它还通过读取输入流从服务器读取响应。
  • 此外,该程序还创建了两个线程——一个用于发送消息,另一个用于接收消息。这些线程与主线程同时运行,允许
  • 客户端和服务器之间同时进行通信
/**
*这个类表示一个客户端套接字测试程序。
*它与服务器建立套接字连接,并发送和接收消息。
*/
public class SocketTest {
    public static void main(String[] args) throws IOException {
        System.out.println("==============Socket端=============");
        //与服务器建立套接字连接
        Socket socket = new Socket("127.0.0.1",8095);
        //获取套接字的输出流
        OutputStream outputStream = socket.getOutputStream();
        //获取套接字的输入流
        InputStream inputStream = socket.getInputStream();
        //定义发送方和接收方的名称
        String sendName = "张三";
        String acceptName = "李四";
        //向服务器发送消息
        outputStream.write(("你好,ServerSocket端,我是" + sendName).getBytes());
        byte[] bytes = new byte[1024];
        //从服务器读取响应
        int len = inputStream.read(bytes);
        System.out.println(new String(bytes, 0, len));
        //启动用于发送和接收消息的线程
        new SendMsg(sendName,socket).start();
        new AcceptMsg(acceptName,socket).start();

    }
}

工具类

Utils: 关闭资源类,用于关闭所有开通的资源,如:输入与输出流;此类提供用于处理实现Closeable接口的资源的实用程序方法。

closeAll方法可用于同时关闭多个资源:

  • 如果资源不为null,则将使用close方法关闭该资源。
  • 如果在关闭资源时发生IOException,则异常将打印到标准错误流中。
/**
*此类提供了用于关闭Closeable对象的实用方法。
*/
public class Utils {
    /**
*关闭所有提供的Closeable对象。
*@param关闭要关闭的Closeable对象数组
*/
    public static void closeAll(Closeable... close){
        for (Closeable c : close) {
            if (c != null){
                try {
                    c.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

运行效果

客户端:

在这里插入图片描述

服务器端:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值