zoukankan      html  css  js  c++  java
  • 文本聊天室(TCP-下)

    ///-------------------------------------------------------------------------------------------------------------------------------------//

    如果有人真的对GUI感兴趣的话emmmm, 我推荐你们去下载SceneBuilder琢磨一下这个,这个针对javafx的,可以加入样式表....emmm,感觉还是挺有意思的。

    1.今天看到一个有意思的API,可以插入HTMl,  赞一下..呵呵

      你要先写好HTML然后直接复制到代码里,,,,

     1 package jffx.blogs.gui;
     2 
     3 import javafx.application.Application;
     4 import javafx.scene.Group;
     5 import javafx.scene.Scene;
     6 import javafx.scene.control.ScrollPane;
     7 import javafx.scene.layout.VBox;
     8 import javafx.scene.web.WebEngine;
     9 import javafx.scene.web.WebView;
    10 import javafx.stage.Stage;
    11 
    12 
    13 public class MenuTest2 extends Application {
    14     @Override
    15     public void start(Stage primaryStage) {
    16         primaryStage.setWidth(300) ;
    17         primaryStage.setHeight(400) ;
    18         Scene scene = new Scene(new Group()) ;
    19 
    20         VBox vbox = new VBox() ;
    21 
    22         final WebView webView = new WebView() ;
    23         final WebEngine webEngine = webView.getEngine() ;
    24 
    25         ScrollPane scrollPane = new ScrollPane() ;
    26         scrollPane.setContent(webView) ;
    27         webEngine.loadContent("<!DOCTYPE html>
    " +
    28                 "<html>
    " +
    29                 "<head>
    " +
    30                 "	<meta charset="utf-8">
    " +
    31                 "	<title></title>
    " +
    32                 "</head>
    " +
    33                 "<body>
    " +
    34                 "	<!-- ==== 开始页面级页眉 ==== -->
    " +
    35                 "	<header role="banner">
    " +
    36                 "		<!-- 站点标识 -->
    " +
    37                 "		<nav role="navigation">
    " +
    38                 "			<ul>
    " +
    39                 "				<li><a href="http://www.baidu.com">www.baidu.com</a></li>
    " +
    40                 "				<li><a href="http://www.google.com.cn">www.google.com</a></li>
    " +
    41                 "			</ul>
    " +
    42                 "		</nav>
    " +
    43                 "	</header>
    " +
    44                 "
    " +
    45                 "	<!-- 开始主要内容 -->
    " +
    46                 "	<main role="main">
    " +
    47                 "		<h1>Arts & Entertainment; Museums</h1>
    " +
    48                 "
    " +
    49                 "		<article>
    " +
    50                 "			<h2>Gallery</h2>
    " +
    51                 "			<img src="/home/jff/图片/1.jpeg" width="300" height="175" alt="Blue Flax"> 	
    " +
    52                 "			<p>ajishudasssssssssssssssssssssssssssssssssssssssssassssssssssssssssssssss
    " +
    53                 "				asddddddddddddddddddddddddddddddddddddddddddddddaasdddddddd
    " +
    54                 "				asdddddddddddddddddddddddddddddddddaasdddddddddddddddd</p>
    " +
    55                 "		</article>
    " +
    56                 "
    " +
    57                 "		<aside>
    " +
    58                 "			<h2>Subject</h2>
    " +
    59                 "			<ul>
    " +
    60                 "				<li><a href="http://www.baidu.com">baidu</a></li>
    " +
    61                 "			</ul>
    " +
    62                 "		</aside>
    " +
    63                 "	</main>
    " +
    64                 "
    " +
    65                 "	<!-- 开始附注栏 -->
    " +
    66                 "	<aside>
    " +
    67                 "		<!-- 次级导航 -->
    " +
    68                 "	</aside>
    " +
    69                 "
    " +
    70                 "	<footer role="contentinfo">
    " +
    71                 "		<p><small>We are young</small></p>
    " +
    72                 "	</footer>
    " +
    73                 "</body>
    " +
    74                 "</html>") ;
    75 
    76         vbox.getChildren().add(scrollPane) ;
    77         scene.setRoot(vbox) ;
    78 
    79         primaryStage.setScene(scene) ;
    80         primaryStage.setTitle("-----") ;
    81         primaryStage.show() ;
    82 
    83     }
    84 }

    这次实现了基本功能,不过呢,对于客户端的代码有点要求,就是有点差强人意...

    下面给完整代码

    服务器代码:

      

     1 package jffx.blogs.net;
     2 
     3 import java.io.*;
     4 import java.net.*;
     5 import java.util.*;
     6 
     7 /**
     8  * 代码文件:    TalkRoomServer.java
     9  * 功能描述:    管理服务器与客户端的活动连接
    10  */
    11 public class TalkRoomServer {
    12     public static void main(String[] args) {
    13         try {
    14             //服务器端serversocket, 绑定端口(5210), 随意选(1024后的)
    15             ServerSocket server = new ServerSocket(5210);
    16 
    17             /**
    18              * 容器来保存服务器与客户端的连接, 键为姓名,值为socket
    19              */
    20             Map<String, Socket> socketMap = new HashMap<>() ;
    21 
    22             while(true) {
    23                 //监听客户端的连接, accept的方式是阻塞的
    24                 try {
    25                     Socket ss = server.accept();
    26 
    27                     //创建流
    28                     //采用缓冲流,提高效率
    29                     DataInputStream in = new DataInputStream(
    30                             new BufferedInputStream(ss.getInputStream())
    31                     ) ;
    32                     DataOutputStream out = new DataOutputStream(
    33                             new BufferedOutputStream(ss.getOutputStream())
    34                     ) ;
    35 
    36                     /**
    37                      * 在客户端设计时,一个新的用户登录到这个服务器上时,就向服务器发送其姓名
    38                      */
    39                     String name = in.readUTF() ;
    40                     //获取IP
    41                     String IP = ss.getInetAddress().toString() ;
    42                     //显示到服务器
    43                     System.out.println(name + " : " + IP);
    44 
    45                     //查看已监听的客户,并发送新用户登录消息
    46                     Collection<Socket> values = socketMap.values() ;
    47                     Iterator<Socket> iter = values.iterator() ;
    48                     while(iter.hasNext()) {
    49                         Socket temp = iter.next() ;
    50                         DataOutputStream write = new DataOutputStream(temp.getOutputStream()) ;
    51                         System.out.println(temp) ;
    52                         write.writeUTF("Add:" + name) ;
    53                         write.flush() ;
    54                     }
    55                     //将新用户添加到容器中
    56                     socketMap.put(name, ss) ;
    57 
    58 
    59                     /**
    60                      * 向新登录的用户发送都有谁在线
    61                      */
    62                     Set<String> names = socketMap.keySet() ;
    63                     Iterator<String> iterName = names.iterator() ;
    64                     while(iterName.hasNext()) {
    65                         String loginUser = iterName.next();
    66                         out.writeUTF("Add:" + loginUser) ;
    67                         out.flush() ;
    68                     }
    69 
    70 
    71                     /**
    72                      * 创建新线程转发用户给服务器发送的消息
    73                      *  由于需要分
    74                      *      向某个人发送消息,即:私聊
    75                      *      向所有发送消息,即:广播吧.
    76                      *  我们采用修改处理客户端的发送方式:
    77                      *      在消息的基础上,给前面即消息前缀加上一些表示发送目标的字符串.
    78                      *      具体看Talking.java的处理方式
    79                      */
    80                     //由于客户有可能下线,所以需要将姓名和容器都传递给线程类
    81                     new Thread(new Talking(name, ss, socketMap)).start() ;
    82 
    83                 } catch (Exception ex) {
    84                     ex.printStackTrace() ;
    85                 }
    86             }
    87         } catch (Exception ex) {
    88             ex.printStackTrace() ;
    89         }
    90     }
    91 }
    View Code
    package jffx.blogs.net;
    
    import java.io.*;
    import java.net.*;
    import java.util.*;
    
    /**
     * 代码文件:Talking.java
     * 功能描述:线程类转发用户的数据
     */
    public class Talking implements Runnable {
        String name ;
        Socket connecter ;
        Map<String, Socket> socketMap = null ;
        public Talking(String name, Socket socket, Map<String, Socket> socketMap) {
            this.name = name ;
            this.connecter = socket ;
            this.socketMap = socketMap ;
        }
    
        @Override
        public void run() {
            try {
                DataInputStream in = new DataInputStream(
                        new BufferedInputStream(connecter.getInputStream())
                ) ;
    
                while(true) {
                    String words = in.readUTF() ;
                    /**
                     * 我们约定,客户端发送("name@text")这种格式的消息
                     * 不过用户不需要处理,我们交由客户端程序在发送消息时自动加上
                     */
                    String [] tokens = words.split("@") ;
                    String sendName = tokens[0] ;
                    String text = tokens[1] ;
    
                    if("All".equals(sendName)) {
                        //容器的值为Socket变量
                        Collection<Socket> sockets = socketMap.values() ;
                        Iterator<Socket> iter = sockets.iterator() ;
                        while(iter.hasNext()) {
                            //创建流.并以固定格式写出
                            Socket sendSocket = iter.next() ;
                            DataOutputStream out = new DataOutputStream(sendSocket.getOutputStream()) ;
                            out.writeUTF("Text:" + text) ;
                            out.flush() ;
                        }
                    } else {        //私聊
                        Socket sendSocket = socketMap.get(sendName) ;
                        DataOutputStream out = new DataOutputStream(sendSocket.getOutputStream()) ;
                        out.writeUTF("Text:" + text) ;
                        out.flush() ;
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace() ;
            } finally {                     //当登陆的用户退出后,就会跳出while(true)
                try {
                    /**
                     * 查找登陆的用户,删除服务器与其的连接,并发送给所有的客户端
                     */
                    this.socketMap.remove(this.name) ;
                    Collection<Socket> sockets = this.socketMap.values() ;
                    Iterator<Socket> iter = sockets.iterator() ;
                    while(iter.hasNext()) {
                        Socket sender = iter.next() ;
                        DataOutputStream out = new DataOutputStream(sender.getOutputStream()) ;
                        out.writeUTF("Del:" + this.name) ;
                        out.flush() ;
                    }
                } catch (Exception ex) {
                    ex.printStackTrace() ;
                }
            }
        }
    }
    View Code

    客户端代码:

     

    package jffx.blogs.net;
    
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.ScrollPane;
    import javafx.scene.control.TextArea;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.*;
    import javafx.scene.text.Font;
    import javafx.scene.text.FontPosture;
    import javafx.scene.text.FontWeight;
    import javafx.scene.text.Text;
    import javafx.stage.Stage;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.net.Socket;
    import java.util.Date;
    
    
    //采用切换舞台的方式来切换登录界面和聊天界面
    /**
     * 代码文件:    TalkRoomServer.java
     * 功能描述:    客户端代码
     */
    public class TalkRoomClient extends Application {
        /**
         * 将两个组件放在一个类内,耦合性太高了,不过我还不会解耦和..
         */
    //=================GUI组件===============================
        private TextArea ta = new TextArea();                       //聊天记录框
        private TextField tf = new TextField();                     //信息发送框
        private ComboBox<String> userList = new ComboBox<>();       //在线用户下拉列表
    
    //===================网络组件============================
        String name ;                                               //用户名称
        Socket socket ;                                             //与服务器端连接的Socket
        DataInputStream in ;                                        //和服务器进行交互的流
        DataOutputStream out ;                                      //同上
    
    
        @Override
        public void start(Stage primaryStage) {
            /**
             * 画登录界面,采用GridPane
             */
            GridPane mainPane = new GridPane() ;
    
            //设置面板及布局
            mainPane.setAlignment(Pos.CENTER) ;   //向中间靠齐
            mainPane.setHgap(10) ;      //节点的水平间距
            mainPane.setVgap(10) ;      //节点的垂直间距
            mainPane.setPadding(new Insets(5, 5, 5, 5)) ;
    
            //文本框
            Text text = new Text("Welcome") ;
            text.setFont(Font.font("CourierNew", FontWeight.BOLD, FontPosture.REGULAR, 23));
            mainPane.setId("welcome-text");
            mainPane.add(text, 0, 0, 2, 1) ;
    
            //标签加上输入的文本域
            Label serviceName = new Label("SericeIP: ") ;   //如果是在本机的话是127.0.0.1
            TextField tfForService = new TextField() ;
            mainPane.add(serviceName, 0, 1) ;
            mainPane.add(tfForService, 1, 1);
    
            //昵称加上输入的文本域
            Label inputName = new Label("Name: ") ;
            TextField tfForName = new TextField() ;
            mainPane.add(inputName, 0, 2) ;
            mainPane.add(tfForName, 1, 2) ;
    
            //按钮这行单独处理,用一个Hbox包装一下
            Button btForLogin = new Button("Login") ;
            HBox box = new HBox(10) ;
            box.getChildren().add(btForLogin) ;
            box.setAlignment(Pos.BOTTOM_RIGHT) ;        //靠向最右边
            //然后再加入主面板
            mainPane.add(box, 1, 4) ;
    
    
            //显示一下--将面板放入舞台,至于为什么清查阅相关资料,-----h
            Scene scene = new Scene(mainPane, 300, 200) ;
            primaryStage.setScene(scene) ;
            primaryStage.setTitle("Login") ;
            primaryStage.show() ;
    
            /**
             * 监听按钮事件,以更换舞台
             */
            //这里用lambda表达式,因为就算你写完整的继承事件接口即
            // EventHandle<ActionEvent>处理,也只是处理一个handle方法
            //所以还不如写lamda,因为handle()只有一个参数,所以event只是一个标识符
            // 代表只有一个参数.
            btForLogin.setOnAction(event -> {
                //登陆之后,给成员name初始化
                this.name = tfForName.getText() ;
                String hostName = tfForService.getText() ;
    
    
                /**
                 * 继续画需要切换的聊天界面图
                 */
                ta.setWrapText(true) ;      //自动换行
                Pane pane = new ChatPane(ta, tf, userList) ;        //CharPane在另一个文件
                Scene chatScene = new Scene(pane, 550, 400) ;
                primaryStage.setTitle("Chatting--" + this.name) ;
                primaryStage.setScene(chatScene) ;
                primaryStage.show() ;
    
                //连接服务器
                try {
                    this.socket = new Socket(hostName, 5210) ;
                    //初始化流
                    in = new DataInputStream(this.socket.getInputStream()) ;
                    out = new DataOutputStream(this.socket.getOutputStream()) ;
    
                    //向服务器发送用户登录的昵称
                    out.writeUTF(this.name) ;
                    out.flush() ;
                } catch (Exception ex) {
                    ex.printStackTrace() ;
                }
    
                //监听文本域的事件
                tf.setOnAction(e -> {
                    String words = tf.getText() ;
                    //重新将文本域滞空,显示一种消息发送的感觉
                    tf.setText("");
    
                    if(words.length() == 0) {
                        System.out.println("警告:不允许 消息为空!!") ;
                    } else {
                        //从ComboBox获取要发送的对象
                        String sendName = userList.getValue() ;
    
                        try {
                            //区分群聊和私聊
                            if (sendName.equals("All")) {
                                out.writeUTF(sendName + "@" + words);
                                out.flush() ;
                            } else {
                                ta.appendText(this.name + ":") ;
                                ta.appendText("	" + words + "
    ");
                                out.writeUTF(sendName + "@" + words) ;
                                out.flush() ;
                            }
                        } catch (Exception ex) {
                            ex.printStackTrace() ;
                        }
                    }
                });
                //发送看完了,就该到接收了,为提高效率,用一个线程处理
                new Thread(new GetMessage()).start() ;
    
            }) ;
        }
    
        /**
         * 类类型:     内部类
         *  功能描述:   接收服务器发来的数据
         */
        //解释一下,因为是内部类,所以外部类的私有数据内部类也可以访问,所以不需要接收
        //什么数据,当然如果你想写到另一个文件就得加入数据域了
        private class GetMessage implements Runnable {
            @Override
            public void run() {
                try {
                    while(true) {
                        //获取服务器发过来的数据
                        //格式为   Add:name, Text:msg Del:name
                        String text = in.readUTF() ;
                        String [] token = text.split(":") ;
                        String type = token[0] ;   //Add..
                        String msg = token[1] ;    //name..
    
                        //根据信息类型的不同做不同的处理
                        if("Add".equals(type)) {
                            System.out.println(msg) ;
                            //如果添加的不是自己,那么将其加入到下拉列表中
                            //name是客户端的姓名
                            if(!msg.equals(name)) {
                                userList.getItems().add(msg) ;
                            }
                            //在聊天记录框中显示
                            //因为是在另一个线程中所以调用
                            Platform.runLater(() -> {
                                ta.appendText(msg + " " + new Date() + "上线了!
    ");
                            });
                        }
                        if("Del".equals(type)) {
                            Platform.runLater(() -> {
                                userList.getItems().remove(msg);
                                ta.appendText(msg + " " + new Date() + "下线了!
    ");
                            });
                        }
                        if("Text".equals(type)) {
                            Platform.runLater(() -> {
                                ta.appendText(msg + "
    ") ;
                            }) ;
                        }
                    }
                } catch (Exception ex) {
                    ex.printStackTrace() ;
                }
            }
        }
    }
    View Code
    package jffx.blogs.net;
    
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.control.ComboBox;
    import javafx.scene.control.ScrollPane;
    import javafx.scene.control.TextArea;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.VBox;
    
    /**
     * 将聊天面板从主程序中抠出来, 可以以内部类方式实现,就不需接受这些参数了,更方便些,
     */
    class ChatPane extends VBox
    {
        private TextArea ta ;
        private ComboBox<String> userList ;
        private TextField tf ;
        public ChatPane(TextArea ta, TextField tf, ComboBox<String> userList) {
            this.ta = ta ;
            this.userList = userList ;
            this.tf = tf ;
            super.setSpacing(30);
    
            setPadding(new Insets(20, 20, 20, 20));
            //设置文本域的属性
            this.ta.setPrefColumnCount(70);
            this.ta.setEditable(false);      //不可编辑
            this.ta.setWrapText(true);       //自动换行
            getChildren().add(new ScrollPane(ta)) ;
    
            HBox hBox = new HBox(150) ;
            //加入在线用户及输入文本框
            //getItems()方法返回一个选项列表
            this.userList.getItems().add("All") ;     //默认给所有人
            this.userList.setStyle("-fx-color: White") ;
            this.userList.setValue("All") ;
            setAlignment(Pos.CENTER) ;
            this.tf.setPrefColumnCount(30);
            hBox.getChildren().addAll(this.userList, this.tf) ;
    
            getChildren().addAll(ta, hBox) ;
        }
    }
    View Code

    //-------------------------我是华丽的分割线-----------------------------

  • 相关阅读:
    spring管理hibernate,mybatis,一级缓存失效原因
    The constructor ClassPathXmlApplicationContext(String) refers to the missing type BeansException
    idea中Hibernate错误:无法解析表
    使用Dom4解析xml
    关于idea中新建web项目 webapp文件夹没有小蓝点 ,启动服务,访问不到解决方案
    解决VS编译太慢问题
    Entity FrameWork6 Code First带virtual关键字外键 Asp.Net WebApi无法返回实体类数据问题
    webapi请求返回{"$id":"1","Message":"请求的资源不支持 http 方法“GET”。"}
    WPF的BusyIndicator控件只显示遮罩层,不显示提示层问题
    [转]Mysql将数据分组后取出时间最近的数据
  • 原文地址:https://www.cnblogs.com/jffx/p/9819839.html
Copyright © 2011-2022 走看看