[java网络编程]简易聊天程序-TCP-C/S-多线程

项目要求

设计一个简单的聊天室程序。采用C/S模式,分为客户端程序和服务器端程序。

客户端程序和服务器程序通过网络交换聊天字符串内容。同一端可实现多语句收发。

服务端

  • 创建ServerSocket并在本机8888窗口监听
  • 调用accept方法建立连接
  • 启动发送和接收线程

发送线程

  • BufferedWriter对象writer:用于向客户端写入内容
  • BufferedReader对象kb:用于获取键盘输入内容
  • running:标记是否关闭线程
重载run()方法
  1. 首先while循环条件检查running,running为false则关闭。
  2. 检查键盘输入kb是否可用。

可用则调用readLine方法获取输入并向客户端发送。同时检查输入是否为quit。如果是,则设置running为false以便关闭线程,同时调用receive线程的setRunning方法将标志设置为关闭。

不可用则线程休眠100ms,再从头执行。

  1. 发送

调用writer对象的write方法向客户端写入信息,调用newLine方法进行换行。最重要的:调用flush方法刷新缓冲区。

  1. 关闭

关闭writer、kb。

接收线程

  • BufferedReader对象reader:用于获取客户端发送的内容
  • running:标记是否关闭线程
run
  1. 检查running
  2. 调用reader的readLine方法获取客户端发送的信息
  3. 判断信息内容是否为空:不为空则在控制台打印输出
  4. 判断是否为quit:是则设置running和send线程的running为false。
  5. 关闭

代码

package pro2.multi;

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

/**
 * @author zayn
 * @date 2023/4/7/19:00
 * TCP 聊天程序服务器端
 */
public class Server {
    static ServerSocket serverSocket;
    static Socket socket;
    static int port = 8888;

    public static void main(String[] args) throws IOException, InterruptedException {
        serverSocket = new ServerSocket(port);
        System.out.println("服务端在8888端口监听");
        socket = serverSocket.accept();
        System.out.println("客户端连接成功");

        //创建线程并启动
        ServerSend send = new ServerSend();
//        send.setDaemon(true);//设置为守护线程
        send.start();

        ServerReceive receive = new ServerReceive();
//        receive.setDaemon(true);//设置为守护线程
        receive.start();

        //线程插队执行
        send.join();
        receive.join();

        //关闭流
        socket.close();
        serverSocket.close();

        System.out.println("服务端退出成功");
    }
}

class ServerSend extends Thread {
    //运行标记
    private static boolean running = true;
    //输出流
    static BufferedWriter writer;
    //键盘输入流
    static BufferedReader kb;
    //接收输入流
    static final int TIMEOUT = 100;

    static {
        try {
            writer = new BufferedWriter(new OutputStreamWriter(Server.socket.getOutputStream()));
            kb = new BufferedReader(new InputStreamReader(System.in));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void run() {
        while (running) {
            String str;
            try {
                if (kb != null && kb.ready()) {//检查键盘输入是否可用
                    str = kb.readLine();//获取键盘输入
                    if (!str.equals("")) {//判断是否为空
                        send(str);//发送信息至客户端
                        if (str.equals("quit")) {//判断是否退出
                            running = false;
                            close();
                            ServerReceive.setRunning();
                            break;
                        }
                    }
                } else {
                    //等待指定的超时时间
                    Thread.sleep(TIMEOUT);
                }
            } catch (IOException | InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 发送信息
     *
     * @param str 要发送的信息
     * @throws IOException IO异常
     */
    public static void send(String str) throws IOException {
        writer.write(str);
        writer.newLine();
        writer.flush();
    }

    /**
     * 关闭流
     */
    public static void close() {
        try {
            writer.close();
            kb.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void setRunning() {
        running = false;
    }
}

class ServerReceive extends Thread {
    static BufferedReader reader;
    //运行标记
    private static boolean running = true;

    static {
        try {
            reader = new BufferedReader(new InputStreamReader(Server.socket.getInputStream()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 接收信息
     * 判断quit退出
     *
     */
    @Override
    public void run() {
        while (running) {
            String str = "";
            try {
                str = reader.readLine();//接收信息
            } catch (IOException e) {
//                throw new RuntimeException(e);
            }
            if (!str.equals("")) {//判断是否为空
                System.out.println("客户端:" + str);
            }
            if (str.equals("quit")) {//判断是否退出
                running = false;
                ServerSend.setRunning();
                break;
            }
        }
        close();
    }

    /**
     * 关闭流和socket
     */
    public static void close() {
        try {
            reader.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置运行标记
     */
    public static void setRunning() {
        running = false;
    }
}

客户端

  • 创建连接本机8888窗口的套接字
  • 启动发送和接收线程

客户端功能和代码实现都与服务端有很高的相似性,这里直接放代码。

package pro2.multi;

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

/**
 * @author zayn
 * @date 2023/4/7/18:58
 * TCP 聊天程序客户端
 */
public class Client {
    static Socket ClientSocket;
    static int port = 8888;

    public static void main(String[] args) throws IOException, InterruptedException {
        //创建客户端套接字
        ClientSocket = new Socket(InetAddress.getLocalHost(), port);
        System.out.println("客户端启动成功");
        System.out.println("连接服务器成功");

        //创建线程并启动
        ClientSend send = new ClientSend();
//        send.setDaemon(true);//设置为守护线程
        send.start();

        ClientReceive receive = new ClientReceive();
//        receive.setDaemon(true);//设置为守护线程
        receive.start();

        //线程插队
        send.join();
        receive.join();

        //关闭
        ClientSocket.close();

        System.out.println("客户端退出成功");
    }
}

class ClientSend extends Thread {
    //输出流
    static BufferedWriter writer;
    //键盘输入流
    static BufferedReader kb;
    //接收输入流
    //运行标记
    private static boolean running = true;
    static final int TIMEOUT = 100;

    static {
        try {
            writer = new BufferedWriter(new OutputStreamWriter(Client.ClientSocket.getOutputStream()));
            kb = new BufferedReader(new InputStreamReader(System.in));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取键盘输入
     * 发送信息
     * 判断quit退出
     */
    @Override
    public void run() {
        while (running) {
            String str;
            try {
                if (kb != null && kb.ready()) {//检查键盘输入是否可用
                    str = kb.readLine();//获取键盘输入
                    if (!str.equals("")) {//判断是否为空
                        send(str);//发送信息至客户端
                        if (str.equals("quit")) {//判断是否退出
                            running = false;
                            close();
                            ClientReceive.setRunning();
                            break;
                        }
                    }
                } else {
                    //等待指定的超时时间
                    Thread.sleep(TIMEOUT);
                }
            } catch (IOException | InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 发送信息
     *
     * @param str 要发送的信息
     * @throws IOException IO异常
     */
    public static void send(String str) throws IOException {
        writer.write(str);
        writer.newLine();
        writer.flush();
    }

    /**
     * 关闭流和套接字
     */
    public static void close() {
        try {
            writer.close();
            kb.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置运行标记
     *
     */
    public static void setRunning() {
        running = false;
    }
}

class ClientReceive extends Thread {
    //输入流
    static BufferedReader reader;
    static Socket clientSocket;
    //运行标记
    private static boolean running = true;

    static {
        try {
            reader = new BufferedReader(new InputStreamReader(Client.ClientSocket.getInputStream()));
            clientSocket = Client.ClientSocket;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 接收信息
     * 判断quit退出
     *
     */
    @Override
    public void run() {
        while (running) {
            String str = "";
            try {
                str = reader.readLine();//接收信息
            } catch (IOException e) {
//                throw new RuntimeException(e);
            }
            if (str != null) {
                if (!str.equals("")) {//判断是否为空
                    System.out.println("服务端:" + str);
                }
                if (str.equals("quit")) {//判断是否退出
                    running = false;
                    ClientSend.setRunning();//关闭发送线程
                    break;
                }
            }
        }
        close();
    }

    /**
     * 关闭流
     */
    public static void close() {
        try {
            reader.close();
            clientSocket.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void setRunning() {
        running = false;
    }
}

结果展示

在这里插入图片描述

总结

两端发送和接收信息的处理都比较好做。就是某一端发起退出然后两端都退出的操作不是很好实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值