暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Unit11:TCP通信、wechat小程序

amazingweiwei 2020-09-24
231


输出流共享到集合中,服务端遍历集合来广播输出流给客户端,来达到聊天室的功能.


课后作业:使用Mapk来实现@xxx 时,发送私聊内容给对方


Client

package chat;


import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.Socket;

import java.util.Scanner;


/**

 * 聊天室客户端

 * @author Administrator

 *

 */

publicclassClient {

    /*

     * java.net.Socket

     * 封装了TCP协议,使用它就可以基于TCP协议进行网络通讯

     * Socket是运行在客户端的

     */

    private Socket socket;

    /**

     * 构造方法,用来初始化客户端

     * 实例化Socket的时候需要传入两个参数:

     * 1.服务端地址:通过IP地址可以找到服务端的计算机.

     * 2.服务端端口:通过端口可以找到服务端计算机上的服务端应用程序

     * 实例化Socket的过程就是连接的过程,若远端计算机没有响应会抛出异常.

     */

    public Client() throws Exception{

        System.out.println("正在连接服务端...");

        socket =new Socket("localhost", 8088);

        System.out.println("已与服务端建立连接!");

    }

    /**

     * 启动客户端

     */

    publicvoid start(){

        try {

            Scanner scan =new Scanner(System.in);

           

            /*

             * 先要求用户输入一个昵称

             */

            String nickName =null;

            while(true){

                System.out.println("请输入昵称:");

                nickName = scan.nextLine();

                if(nickName.length() >0){

                    break;

                }

                System.out.println("输入有误!");

            }

            System.out.println("欢迎你,"+ nickName +"!开始聊天吧!");

           

            /*

             * Socket提供的方法:

             * OutputStream getOutputStream()

             * 获取一个字节输出流,通过该流写出的数据会被发送至远端计算机.

             */

            OutputStream out = socket.getOutputStream();

            OutputStreamWriter osw =new OutputStreamWriter(out, "UTF-8");  //转换流,指定字符集

            PrintWriter pw =new PrintWriter(osw, true);    //自动行刷新

           

            /*

             * 先将昵称发送给服务器

             */

            pw.println(nickName);

           

            /*

             * 启动读取服务端发送过来消息的线程

             */

            ServerHandler handler =new ServerHandler();

            Thread t =new Thread(handler);

            t.start();

           

            /*

             * 将字符串发送至服务端

             */

//          System.out.println("输入内容:");

            while(true){

                pw.println(scan.nextLine());

            }

           

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

   

    publicstaticvoid main(String[] args) {

        try {

            Client client =new Client();

            client.start();

        } catch (Exception e) {

            e.printStackTrace();

            System.out.println("客户端启动失败!");

        }

    }

   

    /**

     * 该线程用来读取服务端发送过来的消息并输出到客户端控制台

     * @author Administrator

     *

     */

    classServerHandlerimplements Runnable{

        publicvoid run(){

            try {

                InputStream is = socket.getInputStream();

                InputStreamReader isr =new InputStreamReader(is, "UTF-8");

                BufferedReader br =new BufferedReader(isr);

               

                String message =null;

                while((message = br.readLine()) !=null){

                    System.out.println(message);

                }

            } catch (Exception e) {

               

            }

        }

    }

}



Server

package chat;


import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.ArrayList;

import java.util.List;


/**

 * 聊天室服务端

 * @author Administrator

 *

 */

publicclassServer {

    /*

     * 运行在服务端的ServerSocket主要负责:

     * 1.向系统申请服务端口

     *  客户端就是通过这个端口与之连接

     * 2.监听申请的服务端口,当一个客户端通过该端口尝试连接时,

     *  ServerSocket会在服务端创建一个Socket与客户端建立连接.

     */

    private ServerSocket server;

   

    /*

     * 保存所有客户端输出流的集合

     */

    private List<< span="">PrintWriter> allOut;

   

    /**

     * 用来初始化服务端

     */

    public Server() throws Exception {

        /*

         * 初始化的同时申请服务端口

         */

        server =new ServerSocket(8088);

       

        allOut =new ArrayList<< span="">PrintWriter>();

    }

   

    /**

     * 将给定的输出流存入共享集合

     * @param out

     */

    privatesynchronizedvoid addOut(PrintWriter out){

        allOut.add(out);

    }

    /**

     * 将给定的输出流从共享集合中删除

     * @param out

     */

    privatesynchronizedvoid removeOut(PrintWriter out){

        allOut.remove(out);

    }

    /**

     * 将给定消息发送给所有客户端

     * @param message

     */

    privatesynchronizedvoid sendMessage(String message){

        for(PrintWriter out : allOut){

            out.println(message);

        }

    }

   

   

    /**

     * 服务端开始工作的方法

     */

    publicvoid start() {

        try {

            /*

             * ServrSocket的accept方法是一个阻塞方法,作用是监听服务端口,

             * 直到一个客户端连接并创建一个Socket,使用该Socket即可与刚连接的客户端进行交互.

             */

            while(true){

                System.out.println("等待客户端连接...");

                Socket socket = server.accept();

                System.out.println("一个客户端连接了!");

                /*

                 * 启动一个线程,来完成与该客户端的交互

                 */

                ClientHandler handler =new ClientHandler(socket);

                Thread t =new Thread(handler);

                t.start();

            }

           

           

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

   

    publicstaticvoid main(String[] args) {

        try {

            Server server =new Server();

            server.start();

        } catch (Exception e) {

            e.printStackTrace();

            System.out.println("服务端启动失败!");

        }

    }

   

    /*

     * 该线程负责处理一个客户端的交互

     */

    classClientHandlerimplements Runnable{

        /*

         * 该线程处理的客户端的Socket

         */

        private Socket socket;

       

        /*

         * 客户端该用户的昵称

         */

        privateString nickName;

       

        //客户端的地址信息

        privateString host;


        public ClientHandler(Socket socket){

            this.socket = socket;

            /*

             * 通过Socket可以获取远端计算机的地址信息.

             */

            InetAddress address = socket.getInetAddress();

            //获取IP地址

            host = address.getHostAddress();

        }

       

        publicvoid run(){

            PrintWriter pw =null;

            try {

               

                /*

                 * Socket提供的方法

                 * InputStream getInputStream()

                 * 该方法可以获取一个输入流,从该流读取的数据就是从远端计算机发送过来的

                 */

                InputStream in = socket.getInputStream();

                InputStreamReader isr =new InputStreamReader(in, "UTF-8");

                BufferedReader br =new BufferedReader(isr);

               

                /*

                 * 首先读取一行字符串为昵称

                 */

                nickName = br.readLine();

                sendMessage(nickName +"上线了");

               

                /*

                 * 通过Socket创建输出流用于将消息发送给客户端

                 */

                OutputStream os = socket.getOutputStream();

                OutputStreamWriter osw =new OutputStreamWriter(os, "UTF-8");

                pw =new PrintWriter(osw, true);

               

                /*

                 * 将该客户端的输出流存入到共享集合中

                 */

                addOut(pw);

               

                String message =null;

                /*

                 * br.readLine() 在读取客户端发送过来的消息时,由于客户端断线,而其操作系统的不同,这里读取后的结果不同:

                 * 当windows的客户端断开时:br.readLine会抛出一场

                 * 当linux的客户端断开时:br.readLine会返回null

                 */

                while((message = br.readLine()) !=null){   //readLine() 读到文件末尾会返回null

//                  System.out.println(host + "说:" + message);

//                  pw.println(host + "说:" + message); 将客户端发送过来的消息返回给客户端,客户端去读Socket

                    //广播消息

                    sendMessage(nickName +"说:"+ message);

                }

               

            }catch (Exception e) {

                e.printStackTrace();

            } finally{

                /*

                 * 处理当前客户端断开后的逻辑

                 */

                //将该客户端的输出流从共享集合中删除

                removeOut(pw);

               

                sendMessage(nickName +"下线了!");

               

                try {

                    socket.close(); //Socket断开连接时,会自动关闭流

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }

    }


}


文章转载自amazingweiwei,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论