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

使用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:在实现上述功能的基础上可以进行功能增加,添加客户端用户姓名,和改进让用户进行私聊。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值