网络:QQ登路 私聊 群聊

  • QQ群:
  • 功能:私聊
  • 好友
  • 拍照
  • 拍视频
  • 视频通话
  • 发文件
  • QQ空间
  • 1.每个客户端有个名称
  • 2.私聊 向一个客户端发起私聊
  • 3.群聊
  • 4.发文件
  • 客户端只能像服务器端发送文件或字符
  • 服务器端只能得到客户端发过来的数据
  • 必须客户端和服务器端有一个规范
  • 客户端的需求可以再发送的字符里体现
实现功能:
  • 1.登陆 u+ 姓名 u+
  • 2.返回结果 成功1 失败-1
  • 3.私聊 p+ 姓名♥聊天内容p+
  • 4.群聊 a+ 聊天内容 a+
  • 5.发文件 f+
  • 6.发语音
定义规范
    //登陆
    String LOGIN_FLAG = "u+";
    //私聊
    String PRIVAIE_FLAG = "p+";
    //群聊
    String PUBLIC_FLAG = "a+";
    //分隔符
    String SPLIT_FLAG = "♥";
    //成功失败的状态
    String SUNCESS="1";
    String  FAILASE="-1";

定义用户管理

功能
  • 管理所有的登陆用户Map<String ,Socket>
  • 判断用户是否登陆
  • 保存登陆用户信息
  • 通过用户名找到对应的socket
  • 通过socket对象找到对应的名称
  • 遍历 获取所有人的socket对象
public class usermanager {
    //保存所有的用户信息
    private Map<String, Socket>users=new HashMap<>();
    /**
     * 判断用户是否登路
     */
    public boolean isLogined(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;
    }
    /**
     * 获取所有人的socket对象
     */
    public synchronized Collection<Socket> AllUsers(){
        return users.values();
    }
}

定义服务器端:

  • 保存每一个用户的姓名和Socket
  • 开启子线程 处理客户端数据
public class Server {
    //Map  键值对
    //用于保存每一个用户的姓名和Socket
    public static usermanager manager=new usermanager();
    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){

        }

    }
}

服务器端⼦线程
  • 处理每个客户端的输⼊输出
  • 定义socket 构造方法
  • 获取输入流对象 输出流对象
  • 实现功能登陆 私聊 群聊

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 {
          //获取输入流对象
           br=new BufferedReader(new InputStreamReader(socket.getInputStream()));

           //输出流
            ps=new PrintStream(socket.getOutputStream());
            String line=null;

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

        
子线程功能一 登陆 u+.....u+
  • 使用line.substring获取对象的名称
  • 判断这个用户是否登陆
  • 登陆过了
  • 发送结果给客户端 显示成功
  • 没有登陆
  • 保存当前用户的登陆信息 重新登陆
  if (line.startsWith(ChatClass.LOGIN_FLAG)&&line.endsWith(ChatClass.LOGIN_FLAG))
                {
//                  String []items=line.substring(2).split("u+");
//                  String   name=items[0];
                    //获取名字
                    String name=line.substring(2,line.length()-2);
                    //判断这个用户是否登陆
                    if (Server.manager.isLogined(name))
                    {
                        //登陆过了
                        //发送结果给客户端
                        ps.println(ChatClass.FAILASE);
                    }else {
                        //没有登陆
                        //保存当前用户的登陆信息
                        Server.manager.save(name,socket);
                        ps.println(ChatClass.SUNCESS);
                    }
                }
子线程功能二判断私聊 p+ 姓名♥聊天内容p+
  • 获取分割输入流的内容 名称和聊天内容
  • 找到当前用户名称

               else if(line.startsWith(ChatClass.PRIVAIE_FLAG)&&line.endsWith(ChatClass.PRIVAIE_FLAG))
                {
                    //私聊    p+ 姓名♥聊天内容p+
                    //获取信息  jack♥hellow
                    String msg = line.substring(2, line.length() - 2);
                    //分割  Jack hellow
                    String[] items = msg.split(ChatClass.SPLIT_FLAG);
                    //用户名
                    String name = items[0];
                    //聊天内容
                    String message = items[1];
                    //通过用户名找到对应的socket
                    Socket desSocket = Server.manager.socketByname(name);
                    PrintStream desps = new PrintStream(desSocket.getOutputStream());
                    //获取当前用户的名称
                    String currentName = Server.manager.nameBySocket(socket);
                    //发送私聊信息
                    desps.println(currentName + "向你发送私聊" + message);

                    //
                }
          
子线程功能三 群聊
  
                else {
                    //处理数据
                    String  msg=line.substring(2,line.length()-2);
                    //获取当前用户的名称
                    String currentName=Server.manager.nameBySocket(socket);
                    //遍历所有的用户信息
                    Collection<Socket> sockets=Server.manager.AllUsers();
                    for (Socket s:sockets)
                   {
                    PrintStream temps=new PrintStream(s.getOutputStream());
                    temps.println(currentName+"发来群聊"+msg);

                   }
                }
            }
        } 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("192.168.43.164",8989))
        {
            //发给服务器端的输出流
            ps=new PrintStream(socket.getOutputStream());
            //接受终端的输入流
            br=new BufferedReader(new InputStreamReader(System.in));
            //接受服务器端的输入流
            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=ChatClass.LOGIN_FLAG+line+ChatClass.LOGIN_FLAG;
                //发送个服务端
                ps.println(loginStr);

                //接收服务器返沪的结果
                String result=brServer.readLine();
                //判断登陆结果
                if (result.equals(ChatClass.SUNCESS))
                {
                    System.out.println("登陆已成功");
                    break;
                }else {
                    System.out.println("用户已存在  请重新输入");
                }
            }
             //登陆成功
            //开启线程处理服务器端的输入
            new ClientThread(socket).start();
           String line=null;

           while ((line =br.readLine())!=null)
           {
               ps.println(line);
           }
        }catch (IOException e){

        }

   }
}
定义客户端子线程
  • 获取服务器端的输出流 并输出
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();
            }

        }
    }
}

QQ功能.jpg

感悟:虽然这个程序敲的我一脸迷糊,不明所以,但是当程序成功运行时,心里是非常的高兴,敲三百多行代码的辛苦,换回了运行成功的几行代码,为的就是那一刻的成就感。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,653评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,321评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,833评论 0 324
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,472评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,306评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,274评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,658评论 3 385
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,335评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,638评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,697评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,454评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,311评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,699评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,986评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,254评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,647评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,847评论 2 335

推荐阅读更多精彩内容

  • 计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。 按照计算机网络的定义,通过一定...
    蛋炒饭_By阅读 1,203评论 0 10
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,068评论 1 32
  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,283评论 0 6
  • 一、简历准备 1、个人技能 (1)自定义控件、UI设计、常用动画特效 自定义控件 ①为什么要自定义控件? Andr...
    lucas777阅读 5,181评论 2 54
  • 6.1 公钥密钥加密原理 6.1.1 基础知识 密钥:一般就是一个字符串或数字,在加密或者解密时传递给加密/解密算...
    AndroidMaster阅读 3,980评论 1 8