Java语言的学习与实践13 (网络编程 实现群聊和私聊(带上昵称))

1.收获

今天的课没有学新的知识,是接着昨天的demo做的,在昨天的的demo的基础上来实现实现群聊和私聊(带上昵称),这样就更加接近于我们生活中常用的 QQ,只是没有传递文件的功能,这个demo也更加贴近于我们的生活。这也体现了我们写的东西是有利于生活,使生活更加方便。知识改变生活,但是知识更能改变命运,知识会改变一个人思想和行为,使人变得文明。尽管自己的在课上没有听得太懂,但是自己还是能够去花些时间去理解,而不是得过且过,迷迷糊糊,只有这样才能达到自己当初想要的。

2.技术

(1)集合Map
(2)接口
(3)多线程
(4)Socket通信模式

3.技术的实际应用和实践

(1)集合Map
在这个demo中由于我们的昵称和Socket是两个不同的部分,但是我们有一种需求就要他们变成一一对应关系,但是他们并不是同一种类型的数据,将昵称和发送的内容放在一个集合里面Map<String,Socket>即可形成一一对应关系,当我们需要取出对应的东西我们可以利用Map里面的函数进行。

image.png

在我们这个demo中的应用

//保存所有用户信息(集合)
    private Map<String, Socket>users=new HashMap<>();

(2)接口
我们在聊天时,群聊就是群聊,私聊就是私聊,当用户选择群聊时,就应该按照群聊的方式来发送消息,当选择私聊的时候就应该按照私聊的方式来发送消息,这种方式就相当于是一种规定,我们可以用接口来实现。
在我们这个demo中的应用

public interface Chat {
    //登陆
    String LOGIN="u+";
    //私聊
    String PRIVATE_FLAG="p+";
    //群聊
    String PUBLIC_FLAG="a+";

    //分隔符
    String SPLIT_FLAG="♥";

    //成功的状态
    String  SUCCESS="1";
    String FAILURE="-1";
}

(3)多线程

在处理接收和发送消息的时候怎末么处理两者的先后顺序
由于两者的先后顺序不是清楚的,所以就可以利用多线程来解决这个问题,可以把发消息当做主线程,也可当作子线程,接收消息也是一样的。由于在服务器端和客服端也要接收和发送消息,所以两者都要用多线程。
在我们这个demo中的应用(只是客户端,服务器端也是一样的,在这就不举出来了)

public class Client {
    public static void main(String[] args) {
        BufferedReader br=null;
        PrintStream ps;
        BufferedReader bufferedReader;
        try {
            Socket socket = new Socket("10.129.25.253", 8888);
           ///接收终端的输入流
            br=new BufferedReader(new InputStreamReader(System.in));
            //发送给服务器端
            ps=new PrintStream(socket.getOutputStream());
            //接收服务器端的返回的结果
            bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));

            //登陆
            while (true){
                //接收终端输入信息
                String line=JOptionPane.showInputDialog("请输入用户名:");
                String loginStr=Chat.LOGIN+line+Chat.LOGIN;
                ps.println(loginStr);
                 //接收服务器端的返回的结果
                String result=bufferedReader.readLine();
                //判断登陆结果
                if(result.equals(Chat.SUCCESS)){
                    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();
            }
        }
        super.run();
    }
}

我们这里将接收服务器传来的消息作为子线程,
(4)Socket通信模式
由于该项目是建立在网络编程的基础上的所以要用到Socket通信模式

QQ图片20190822162500.jpg

首先我们要按照这个模式创建一个Server服务器

public class Server {
    //用于保存每一个用户对应的姓名和Socket(也可以用单例设计模式)
    public static UserManager manager=new UserManager();
    public static void main(String[] args) {
        try(ServerSocket serverSocket=new ServerSocket(8888)){
            //监听所有的来连接的客服端
            while(true){
                Socket socket=serverSocket.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 bufferedReader=null;
        PrintStream ps=null;
        try {
            //登陆
            //1.得到对应的客户端传来的输入流
            bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //得到对应输出流
            ps=new PrintStream(socket.getOutputStream());
            String line=null;
            while((line=bufferedReader.readLine())!=null){
                  if(line.startsWith(Chat.LOGIN)&&line.endsWith(Chat.LOGIN)){
                      //获取名字
                     String name=line.substring(2,line.length()-2);

判断用户的操作


                     //判断这个用户是否已经登陆
                      if(Server.manager.islogin(name)==true){
                          ps.println(Chat.FAILURE);
                      }else{
                          //没有登陆
                          //保存当前的用户信息
                          ps.println(Chat.SUCCESS);
                          Server.manager.save(name,socket);
                      }
                  }

                  //判断是是否为私聊
                  else if(line.startsWith(Chat.PRIVATE_FLAG)&&line.endsWith(Chat.PRIVATE_FLAG)){
                      //获取信息
                      String msg=line.substring(2,line.length()-2);
                      //分割
                      String[] items=msg.split(Chat.SPLIT_FLAG);
                      //用户名
                      String name=items[0];
                      //聊天内容
                      String message=items[1];
                      //通过用户名找到对应的socket
                      Socket socket=Server.manager.socketbyname(name);
                      PrintStream printStream=new PrintStream(socket.getOutputStream());
                      //获取当前用户的名称
                      String currentName=Server.manager.namebysocket(socket);
                      printStream.println(currentName+"向你发来私聊:"+message);
                  }else if(line.startsWith(Chat.PUBLIC_FLAG)&&line.endsWith(Chat.PUBLIC_FLAG)){
                      //群聊
                      //处理数据
                      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 tempps=new PrintStream(s.getOutputStream());
                          tempps.println(currentName+"发来的群聊:"+msg);
                          //tempps.close();
                      }
                  }else{
                      PrintStream printStream=new PrintStream(socket.getOutputStream());
                      printStream.println("发送消息的格式有误 请重新输入:");
                  }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        super.run();
    }
}

这样我们服务器就创建好了,自需要我们来创建几个客服端就可以实现群聊了,但是要注意的是必须先运行服务器端,然后再是客服端。

class Client1 {
    public static void main(String[] args) {
        BufferedReader br=null;
        PrintStream ps;
        BufferedReader bufferedReader;
        try {
            Socket socket = new Socket("10.129.25.253", 8888);
            //接收终端的输入流
            br=new BufferedReader(new InputStreamReader(System.in));
            //发送给服务器端
            ps=new PrintStream(socket.getOutputStream());
            //接收服务器端的返回的结果
            bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));

            //登陆
            while (true){
                //接收终端输入

                String line=JOptionPane.showInputDialog("请输入用户名:");
                String loginStr=Chat.LOGIN+line+Chat.LOGIN;
                ps.println(loginStr);

                String result=bufferedReader.readLine();
                //判断登陆结果
                if(result.equals(Chat.SUCCESS)){
                    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 Client2 {
    public static void main(String[] args) {
        BufferedReader br=null;
        PrintStream ps=null;
        BufferedReader bufferedReader=null;
        try {
            Socket socket = new Socket("10.129.25.253", 8888);
            //接收终端的输入流
            br=new BufferedReader(new InputStreamReader(System.in));
            //发送给服务器端
            ps=new PrintStream(socket.getOutputStream());
            //接收服务器端的返回的结果
            bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));

            //登陆
            while (true){
                //接收终端输入
                String line=JOptionPane.showInputDialog("请输入用户名:");
                String loginStr=Chat.LOGIN+line+Chat.LOGIN;
                ps.println(loginStr);
                //接收服务器端
                String result=bufferedReader.readLine();
                //判断登陆结果
                if(result.equals(Chat.SUCCESS)){
                    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){

        }
    }
}

实现效果:
提示用户输入

image.png

群聊
image.png

私聊
image.png

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

推荐阅读更多精彩内容

  • 6.1 公钥密钥加密原理 6.1.1 基础知识 密钥:一般就是一个字符串或数字,在加密或者解密时传递给加密/解密算...
    AndroidMaster阅读 4,011评论 1 8
  • 计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。 按照计算机网络的定义,通过一定...
    蛋炒饭_By阅读 1,220评论 0 10
  • 一、简历准备 1、个人技能 (1)自定义控件、UI设计、常用动画特效 自定义控件 ①为什么要自定义控件? Andr...
    lucas777阅读 5,200评论 2 54
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.h...
    eddy_wiki阅读 2,110评论 0 14
  • 一、Socket编程 (一)什么是Socket编程 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向...
    颵麏阅读 110评论 0 2