当前位置: 首页 > news >正文

Java网络编程:UDP套接字程序设计,UDP实现Socket通信(附完整代码实现)

Java UDP套接字程序设计

UDP特点:

(1)UDP有别于TCP,有自己独立的套接字(IP+PORT),它们的端口号不冲突。和TCP编程相比,UDP在使用前不需要进行连接,没有流的概念。如果说TCP协议通信与电话通信类似,那么UDP通信就与邮件通信类似:不需要实时连接,只需要目的地址;

(2)UDP 通信前不需要建立连接,只要知道地址(ip地址和端口号)就可以给对方发送信息;

(3)基于用户数据报文(包)读写;

(4)UDP通信一般用于线路质量好的环境,如局域网内,如果是互联网,往往应用于对数据完整性不是过于苛刻的场合,例如语音传送等。

几个关键的Java类:DatagramSocket,DatagramPacket,MulticastSocket。

一、程序设计第一步:创建UDPClient.java程序

UDPClient.java源程序见附录。

UDP通信,一般都是客户端必须先向被动等待连接的服务器端发UDP包,客户端不发UDP包,服务器端就根本不知道客户端的地址和端口号。一个典型的UDP客户端主要执行以下三步:

(1)创建一个DatagramSocket实例,可以选择对本地地址和端口号进行设置,但一般不用指定,程序将自动选择本地地址和可用的端口;

(2)使用DatagramSocket类来发送和接收DatagramPacket类的实例,进行通信;

(3)通信完成后,使用DatagramSocket类的close()销毁该套接字。

与Socket类不同,使用DatagramSocket实例在创建的时候并不需要指定目的地址,这也是TCP协议和UDP协议最大的不同点之一。

1. UDP套接字类: DatagramSocket

不像TCP通信有客户套接字Socket和服务器套接字ServerSocket两种,而UDP套接字只有一种DatagramSocket,不区分客户套接字和服务器套接字。

(1)UDP套接字创建:

DatagramSocket datagramSocket = new DatagramSocket();

正如前面所述,不需要指定本地的地址和端口号。

(2)UDP套接字的几个重要方法:

发送网络数据用:datagramSocket.send(packet); //发送一个数据包到由IP和端口号指定的地址。

接收网络数据用:datagramSocket.receive(packet);//接收一个数据包,没有数据,则程序会在这里阻塞。

(其中packet属于 DatagramPacket报文类的一个实例对象,用户数据封装在packet中)

指定超时:datagramSocket.setSoTimeout(n),n是个整数,表示毫秒数。用来指定上面的receive()方法的最长阻塞时间。超过这个时长还receive方法还没得到响应,就会抛出InterruptedIOException异常,如果你的客户端设置为一个send,然后等待一个receive,可以用这个方法来设置超时,避免程序无限等待;但如果你的客户端类似TCP的设计方式,开启一个新线程来专门receive信息,那就不要用这个方法,否则在等待的过程中就超时报错了。

2. UDP数据报文类:DatagramPacket

TCP发送数据是基于字节流的,而UDP发送数据是基于报文DatagramPacket,网络中传递的UDP数据都要封装在这种自包含(self-contained)的报文中。

上面的UDP套接字创建的过程,会发现根本没有指定远程通信方的IP和端口,那么其send方法是发给谁呢,关键就在send方法的参数:DatagramPacket类型。数据报文除了要传输的信息外,每个数据报文实例中还附加了地址和端口信息,其具体含义取决了该数据报文是被发送还是被接收。若是要发送的数据报文,其实例中的地址则指明了目的地址和端口号;如果是要接收的数据报文,其实例中的地址则指明了所接收信息的源地址。

(1)UDP数据报文的创建:

DatagramPacket有多个构造方法,对于发送信息,需要明确远程的地址信息,采用下面的构造方法创建:

DatagramPacket(byte[ ] data, int length, InetAddress remoteAddr, int remotePort)

对于接收信息,其对象实例的创建则不用指定地址信息:

DatagramPacket(byte[ ] data, int length)

这样UDP套接字的send方法就能把报文发送到目的地址。上面的int length表示要读取的数据长度。byte[ ] data就是用于存储报文数据的字节数组缓存。

(2)UDP数据报文DatagramPacket的几个重要方法:

InetAddress getAddress( ):如果是发送的报文,返回的是目标主机的地址信息;如果是要接收的报文,返回的是发送该数据报文的主机地址信息;

int getPort( ):如果是发送的报文,返回的是目标主机的端口;如果是要接收的报文,返回的是发送该数据报文的主机端口;

注意:上面这两个方法主要是服务端常用,服务端可以通过这两个方法获知客户端的地址信息。

byte[ ] getData( ): 从报文中取数据,返回与数据报文相关联的字节数组;

二、程序设计第二步:创建UDPClientFX.java客户端窗体程序

用户界面如图6.1所示:

image-20201108112159116

图6.1 UDP客户端界面

(1)在“连接”按钮中实例化UDPClient对象,并启动接收信息的“新线程”;

(2)在“发送”按钮中设置发送信息的代码;

(3)在“退出”按钮中设置退出程序运行的代码;

(4)用客户端向测试服务器172.16.229.253:6006发送信息,测试客户端是否工作正常。

三、程序设计第三步:创建UDPServer.java程序

类似TCP服务器,UDP服务器的工作也是建立一个通信终端,并被动等待客户端发起连接。但由于UDP是无连接的,并没有TCP中建立连接的那一步。UDP通信通过客户端的数据报文初始化。典型的UDP服务器需要执行以下三步:

(1)创建一个UDP套接字DatagramSocket实例,但和客户端不同,需要指定一个本地端口(端口号范围在1024-65535之间选取),这样客户端才能发起初次通信):

DatagramSocket datagramSocket = new DatagramSocket( port );

此时,服务器就准备好了从任何客户端接收数据报文。UDP服务器默认是为所有的客户端使用同一个套接字,(这和TCP不同,TCP服务器为每个成功返回的accept方法都创建一个新的套接字;UDP套接字就像个邮箱,所有人的邮件都往这里发送);

(2)使用UDP套接字DatagramSocket实例的receive方法来接收一个UDP报文DatagramPacket实例。当receive方法返回的时候,数据报文就包含了客户端的地址信息,这样服务器就知道该消息来自哪里,回复消息该发送到什么地方;

(3)使用套接字DatagramSocket类的send和receive方法来发送和接收报文DatagramPacket的实例,进行通信。

注意:因为服务端经常需要循环多次调用receive方法接收消息,如果是用同一个报文实例来接收,那么就需要在下一个receive方法之前,先调用报文实例的setLength(缓存数组.length)方法。原因是报文实例包含一个内部消息的长度值,每一次receive收到报文,这个长度值就会修改为接收到的消息的实际长度值。例如这一次收到的消息是10字节,那么下一次receive到消息,超出10字节的内容都会被无提示的丢弃。所以就需要调用setLength方法来将内部消息长度值重置为构造报文实例时指定的缓存字节数组的长度。

UDPServer功能要求:

根据UDPClient.java程序,创建对应的UDPServer服务器程序。完成如下功能:用自己的UDPClientFX客户端发送信息到自己的UDPServer服务器,UDPServer接收信息并能自动将修改后的信息返回,具体来说,就是收到客户端的任意原始信息后,返回如下内容:你学号和姓名的信息头、时间信息及你服务器接收到的原始信息,各信息用“&”符号隔开成四部分,注意顺序一定不要错,如:

20180000001&程旭元& Wed Sep 02 10:43:12 CST 2020&原始信息

上面的时间信息是用new Date( ).toString( )来生成。

注意:与TCP不同,小负荷的UDP服务器往往不是多线程的。由于UDP是同一个套接字对应多个客户端,对于UDP服务端,可以不需要采用多线程方式(除非为了准备响应需要做大量耗费时间的工作,这种情况确实需要多线程,避免其他客户排队等待),而是直接使用一种顺序迭代方法就可以:服务器等待客户端请求,然后读取请求,处理请求,发回响应,接着循环往复。

五、扩展练习:组播程序设计

组播是指在一群用户范围内发送和接收信息,该信息具有共享性。UDP具有组播功能,而TCP不具有。

组播地址范围为224.0.0.0— 239.255.255.255。组播地址号唯一标示一群用户(一定网络范围内,仅限于局域网络内或有些自治系统内支持组播)。但有很多组播地址默认已经被占用,建议在225.0.0.0到238.255.255.255之间随机选择一个组播地址使用。(默认组播不能跨子网,除非设置了TTL值,且有配置组播路由器。另外同一个子网内如果也出现部分主机组播无效,可能是vmware的虚拟网卡影响,可先临时禁用这些命名为Vmnet的虚拟网卡,并关闭防火墙)

只要大家加入同一个组播地址,就能全体收取信息。在Java中,使用组播套接字MulticastSocket来组播数据,其是DatagramSocket的一个子类,使用方式也与DatagramSocket十分相似∶将数据放在DatagramPacket对象中,然后通过MulticastSocket收发DatagramPacket对象。

组播套接字类MulticastSocket及其几个重要的方法:

(1)路由器、交换机一般只转发和终端机一致IP地址和广播地址数据,终端机如何知道要接收组内信息?要先声明加入或退出某一组播组,其方法是:

MulticastSocket ms = new MulticastSocket(8900);

ms.joinGroup(groupIP); 该方法表示加入groupIP组, groupIP是InetAddress类型的组播地址。其作用是:告知自己的网络层该IP地址的包要收;转告上联的路由器这样的IP地址包要转发。

ms.leaveGroup(groupIP); 该方法表示退出groupIP组

(2)组内接收和发送信息的方法同UDP单播,也是以下两个方法,packet为DatagramPacket类型:

ms.send(packet);

ms.receive(packet);

(3)独立完成组播程序Multicast.java(源代码见附录)和窗体界面MulticastFX.java,组播套接字为: 225.0.0.1:8900,在组内发言要求以“From ip地址 学号 姓名:”为信息头。其效果如图6.3所示,要求每位同学都能看到组内其他同学的留言。

图6.3 组播对话界面

image-20201108112223223

六、扩展练习二:UDP局域网聊天程序

与TCP不同,UDP其实不真正区分服务端和客户端,一个程序其实可以身兼二职,尝试写一个不区分服务端和客户端UDP局域网聊天程序UDPChatFX.java。在同一局域网的机器运行该程序,可以互相发消息及发送广播消息。程序应该提供一个下拉组合框来显示在线的用户ip地址,选中地址即可以给该用户发送消息;如果下拉组合框的内容为空,则给所有用户发送广播消息。发送广播消息可以简单的给广播地址"255.255.255.255"发送报文来实现。

关于在线IP地址列表的获得方法,可以给广播地址发送一个约定的探测信息,收到该特定探测信息的用户就回发一个约定的信息报文,这样就可以从该报文中取出ip地址,加入到下拉组合框中。

附录

1. UDPClient.java源程序

package chapter06.client;
 
 import java.io.IOException;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 
 public class UDPClient {
  private int remotePort;
  private InetAddress remoteIP;
  private DatagramSocket socket;//UDP套接字
 
  //用于接收数据的报文字节数组缓存最大容量,字节为单位
  private static final int MAX_PACKET_SIZE = 512;
 
  public UDPClient(String remoteIP, String remotePort) throws IOException {
   this.remoteIP = InetAddress.getByName(remoteIP);
   this.remotePort = Integer.parseInt(remotePort);
   //创建一个UDP套接字,系统随机选定一个未使用的UDP端口绑定
   socket = new DatagramSocket();
   //设置接收数据超时
 //  socket.setSoTimeout(30000);
  }
 
  //定义一个数据的发送方法
  public void send(String msg) {
   try {
    //将待发送的字符串转为字节数组
    byte[] outData = msg.getBytes("utf-8");
    //构建用于发送的数据报文,构造方法中传入远程通信方(服务器)的ip地址和端口
    DatagramPacket outPacket = new DatagramPacket(outData, outData.length, remoteIP, remotePort);
    //给UDPServer发送数据报
    socket.send(outPacket);
 
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 
  //定义数据接收方法
  public String receive() {
   String msg;
   //先准备一个空数据报文
   DatagramPacket inPacket = new DatagramPacket(
     new byte[MAX_PACKET_SIZE], MAX_PACKET_SIZE);
   try {
    //读取报文,阻塞语句,有数据就装包在inPacket报文中,以装完或装满为止。
    socket.receive(inPacket);
    //将接收到的字节数组转为对应的字符串
    msg = new String(inPacket.getData(),
      0,inPacket.getLength(),"utf-8");
   } catch (IOException e) {
    e.printStackTrace();
    msg = null;
   }
   return msg;
  }
 }

2. Multicast.java源程序

package chapter06.multicast;
 
 import java.io.IOException;
 import java.net.DatagramPacket;
 import java.net.InetAddress;
 import java.net.MulticastSocket;
 
 public class Multicast {
  InetAddress groupIP;
  int port = 8900;
  MulticastSocket ms = null; //组播套接字
  byte[] inBuff = new byte[1024];
  byte[] outBuff = new byte[1024];
 
  public Multicast() throws IOException {
 
   groupIP = InetAddress.getByName("225.0.0.1");
   //开启一个组播端口(UDP端口)
   ms = new MulticastSocket(port);
   //告诉网卡这样的IP地址数据包要接收
   ms.joinGroup(groupIP);
  }
 
  public void send(String msg) {
   try {
    outBuff = ("201700000001 程旭元: " + msg).getBytes("utf-8");
    DatagramPacket outPacket = new DatagramPacket(outBuff, outBuff.length,
      groupIP, port);
    ms.send(outPacket);
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 
  public String receive() {
   try {
    DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
    ms.receive(inPacket);
    String msg = new String(inPacket.getData(), 0, inPacket.getLength(), "utf-8");
    return "From " + inPacket.getAddress() + " " + msg;
   } catch (IOException e) {
    return null;
   }
  }
 
  public void close() {
   try {
    ms.leaveGroup(groupIP);
    ms.close();
 
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
 }

UDPClientFX

package chapter06;

import chapter01.TextFileIO;
import chapter06.UDPClient;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;

public class UDPClientFX extends Application {
    private Button btnExit = new Button("退出");
    private Button btnSend = new Button("发送");
    private Button btnConnect = new Button("连接");
    //待发送信息的文本框
    private TextField tfSend = new TextField();
    private TextField tfip = new TextField("127.0.0.1");
    private TextField tfport = new TextField("8008");
    //显示信息的文本区域
    private TextArea taDisplay = new TextArea();
    private TextFileIO textFileIO = new TextFileIO();
    private UDPClient udpClient;
    Thread readThread;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        BorderPane mainPane = new BorderPane();
        // 顶部输入
        HBox tophbox = new HBox();
        tophbox.setSpacing(10);
        tophbox.setPadding(new Insets(10,20,10,20));
        tophbox.setAlignment(Pos.CENTER);
        tophbox.getChildren().addAll(new Label("IP地址:"),tfip,new Label("端口:"),tfport,btnConnect);
        //内容显示区域
        VBox vBox = new VBox();
        vBox.setSpacing(10);//各控件之间的间隔
        //VBox面板中的内容距离四周的留空区域
        vBox.setPadding(new Insets(10, 20, 10, 20));
        vBox.getChildren().addAll(tophbox,new Label("信息显示区:"),
                taDisplay, new Label("信息输入区:"), tfSend);
        //设置显示信息区的文本区域可以纵向自动扩充范围
        VBox.setVgrow(taDisplay, Priority.ALWAYS);
        mainPane.setCenter(vBox);
        //底部按钮区域
        HBox hBox = new HBox();
        hBox.setSpacing(10);
        hBox.setPadding(new Insets(10, 20, 10, 20));
        hBox.setAlignment(Pos.CENTER_RIGHT);
        hBox.getChildren().addAll(btnSend,  btnExit);
        mainPane.setBottom(hBox);
        Scene scene = new Scene(mainPane, 800, 400);
        primaryStage.setScene(scene);
        primaryStage.show();

        btnSend.setDisable(true);
        btnConnect.setOnAction(event -> {
            String ip = tfip.getText().trim();
            String port = tfport.getText().trim();
            try {
                udpClient = new UDPClient(ip,port);
//                String firstMsg = udpClient.receive();
//                taDisplay.appendText(firstMsg+"\n");
                btnSend.setDisable(false);
                btnConnect.setDisable(true);
                //多线程方法
                readThread = new Thread(()->{
                    String msg = null;
                    while((msg = udpClient.receive())!=null){
                        String msgTemp = msg;
                        Platform.runLater(()->{
                            taDisplay.appendText(msgTemp+"\n");
                        });
                    }
                    Platform.runLater(()->{
                        taDisplay.appendText("对话已关闭!\n");
                    });
                });
                readThread.start();
            } catch (IOException e) {
                taDisplay.appendText("服务器连接失败"+e.getMessage()+"\n");
                btnSend.setDisable(true);
            }
        });

        btnExit.setOnAction(event -> {
            endSystem();
        });
        primaryStage.setOnCloseRequest(event -> {
            endSystem();
        });
        btnSend.setOnAction(event -> {
            String sendMsg = tfSend.getText();
            if(sendMsg.equals("bye")) {
                btnConnect.setDisable(false);
                btnSend.setDisable(true);
            }
            udpClient.send(sendMsg);//向服务器发送一串字符
            taDisplay.appendText("客户端发送:" + sendMsg + "\n");
            //注释掉这句话,和线程不冲突,不会卡死。
//            String receiveMsg = udpClient.receive();//从服务器接收一行字符
//            taDisplay.appendText(receiveMsg + "\n");
            tfSend.clear();
        });

    }

    private void endSystem() {
        if(udpClient != null){
            //向服务器发送关闭连接的约定信息
            udpClient.send("bye");
            udpClient.close();
        }
        System.exit(0);
    }
}

UDPServer

package chapter06;

import javax.xml.crypto.Data;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Date;

/**
 * Author:
 * Data:2020/10/10
 * description:
 */
public class UDPServer {
    private DatagramSocket server;
    private DatagramPacket packet;
    byte[] buffer =  new byte[512];
    public UDPServer() throws IOException {
         server = new DatagramSocket(8008);
        packet = new DatagramPacket(buffer,buffer.length);
//
        System.out.println("服务器开始运行");
    }
    public void Runserver() throws IOException {
        while(true){
            System.out.println("等待消息");
            server.receive(packet);
            System.out.println("接收到信息");
            String msg = new String(packet.getData(),packet.getOffset(),packet.getLength(),"utf-8");
            String remsg = "20181002946&蔡诚杰&"+new Date().toString()+"&"+msg;
            byte[] outPutData = remsg.getBytes("utf-8");
            DatagramPacket outputPacket = new DatagramPacket(outPutData,outPutData.length,packet.getAddress(),packet.getPort());
            server.send(outputPacket);
            System.out.println("完成发送");
        }
    }

    public static void main(String[] args) throws IOException {
        UDPServer udpServer = new UDPServer();
        udpServer.Runserver();
    }
}

Multicast

package chapter06;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class Multicast {
    InetAddress groupIP;
    int port = 8900;
    MulticastSocket ms = null; //组播套接字
    byte[] inBuff = new byte[1024];
    byte[] outBuff = new byte[1024];

    public Multicast() throws IOException {

        groupIP = InetAddress.getByName("225.0.0.1");
        //开启一个组播端口(UDP端口)
        ms = new MulticastSocket(port);
        //告诉网卡这样的IP地址数据包要接收
        ms.joinGroup(groupIP);
    }

    public void send(String msg) {
        try {
            outBuff = ("20181002946 蔡诚杰: " + msg).getBytes("utf-8");
            DatagramPacket outPacket = new DatagramPacket(outBuff, outBuff.length,
                    groupIP, port);
            ms.send(outPacket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String receive() {
        try {
            DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length);
            ms.receive(inPacket);
            String msg = new String(inPacket.getData(), 0, inPacket.getLength(), "utf-8");
            return "From " + inPacket.getAddress() + " " + msg;
        } catch (IOException e) {
            return null;
        }
    }

    public void close() {
        try {
            ms.leaveGroup(groupIP);
            ms.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

MuClientFX

package chapter06;

import chapter01.TextFileIO;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;

public class MuClientFX extends Application {
    private Button btnExit = new Button("退出");
    private Button btnSend = new Button("发送");
    private Button btnConnect = new Button("连接");
    //待发送信息的文本框
    private TextField tfSend = new TextField();
    private TextField tfip = new TextField("127.0.0.1");
    private TextField tfport = new TextField("8008");
    //显示信息的文本区域
    private TextArea taDisplay = new TextArea();
    private TextFileIO textFileIO = new TextFileIO();
    private Multicast multicast;
    Thread readThread;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        BorderPane mainPane = new BorderPane();
        // 顶部输入
        HBox tophbox = new HBox();
        tophbox.setSpacing(10);
        tophbox.setPadding(new Insets(10,20,10,20));
        tophbox.setAlignment(Pos.CENTER);
        tophbox.getChildren().addAll(new Label("IP地址:"),tfip,new Label("端口:"),tfport,btnConnect);
        //内容显示区域
        VBox vBox = new VBox();
        vBox.setSpacing(10);//各控件之间的间隔
        //VBox面板中的内容距离四周的留空区域
        vBox.setPadding(new Insets(10, 20, 10, 20));
        vBox.getChildren().addAll(new Label("组播对话:"),
                taDisplay, new Label("信息输入区:"), tfSend);
        //设置显示信息区的文本区域可以纵向自动扩充范围
        VBox.setVgrow(taDisplay, Priority.ALWAYS);
        mainPane.setCenter(vBox);
        //底部按钮区域
        HBox hBox = new HBox();
        hBox.setSpacing(10);
        hBox.setPadding(new Insets(10, 20, 10, 20));
        hBox.setAlignment(Pos.CENTER_RIGHT);
        hBox.getChildren().addAll(btnSend,  btnExit);
        mainPane.setBottom(hBox);
        Scene scene = new Scene(mainPane, 800, 400);
        primaryStage.setScene(scene);
        primaryStage.show();

        try {
            multicast = new Multicast();
//                String firstMsg = udpClient.receive();
//                taDisplay.appendText(firstMsg+"\n");
            btnConnect.setDisable(true);
            //多线程方法
            readThread = new Thread(()->{
                String msg = null;
                while((msg = multicast.receive())!=null){
                    String msgTemp = msg;
                    Platform.runLater(()->{
                        taDisplay.appendText(msgTemp+"\n");
                    });
                }
                Platform.runLater(()->{
                    taDisplay.appendText("对话已关闭!\n");
                });
            });
            readThread.start();
        } catch (IOException e) {
            taDisplay.appendText("服务器连接失败"+e.getMessage()+"\n");
            btnSend.setDisable(true);
        }

        btnSend.setDisable(false);

        btnExit.setOnAction(event -> {
            endSystem();
        });
        primaryStage.setOnCloseRequest(event -> {
            endSystem();
        });
        btnSend.setOnAction(event -> {
            String sendMsg = tfSend.getText();
            if(sendMsg.equals("bye")) {
                btnConnect.setDisable(false);
                btnSend.setDisable(true);
            }
            multicast.send(sendMsg);//向服务器发送一串字符
            taDisplay.appendText("客户端发送:" + sendMsg + "\n");
            //注释掉这句话,和线程不冲突,不会卡死。
//            String receiveMsg = udpClient.receive();//从服务器接收一行字符
//            taDisplay.appendText(receiveMsg + "\n");
            tfSend.clear();
        });

    }

    private void endSystem() {
        if(multicast != null){
            //向服务器发送关闭连接的约定信息
            multicast.send("bye");
            multicast.close();
        }
        System.exit(0);
    }
}
);
            if(sendMsg.equals("bye")) {
                btnConnect.setDisable(false);
                btnSend.setDisable(true);
            }
            multicast.send(sendMsg);//向服务器发送一串字符
            taDisplay.appendText("客户端发送:" + sendMsg + "\n");
            //注释掉这句话,和线程不冲突,不会卡死。
//            String receiveMsg = udpClient.receive();//从服务器接收一行字符
//            taDisplay.appendText(receiveMsg + "\n");
            tfSend.clear();
        });

    }

    private void endSystem() {
        if(multicast != null){
            //向服务器发送关闭连接的约定信息
            multicast.send("bye");
            multicast.close();
        }
        System.exit(0);
    }
}

相关文章:

  • Java网络编程:邮件发送程序设计,SMPT传输协议实现(完整代码实现)
  • java网络编程:基于HTTP的下载程序设计及web浏览器制作(完整代码实现)
  • Java网络编程:Socket实现的扫描程序设计 (完整代码实现)
  • 为什么要学习设计模式?看完这篇你就懂了!
  • 使用Wps切分单页PDF文件为多页pdf
  • 深入解析JVM(四):对象的创建
  • IntellIJ IDEA导入项目后无法运行方法的解决方法!
  • Java使用Redis删除指定前缀Key
  • Linux中删除重复行的三种方法
  • Java面试突击系列(一):消息队列的面试连环炮
  • Java面试突击系列(二):分布式搜索引擎的面试连环炮
  • Java面试突击系列(三):分布式缓存
  • Java面试突击系列(四):Redis面试常见问题
  • Java面试突击系列(五):Redis集群模式
  • Java面试突击系列(六):分布式系统
  • CSS居中完全指南——构建CSS居中决策树
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • EOS是什么
  • JS变量作用域
  • laravel 用artisan创建自己的模板
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • Map集合、散列表、红黑树介绍
  • PhantomJS 安装
  • PHP的类修饰符与访问修饰符
  • use Google search engine
  • Zepto.js源码学习之二
  • 百度小程序遇到的问题
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 如何选择开源的机器学习框架?
  • 使用agvtool更改app version/build
  • 微服务核心架构梳理
  • 我看到的前端
  • 新书推荐|Windows黑客编程技术详解
  • 一起参Ember.js讨论、问答社区。
  • AI算硅基生命吗,为什么?
  • 说说我为什么看好Spring Cloud Alibaba
  • ​力扣解法汇总946-验证栈序列
  • ​总结MySQL 的一些知识点:MySQL 选择数据库​
  • "无招胜有招"nbsp;史上最全的互…
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (arch)linux 转换文件编码格式
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (MATLAB)第五章-矩阵运算
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (十一)手动添加用户和文件的特殊权限
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (已解决)什么是vue导航守卫
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转)http协议
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • ***测试-HTTP方法