结合多线程、Socket的聊天小程序

本文通过对比分析,展示了如何通过引入多线程解决socket通信中阻塞问题,进而实现客户端与服务器端的高效对话。通过具体实例演示了改进后的客户端和服务端代码,清晰阐述了多线程在解决此问题上的优势,并强调了socket通信在实际应用中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

客户端:

public class Client {
    public static void main(String[] args) throws UnknownHostException, IOException {
        //使用socket来连接到指定服务器
        while(true){
            Socket s = new Socket("127.0.0.1",8888);
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            //同理,下面的br.readLine()也把程序阻塞了!!
            String s1 = br.readLine();
            System.out.println(s1);
            System.out.println("请输入返回给服务器的话:");
            Scanner scanner = new Scanner(System.in);
            String string = scanner.nextLine();
            bw.write("来自客户端的信息:"+string);
        }
    }
}

服务器端:

public class Server {
    public static void main(String[] args) throws IOException {
        //创建一个serversocket用于监听客户端socket的连接请求
        ServerSocket ss = new ServerSocket(8888);
        while(true){
            Socket s = ss.accept();
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));

            System.out.println("请输入要发送到客户端的信息:");
            Scanner scanner = new Scanner(System.in);
            String string = scanner.nextLine();
            bw.write("来自服务器端的信息:"+string);
//因为readLine()方法是阻塞式的,所以这句话无论放在程序的哪里,程序都会等在那,什么也不执行!
//比如说,放在下面现在的位置,那么好,输出要发送给客户端的话之后,程序就阻塞在那里不动了,因为它在等待客户端给它传话呢!
//即使先让客户端发话也是没用的,因为客户端也在等服务端给它传话!所以也阻塞
            System.out.println(br.readLine());
            bw.close();
            s.close();
        }
    }
}

上面这个小程序无法实现客户端和服务端来回对话,原因上面注释中已经说明了,就是因为br.readLine()是阻塞式的。

来看改进后的例子:

客户端:

public class MyClient {
    public static void main(String[] args) throws UnknownHostException, IOException {
        Socket s = new Socket("127.0.0.1",30000);
        new Thread(new ClientThread(s)).start();
        PrintStream ps = new PrintStream(s.getOutputStream());
        String line = null;
        BufferedReader br = new BufferedReader(new 
                InputStreamReader(System.in));
        while((line = br.readLine())!=null){
            ps.println(line);
        }
    }
}
public class ClientThread implements Runnable{
        private Socket s;
        BufferedReader br = null;
        public ClientThread(Socket s) throws IOException{
            this.s = s;
            br = new BufferedReader(new 
                    InputStreamReader(s.getInputStream()));
        }
        public void run(){
            try {
                String content = null;
                while((content = br.readLine())!=null){
                    System.out.println(content);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

服务端:

public class MyServer {
    public static ArrayList<Socket> socketList = new ArrayList<Socket>();
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(30000);
        while(true){
            Socket s = ss.accept();
            socketList.add(s);
            //为什么加上线程就能完成客户端和服务端通信呢?
            //因为原来单线程的时候,服务端等待接收来自客户端的信息时,br=readLine()方法是阻塞式的,
            //所以无论把br=readLine()放在程序哪里都会阻塞!客户端也是一个道理。
            //而加上了线程就不一样了,比如下面:假设先执行了子线程new Thread(new ServerThread(s)).start(),
            //然后子线程被阻塞(因为在等待客户端传话),所以主线程得到执行,同理客户端也是这个道理。总之,总能保证整个程序能执行下去
            new Thread(new ServerThread(s)).start();
            PrintStream ps = new PrintStream(s.getOutputStream());
            String line = null;
            BufferedReader br = new BufferedReader(new 
                    InputStreamReader(System.in));
            while((line = br.readLine())!=null){
                ps.println(line);
            }
        }
    }
}
public class ServerThread implements Runnable{
    Socket s = null;
    BufferedReader br = null;
    public ServerThread(Socket s) throws IOException{
        this.s = s;
        br = new BufferedReader(new InputStreamReader(s.getInputStream()));
    }
    @Override
    public void run() {
        try{
            String content = null;
            while((content = readFromClient()) != null){
                System.out.println(content);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }

    }

    private String readFromClient(){
        try {
            return br.readLine();
        } catch (IOException e) {
            MyServer.socketList.remove(s);
        }
        return null;
    }

}

加上了线程就能实现对话功能了。通过这两个小例子的对比,让我更加清楚的认识到多线程的好处和必要性,并且还让我熟悉了socket通信,一举多得。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值