Socket(二)基于InputStream和OutputStream实现多人聊天功能

来源:互联网 发布:淘宝助理导出订单 编辑:程序博客网 时间:2024/06/10 21:32

使用Socket进行简单的聊天功能

    //在初步了解了Socket的基础知识,了解了Socket的传输之后,简单写一个手机用Socket进行聊天功能的Demo    //1.先写一个server和client进行交互的例子    public class Server {        public static void main(String[] args) throws IOException {            ServerSocket server = new ServerSocket(9999);            //设置超时时间,过了时间会自动关闭            server.setSoTimeout(60000*10);            Socket client = server.accept();            while(true){                //输入流                DataInputStream dis = new DataInputStream(client.getInputStream());                String a = dis.readUTF();                //输出流                DataOutputStream dos = new DataOutputStream(client.getOutputStream());                dos.writeUTF("服务器--》:"+a);                dos.flush();            }        }    }    public class Client {        public static void main(String[] args) throws IOException {            Socket client = new Socket("localhost", 9999);            //从键盘写入            BufferedReader b = new BufferedReader(new InputStreamReader(System.in));            DataOutputStream dos = new DataOutputStream(client.getOutputStream());            DataInputStream dis = new DataInputStream(client.getInputStream());            while(true){                String re = b.readLine();                //输出流                dos.writeUTF(re);                dos.flush();                //输入流                String a = dis.readUTF();                System.out.println(a);            }        }    }

server端使用死循环使客户端可以一直访问。
先运行server再运行client,在控制台输入:

这里写图片描述
得到这样表示已经成功进行了,server和client的通信。(使用流的方式)

为了是程序更加适用,改进client端进行代码分离和多线程使用:

    public class Client {        public static void main(String[] args) throws IOException {            Socket client = new Socket("localhost", 9999);            //启动发送线程            new Thread(new Send(client)).start();            //启动接收线程            new Thread(new Receive(client)).start();        }    }

提出client的发送和接收方法成单独的目的是让输入和输出流相互独立
使用BufferReader用来进行从键盘的写入(注:BufferReader的readLine()方法是阻塞的,进行到readLine()方法会阻塞,直到接收到键盘输入的数据才会继续运行):

发送类:

     /*        注(Client的Send类思路介绍):        新建一个发送的线程。        把Client类中的BufferReader和DataOutputStream提出到send类中        在无惨构造进行BufferReader的初始化        使用有参构造调用BufferReader并初始化DataOutputStream        在线程的run方法里面进行判断,构造输出流是否成功,是成功进行发送操作(提出来的send())        发送操作(send())分两步:            1.从键盘接收数据(getMesforConsole()方法)            2.发送数据      */    public class Send implements Runnable{        //键盘写入流        private BufferedReader buff;        //管道输出流        private DataOutputStream dos;        //控制线程的标识        private boolean isRunning = true;        //构造器里初始化        public Send() {            buff = new BufferedReader(new InputStreamReader(System.in));        }        //把管道传进来        public Send(Socket client){            this();            try {                dos = new DataOutputStream(client.getOutputStream());            } catch (IOException e) {        //          e.printStackTrace();                isRunning = false;                CloseUtil.closeAll(dos,buff);            }        }        //从控制台接收数据        public  String getMesforConsole(){            try {                return buff.readLine();            } catch (IOException e) {        //          e.printStackTrace();            }            return "";        }        //发送信息        public void send(){            String console = getMesforConsole();            try {                if(null != console && console != ""){                    dos.writeUTF(console);                    dos.flush();                }            } catch (IOException e) {        //              e.printStackTrace();                isRunning = false;                CloseUtil.closeAll(dos,buff);            }        }        @Override        public void run() {            while(isRunning){                send();        }    }

接收类:

    /*        注(Client的Send类思路介绍):        构建接受类run()里面调用接收方法,输出接收到的数据     */    public class Receive implements Runnable{        private DataInputStream dis;        private boolean isRunning = true;        public Receive() {        }        public Receive(Socket client){            this();            try {                dis = new DataInputStream(client.getInputStream());            } catch (IOException e) {        //          e.printStackTrace();                isRunning = false;                CloseUtil.closeAll(dis);            }        }        /**         * 接收数据         * @return         */        public String receive(){            String receive = "";            try {                receive = dis.readUTF();            } catch (IOException e) {        //          e.printStackTrace();                isRunning = false;                CloseUtil.closeAll(dis);            }            return receive;        }        @Override        public void run() {            while(isRunning){                System.out.println(receive());            }        }    }

关闭流的工具类CloseUtil:

    /**     * 关闭所有流,工具类,可关闭多个(传进来多个以“,”分割)     * @author Administrator     *     */    public class CloseUtil {        public static void closeAll(Closeable... io){            for(Closeable temp:io){                try {                    if(null != temp){                        temp.close();                    }                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }

这个时候只能单个client进行访问,改进server端(支持多客户端访问):

    /*        注(Server思路介绍):        构建private List<MyChannel> list = new ArrayList<MyChannel>();        进行存储所有的管道,也就是客户端的连接每个连接可以称为一个管道        使用list统一管理容器        提出send和reciive方法还有发送给他人的sendOthers(发送给所有人)     */    public class Server {        private List<MyChannel> list = new ArrayList<MyChannel>();        public static void main(String[] args) throws IOException {            //设置超时时间,过了时间会自动关闭        //      server.setSoTimeout(60000*10);            new Server().start();        }        public void start() throws IOException{            ServerSocket server = new ServerSocket(9999);            ExecutorService pool = Executors.newFixedThreadPool(2);            while(true){                //每隔进来会开启一个线程                Socket client = server.accept();                MyChannel myChannel = new MyChannel(client);                //将进来的管道都加进去.加入到容器统一管理                list.add(myChannel);//统一管理        //          new Thread(myChannel).start();//一条道路                pool.execute(myChannel);            }        }        //写到内部类里面,便于访问私有信息        private class MyChannel implements Runnable{            private DataInputStream dis;            private DataOutputStream dos;            private boolean isRunning = true;            private String name;            //在有参构造初始化            public MyChannel(final Socket client){                try {                    dis = new DataInputStream(client.getInputStream());                    dos = new DataOutputStream(client.getOutputStream());                    this.name = dis.readUTF();                    send("欢迎进入聊天室");                    sendOthers(name+" 加入了聊天室",true);                } catch (IOException e) {        //              e.printStackTrace();                    isRunning = false;                    CloseUtil.closeAll(dis,dos);                }            }            /**             * 接收方法             */            public String receive(){                String msg = "";                try {                    msg = dis.readUTF();                } catch (IOException e) {        //              e.printStackTrace();                    isRunning = false;                    CloseUtil.closeAll(dis);                    list.remove(this);                }                return msg;            }            /**             * 发送给自己方法             */            public void send(String msg){                if(null == msg || msg.equals("")){                    return;                }                try {                    dos.writeUTF(msg);                    dos.flush();                } catch (IOException e) {        //              e.printStackTrace();                    isRunning = false;                    CloseUtil.closeAll(dos);                    list.remove(this);        //              sendOthers(name+" 离开了聊天室");                }            }            /**             * 发送给别人的             * @param msg             */            public void sendOthers(String msg,boolean sys){//true表示系统发的,false表示给别人发的                if(msg.startsWith("@") && msg.indexOf(":") > -1){                    String name = msg.substring(1, msg.indexOf(":"));                    String context = msg.substring(msg.indexOf(":") + 1);                    for(MyChannel other:list){                        if(other.name.equals(name)){                            other.send(this.name+"私聊你说:"+context);                        }                    }                }else{                    for(MyChannel other:list){                        if(other == this){                            continue;                        }                        if(sys){//系统提示信息                            other.send("系统信息 "+msg);                        }else{                            other.send(this.name+"对所有人说:"+msg);                        }                    }                }            }            @Override            public void run() {                while(isRunning){        //              send(receive());//只发自己                    //发送给别人                    sendOthers(receive(),false);                }            }        }    }

Tip:在实现上述功能的基础上可以进行功能增加,添加客户端用户姓名,和改进让用户进行私聊。

原创粉丝点击