文本聊天室(TCP-下)

///-----------------------------------------------------------分享图片----不用客气-------------------------------------//


1.今天看到一个有意思的API,可以插入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>\n" +
28                 "<html>\n" +
29                 "<head>\n" +
30                 "\t<meta charset=\"utf-8\">\n" +
31                 "\t<title></title>\n" +
32                 "</head>\n" +
33                 "<body>\n" +
34                 "\t<!-- ==== 开始页面级页眉 ==== -->\n" +
35                 "\t<header role=\"banner\">\n" +
36                 "\t\t<!-- 站点标识 -->\n" +
37                 "\t\t<nav role=\"navigation\">\n" +
38                 "\t\t\t<ul>\n" +
39                 "\t\t\t\t<li><a href=\"http://www.baidu.com\">www.baidu.com</a></li>\n" +
40                 "\t\t\t\t<li><a href=\"http://www.google.com.cn\">www.google.com</a></li>\n" +
41                 "\t\t\t</ul>\n" +
42                 "\t\t</nav>\n" +
43                 "\t</header>\n" +
44                 "\n" +
45                 "\t<!-- 开始主要内容 -->\n" +
46                 "\t<main role=\"main\">\n" +
47                 "\t\t<h1>Arts & Entertainment; Museums</h1>\n" +
48                 "\n" +
49                 "\t\t<article>\n" +
50                 "\t\t\t<h2>Gallery</h2>\n" +
51                 "\t\t\t<img src=\"/home/jff/图片/1.jpeg\" width=\"300\" height=\"175\" alt=\"Blue Flax\"> \t\n" +
52                 "\t\t\t<p>ajishudasssssssssssssssssssssssssssssssssssssssssassssssssssssssssssssss\n" +
53                 "\t\t\t\tasddddddddddddddddddddddddddddddddddddddddddddddaasdddddddd\n" +
54                 "\t\t\t\tasdddddddddddddddddddddddddddddddddaasdddddddddddddddd</p>\n" +
55                 "\t\t</article>\n" +
56                 "\n" +
57                 "\t\t<aside>\n" +
58                 "\t\t\t<h2>Subject</h2>\n" +
59                 "\t\t\t<ul>\n" +
60                 "\t\t\t\t<li><a href=\"http://www.baidu.com\">baidu</a></li>\n" +
61                 "\t\t\t</ul>\n" +
62                 "\t\t</aside>\n" +
63                 "\t</main>\n" +
64                 "\n" +
65                 "\t<!-- 开始附注栏 -->\n" +
66                 "\t<aside>\n" +
67                 "\t\t<!-- 次级导航 -->\n" +
68                 "\t</aside>\n" +
69                 "\n" +
70                 "\t<footer role=\"contentinfo\">\n" +
71                 "\t\t<p><small>We are young</small></p>\n" +
72                 "\t</footer>\n" +
73                 "</body>\n" +
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 }

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

  得在发送的消息中加上自己的姓名(GUI标题),不然不知道是谁发的,

  这说明设计时,就有问题,哎--------------------不谈啦....'

下面给完整代码

服务器代码:

  

 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("\t" + words + "\n");
                            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() + "上线了!\n");
                        });
                    }
                    if("Del".equals(type)) {
                        Platform.runLater(() -> {
                            userList.getItems().remove(msg);
                            ta.appendText(msg + " " + new Date() + "下线了!\n");
                        });
                    }
                    if("Text".equals(type)) {
                        Platform.runLater(() -> {
                            ta.appendText(msg + "\n") ;
                        }) ;
                    }
                }
            } 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

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

猜你喜欢

转载自www.cnblogs.com/jffx/p/9819839.html