本文档描述了Client发送消息给Server, Server端打印接收的消息
一、Client
1.1.客户端的类Client.java中添加如下的start()方法 (表示启动客户端功能的方法),并调用
/**start方法,作为客户端开始工作的方法*/
    public void start(){
         try {
             //Socket提供的方法: OutputStream getOutputStream(),通过该Socket获取
            //一个字节输出流,使用这个流写出的所有字节都会陆续发送给服务端对应的Socket
            OutputStream out = socket.getOutputStream();
             OutputStreamWriter osw = new OutputStreamWriter(out, StandardCharsets.UTF_8);
             BufferedWriter bw = new BufferedWriter(osw);
             PrintWriter pw = new PrintWriter(bw,true);
             Scanner scanner = new Scanner(System.in);
             String nickname;
             while(true){
                 System.out.println("请输入昵称:");
                nickname = scanner.nextLine();
                 if(nickname.trim().length()>0){
                     pw.println(nickname);
                     break;
                }
                 System.out.println("昵称不能为空");
            }
             //启动读取服务端发送过来消息的线程
//            ServerHandler handler = new ServerHandler();
//            Thread t = new Thread(handler);
//            t.setDaemon(true);
//            t.start();
            while(true) {
                 String line = scanner.nextLine();
                 if("exit".equalsIgnoreCase(line)){
                     break;
                }
                 pw.println(line);
            }
        }catch (IOException e) {
            e.printStackTrace();
        } finally {
             try {
                 //调用socket的close方法时可以和对方发起四次挥手断开连接
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
1.2.main方法中调用此方法:
在main方法中添加client.start();

二、Server端:
A.服务端用 ServerSocket 对象开启一个端口。并在 while 循环中调用 ServerSocket 对象的 accept()方法等待客户端连接.
B.如果有客户端连接成功accept()方法会得到一个socket对象。
注: socket对象和服务端对应,里边包含此客户端和服务端对应的输入流和输出流可以用来读服务端发送的数据并给服务端返回数据。
C.将socket对象封装到一个线程中,并用线程池运行此线程中的任务。
2.1.线程池的类
创建线程池的核心类对象:

2.2.编写线程类:
/**该线程任务是负责与指定的客户端进行交互*/
    private class ClientHandler implements Runnable{
         private Socket socket;//服务端的socket包含输出流和输入流用于接收客户端数据并返回输出给客户端
        private String host;//对应客户端的IP地址
        private String nickName;//客户端的昵称
        public ClientHandler(Socket socket){
             this.socket = socket;
             //通过socket获取客户端的ip地址信息
            host = socket.getInetAddress().getHostAddress();
        }
         public void run(){
            try {
                 //Socket类中的方法: InputStream getInputStream()
                //通过该Socket对象获取的输入流(可以读取对应客户端发送过来的字节数据)
                InputStream in = socket.getInputStream();
                 InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
                 BufferedReader br = new BufferedReader(isr);//字节输入流封装为缓冲字符输入流
                //读取一行字符串(是对应客户端发送过来的昵称)
                nickName = br.readLine();
                 String message;
                 //服务端调用br.readLine()读取客户端发送过来的一行字符串
                //  读取时如果客户端异常断开连接了,则可能出现异常:
//java.net.SocketException: Connection reset
                //  (原因:没有进行四次挥手)
                while ((message = br.readLine()) != null) {
                     System.out.println(nickName + "[" + host + "]说:" + message);
                }
            } catch (IOException e) {
                e.printStackTrace();
                 System.err.println(e.getMessage());
            } finally {
                 //处理客户端断开连接后的操作
                System.out.println(nickName+"下线了");
                 try {
                     //3.将socket关闭,释放资源
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
2.3.启动线程:
用线程池运行此线程中的任务:

运行效果:

By zhaoyq 2024-05-31