美文网首页程序员
Java网络编程(2)

Java网络编程(2)

作者: iwen大大怪 | 来源:发表于2019-08-22 17:57 被阅读0次

    接着昨天的发文,在昨天的项目中,存在着许多的问题,今天就来将这些问题给解决了,同时会以本人思路来一步步的写代码:

    要实现的功能:

    /**
     * 1.每一个客户端一个名称
     * 2.向某一个客户端发起私聊
     * 3.群聊
     *
     * 客户端只能向服务器发文件或者字符
     * 服务器端只能得到客户端发来的数据
     * 必须客户端和服务器有一个规范
     * 客户端的需求可以在发送的字符里面体现
     *
     * 定义规范:
     * 1.登陆      u+ 姓名  u+
     * 2.返回结果  成功 1 ;失败 -1
     * 3.私聊      p+ 姓名♥内容  P+
     * 4.群聊      a+ 聊天内容  a+
     * 使用Map来保存信息:保存对应的名字信息和一个对应的socket,才能调用每个信息
     * Map<String,Socket>
     */
    

    思路:要实现群聊、私聊、并且给每个客户端都取好名字,首先第一步就是要创建几个类,分别是:服务器类(srever)客户端类(Client)用户管理类(userManage)

    由于本阶段没有界面操作,要如何判断客户端发起的是群聊还是私聊呢?为了解决这个问题,我们就需要来定义一套规范:
    1.当终端接收到以u+开头和结尾的语句时,如 u+ 内容 u+,要将此内容判定为d登陆,从而去执行登陆相对应的代码块;
    2.当终端接收到以a+开头和结尾的语句时,如 a+ 内容 a+,要将此内容判定为群聊,从而去执行群聊相对应的代码块;
    3.当终端接收到以p+开头和结尾的语句时,如 p+ 内容 p+,要将此内容判定为私聊,从而去执行私聊相对应的代码块;

    服务器端

    服务器使用多线程,主线程主要就是在不停的接收客户端链接,并且获取每个客户端的socket

    public class Server{
        //创建一个manage对象
        public static UserManage manage = new UserManage();
    
        //主线程只负责来监听客户端链接
        public static void main(String[] args) {
            //创建ServerSocket
            try (ServerSocket ss = new ServerSocket(8989)){
                //监听所有来链接的客户端
                while (true){
                    Socket socket = ss.accept();
    
                    //让子线程处理这个socket
                    new ServerThread(socket).start();
                }
            } catch (IOException e) {
    
            }
        }
    }
    

    服务器的子线程需要处理的是判断客户端的登陆状态,如果该用户已经存在,就提示用户重新输入,同时还要判断客户端是发起群聊还是私聊具体实现如下:

    class ServerThread extends Thread{
        private Socket socket;
    
        public ServerThread(Socket socket){
            this.socket = socket;
        }
        @Override
        public void run() {
            //登陆 接收客户端发过来的数据
    
            BufferedReader br = null;
            PrintStream ps = null;
            try {
                //1.获取对应的输入流,接收客户端输入数据
                br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    
                //2.获取对应的输出流,向客户端发送数据
                ps = new PrintStream(socket.getOutputStream());
                //2.接收数据
                String line;
                while ((line = br.readLine()) != null){
                    //登陆 U+...U+
                    if (line.startsWith(ChatProtocol.LOGIN_FLAG)&&line.endsWith(ChatProtocol.LOGIN_FLAG)){
    //                    String[] item = line.substring(2).split("u+");
    //                    String name = item[0];
                        //获取用户名
                        String name = line.substring(2,line.length()-2);
    
                        //调用manage方法,判断这个用户名是否已经登陆
                        if (Server.manage.isLonined(name)){
                            //已经登陆
                            //发送结果给客户端
                            ps.println(ChatProtocol.FAILURE);
    
                        }else {
                            //没有登陆
                            //调用save保存当前的登陆的用户信息
                            Server.manage.save(name, socket);
                            ps.println(ChatProtocol.SUCCESS);
                        }
                    }//判断是不是私聊
                    else if (line.startsWith(ChatProtocol.PRIVATE_FLAG)&&line.endsWith(ChatProtocol.PRIVATE_FLAG)){
                        //获取信息
                        String msg = line.substring(2,line.length()-2);
                        //int endIndex
                        //分割
                        String[] items = msg.split(ChatProtocol.STLIT_FLAG);
                        //用户
                        String name = items[0];
                        //聊天内容
                        String message = items[1];
                        //通过用户名找到对应的socket
                        Socket desSocket = Server.manage.socketByName(name);
    
                        PrintStream desPs = new PrintStream(desSocket.getOutputStream());
    
                        //获取当前用户名称
                        String currentName = Server.manage.nameBySocket(desSocket);
                        desPs.println(currentName+"向你发来私聊:"+message);
                    }else {
                        //群聊
                        //处理数据
                        String msg = line.substring(2,line.length()-2);
                        //获取当前用户名称
                        String currentName = Server.manage.nameBySocket(socket);
    
                        //遍历所有用户消息
                        Collection<Socket> sockets = Server.manage.allUsers();
                        for (Socket s: sockets){
                            //创建输出流
                            PrintStream tempps = new PrintStream(s.getOutputStream());
                            tempps.println(currentName+":"+msg);
                            tempps.close();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //私聊
            //群聊
        }
    }
    

    客户端

    客户端也采用多线程的模式,这是因为客户端需要接收服务器返回的消息,同时还要向服务器发送数据,多线程主要接收终端输入,发送给服务器端,具体实现如下:

    public class Client{
        public static void main(String[] args) {
            BufferedReader br = null;
            PrintStream ps = null;
            BufferedReader brServer = null;
            //链接服务器端
            try (Socket socket = new Socket("127.0.0.1",8989)){
                //登陆
                //接收终端的输入流
                br = new BufferedReader(new InputStreamReader(System.in));
                //发给服务器的输出流
                ps = new PrintStream(socket.getOutputStream());
                //接收服务器端的输入流
                brServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    
                while (true){
                    //接收终端输入信息
                    //BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                    //String line = br.readLine();
                    String line  = JOptionPane.showInputDialog("请输入用户名");
                    //拼接好发送
                    String loginStr = ChatProtocol.LOGIN_FLAG+line+ChatProtocol.LOGIN_FLAG;
    
                    ps.println(loginStr);
    
                    String result = brServer.readLine();
    
                    //判断结果
                    if (result.equals(ChatProtocol.SUCCESS)){
                        System.out.println("登陆成功!");
                        break;
                    }else {
                        System.out.println("用户名已经存在,请重新登陆.");
                    }
                }
                //登陆成功
                //开启子线程,处理服务器的输入
                new ClientThread(socket).start();
    
                //接收终端输入,发送给服务器端
                String line;
                while ((line = br.readLine()) != null){
                    //发送给服务器
                    ps.println(line);
                }
            } catch (IOException e) {
                System.out.println("网络出错,请检查服务器是否开启。");
            }
        }
    }
    

    子线程用来处理服务器返回的数据

    class ClientThread extends Thread{
        private Socket socket;
        public ClientThread(Socket socket){
            this.socket = socket;
        }
        @Override
        public void run() {
            BufferedReader br =null;
            try {
                br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                //接收服务器发过来的消息
                String line = null;
                while ((line = br.readLine()) != null){
                    System.out.println(line);
                }
            } catch (IOException e) {
                System.out.println("网络出错!");
            }finally {
                //释放资源
                try {
                    if (br != null){
                        br.close();
                    }
                    if (socket != null){
                        socket.close();
                    }
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    客户端管理

    package day14;
    
    import java.net.Socket;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    
    
    /**
     * 管理所有登陆的用户Map<String, Socket>
     * 方法:
     *     判断用户是否登陆
     */
    
    public class UserManage {
        /**
         * 定义一个Map来管理所有链接,保存每一个用户对应的姓名和Socket
         */
        private Map<String, Socket> users = new HashMap<>();
    
        /**
         * 判断用户是否登陆
         */
        public boolean isLonined(String name){
            //遍历数组
            for (String key:users.keySet()){
                if(key.equals(name)){
                    return true;
                }
            }
            return false;
        }
        /**
         * 保存用户信息
         */
        public void save(String name, Socket socket){
            users.put(name, socket);
        }
        /**
         * 通过用户名找到对应的socket
         */
        public Socket socketByName(String name){
    
            return users.get(name);
        }
        /**
         * 通过socket对象找到对应的名称
         */
        public String nameBySocket(Socket socket){
            for (String key: users.keySet()){
                //取出这个key对应的socket对象
                if (socket == users.get(key)){
                    return key;
                }
            }
            return null;
        }
        /**
         * 获取所有人的放回对象
         */
        public Collection<Socket> allUsers(){
            return users.values();
        }
    }
    

    接口类---存放定义的规范

    package day14;
    
    
    public interface ChatProtocol {
        //登陆
        String LOGIN_FLAG = "u+";
        //私聊
        String PRIVATE_FLAG = "p+";
        //群聊
        String PUBLIC_FLAG = "a+";
        //分割符
        String STLIT_FLAG = "♥";
        //成功状态
        String SUCCESS = "1";
        String FAILURE = "-1";
    }
    

    感悟

    一天下来,很累,但是很开心。一个项目看似简单,可是写了两天,这个过程中出现了许多bug,虽然找bug这个过程是痛苦的,但是还是学会了不少,继续努力吧。

    相关文章

      网友评论

        本文标题:Java网络编程(2)

        本文链接:https://www.haomeiwen.com/subject/oqxosctx.html