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

Java实现UDP与TCP应用程序

三、Java实现UDP应用程序

3.1 InetAddress类

java.net.InteAddress类是用于描述IP地址和域名的一个Java类;

常用方法如下:

  • public static InetAddress getByName(String host):根据主机名获取InetAddress对象
  • public String getHostName():获取该对象对应的主机名
  • public String getHostAddress()获取该对象对应的IP地址

示例代码:

package com.dfbz.demo01_实现UDP;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_InetAddress {
    public static void main(String[] args) throws UnknownHostException {
        // 根据域名获取InetAddress对象
        InetAddress localhost = InetAddress.getByName("localhost");
        
        // 获取主机名
        String hostName = localhost.getHostName();
        
        // 获取ip地址
        String hostAddress = localhost.getHostAddress();

        System.out.println(hostName);               // localhost
        System.out.println(hostAddress);            // 127.0.0.1

        System.out.println("-----------");
        
        InetAddress baidu = InetAddress.getByName("www.baidu.com");
        System.out.println(baidu.getHostName());        // www.baidu.com
        System.out.println(baidu.getHostAddress());     // 14.119.104.254
    }
}

3.2 DatagramPacket类

java.net.DatagramPacket是一种UDP协议的数据包结构,它包含源地址、目标地址和要传输的数据,用于封装一个UDP数据报文。

使用DatagramPacket,可以在应用程序之间发送和接收UDP数据包。

3.2.1 构造方法

  • public DatagramPacket(byte[] buf, int length, InetAddress address, int port):创建一个数据包对象
    • buf:要发送的内容
    • length:要发送的内容⻓度,单位字节
    • address:接收端的ip地址
    • port:接收端⼝号
  • public DatagramPacket(byte buf[], int length):创建一个数据包对象

示例代码:

package com.dfbz.demo01_实现UDP;

import java.net.DatagramPacket;
import java.net.InetAddress;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo02_DatagramPacket_构造方法 {
    public static void main(String[] args) throws Exception {
        // 创建一个UDP报文用于发送
        DatagramPacket packet = new DatagramPacket(
                "abc".getBytes(),                       // 报文封装的数据
                "abc".getBytes().length,                // 报文的数据长度
                InetAddress.getByName("localhost"),     // 接收端的地址
                8989                                    // 端口
        );

        // 创建一个UDP报文用于接收
        byte[] data = new byte[1024];
        DatagramPacket packet2 = new DatagramPacket(
                data,
                data.length
        );
    }
}

3.2.2 常用方法

  • public synchronized int getLength():获取此UDP数据包载荷的数据长度(单位字节)
  • public synchronized int getPort():获取此UDP数据包的目的端口号
  • public synchronized byte[] getData() :获取此UDP数据包的载荷部分(数据)

示例代码:

package com.dfbz.demo01_实现UDP;

import java.net.DatagramPacket;
import java.net.InetAddress;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo03_DatagramPacket_成员方法 {
    public static void main(String[] args) throws Exception {

        // 创建一个UDP报文用于发送UDP报文
        DatagramPacket packet = new DatagramPacket(
                "abc".getBytes(),
                "abc".getBytes().length,
                InetAddress.getByName("localhost"),
                8989
        );

        byte[] data = packet.getData();             // 获取UDP报文的数据
        int length = packet.getLength();            // 获取UDP报文的数据长度
        InetAddress address = packet.getAddress();  // 获取该UDP报文要发送的地址
        int port = packet.getPort();                // 获取该UDP报文要发送的端口

        System.out.println("getData(): " + new String(data));             // abc
        System.out.println("getLength(): " + length);                     // 3
        System.out.println("getAddress(): " + address.getHostAddress());  // 127.0.0.1
        System.out.println("getPort(): " + port);                         // 8989
    }
}

3.2 DatagramSocket类

java.net.DatagramSocket它提供了发送和接收UDP数据包的功能,用于描述一个UDP发送端或接收端;

在 Java 中,可以通过 DatagramSocket 类来创建一个套接字,并且可以通过 socket 的 receive 和 send 方法来发送和接收 UDP 数据包。这些方法提供了更好的灵活性,可以让您轻松地从多个地址和端口接收 UDP 数据包,并且可以选择性地发送数据包到特定地址和端口。 总的来说,DatagramSocket 是 UDP 协议的一种高级抽象,可以让您方便地在 Java 应用程序中发送和接收 UDP 数据包。

3.2.1 构造方法

  • public DatagramSocket(int port):通过端口构建一个发送端/接收端

示例代码:

DatagramSocket socket = new DatagramSocket(6969);

3.2.2 常用方法

  • public void send(DatagramPacket p):发送一个UDP数据包
  • public synchronized void receive(DatagramPacket p):接收一个UDP数据包
  • public void close():释放该Socket占用的资源

3.3 设计UDP应用程序

  • 发送端:
package com.dfbz.demo01_实现UDP;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo04_发送端 {
    public static void main(String[] args) throws Exception {

        // 创建一个UDP报文的发送/接收器
        DatagramSocket socket = new DatagramSocket();

        // 封装一个UDP报文
        byte[] data = new byte[8192];
        DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("localhost"), 9999);

        // 设置UDP报文的数据
        packet.setData("你好".getBytes());
        packet.setLength("你好".getBytes().length);

        // 发送UDP报文
        socket.send(packet);

        // 关闭资源
        socket.close();
    }
}
  • 接收端:
package com.dfbz.demo01_实现UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo05_接收端 {
    public static void main(String[] args) throws IOException {

        // 创建一个UDP报文的发送/接收器
        DatagramSocket socket = new DatagramSocket(9999);

        // 创建一个UDP报文用于接收数据
        byte[] data = new byte[8192];
        DatagramPacket packet = new DatagramPacket(data, data.length);

        /*
         将socket接收到的UDP报文赋值给packet
         注意: 如果没有收到发送端的UDP报文,这一行代码将会阻塞当前线程的执行
         */
        socket.receive(packet);
        socket.close();

        System.out.println("接收到了数据: " + new String(data, 0, packet.getLength()));
    }
}

四、Java实现TCP程序

在TCP通信中,分为数据的发送端(客户端)和接收端(服务器),当建立连接成功后(三次握手),才可以进行数据的发送;

在Java中,提供了两个类用于实现TCP通信程序:

  • 1)客户端:java.net.Socket 类表示;用于与服务器端建立连接,向服务器端发送数据报文等;
  • 2)服务端:java.net.ServerSocket 类表示;用于与客户端的交互;

4.1 Socket

java.net.Sokcet用于封装一个TCP应用程序的客户端;

4.1.1 构造方法

  • public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为本机地址。

示例代码:

Socket client = new Socket("127.0.0.1", 6868);

4.1.2 成员方法

  • public InputStream getInputStream() : 返回此套接字的输入流。关闭生成的InputStream也将关闭相关的Socket。
  • public OutputStream getOutputStream() : 返回此套接字的输出流。关闭生成的OutputStream也将关闭相关的Socket。
  • public void close() :关闭此套接字。关闭此socket也将关闭相关的InputStream和OutputStream 。
  • public void shutdownOutput() : 禁用此套接字的输出流。任何先前写出的数据将被发送,随后终止输出流。

2.2 ServerSocket

ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。

4.2.1 构造方法

  • public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。

构造举例,代码如下:

ServerSocket server = new ServerSocket(6666);

4.2.2 成员方法

  • public Socket accept() :监听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。

4.3 设计TCP应用程序

  • 客户端代码:
package com.dfbz.demo01_实现TCP;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_客户端 {
    public static void main(String[] args) throws Exception {

        // 创建一个客户端
        Socket socket = new Socket("localhost", 6666);

        // 获取与服务器的输入流(用于读取服务器的数据)
        InputStream is = socket.getInputStream();

        // 获取与服务器的输出流(用于向服务器写出数据)
        OutputStream os = socket.getOutputStream();

        // 发送数据到服务器
        os.write("你好呀~!在吗?".getBytes());

        byte[] data = new byte[1024];

        /*
         读取服务器的数据
         注意: 如果没有接收到服务器的数据,这一行代码将会阻塞当前线程的执行
         */
        int len = is.read(data);
        System.out.println("接收到来自服务器的信息【" + new String(data, 0, len) + "】");

        os.write("在干嘛?".getBytes());

        /*
         读取服务器的数据
         注意: 如果没有接收到服务器的数据,这一行代码将会阻塞当前线程的执行
         */
        len = is.read(data);
        System.out.println("接收到来自服务器的信息【" + new String(data, 0, len) + "】");

        socket.close();
    }
}
  • 服务端代码:
package com.dfbz.demo01_实现TCP;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo02_服务端 {
    public static void main(String[] args) throws Exception {
        // 创建了一个服务器
        ServerSocket serverSocket = new ServerSocket(6666);

        /*
         接收一个客户端
         注意: 如果没有客户端来连接服务器,这一行代码将会阻塞当前线程的执行
         */
        Socket client = serverSocket.accept();

        // 获取与客户端的输入流(用于读取客户端的数据)
        InputStream is = client.getInputStream();

        // 获取与客户端的输出流(用于向客户端写出数据)
        OutputStream os = client.getOutputStream();

        byte[] data = new byte[1024];

        /*
        从客户端读取数据
        注意: 如果没有接收到客户端的数据,这一行代码将会阻塞当前线程的执行
         */
        int len = is.read(data);
        System.out.println("接收到来自客户端的信息【" + new String(data, 0, len) + "】");

        os.write("在哦!".getBytes());

        /*
        从客户端读取数据
        注意: 如果没有接收到客户端的数据,这一行代码将会阻塞当前线程的执行
         */
        len = is.read(data);
        System.out.println("接收到来自客户端的信息【" + new String(data, 0, len) + "】");

        os.write("在吃饭呢!".getBytes());

        // 关闭资源
        client.close();
        serverSocket.close();
    }
}

上述程序中,发送内容都是写死在代码中,我们使用Scanner来接受键盘录入的数据进行发送;

  • 改造客户端程序:
package com.dfbz.demo02_使用Scanner改造;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_客户端 {
    public static void main(String[] args) throws Exception {

        // 创建一个客户端,并且去连接服务器
        Socket socket = new Socket("localhost", 7777);

        // 获取与服务器的输入/输出流(用于读取服务器的数据/向服务器写出数据)
        InputStream is = socket.getInputStream();
        OutputStream out = socket.getOutputStream();

        Scanner scanner = new Scanner(System.in);
        byte[] data = new byte[1024];

        while (true) {
            String str = scanner.nextLine();

            // 向服务器写出数据
            out.write(str.getBytes());

            // 读取服务器的信息
            int len = is.read(data);            // 这一句代码会造成阻塞,如果服务器没有向客户端写出数据,那么这一句代码阻塞
            System.out.println("来自服务器的信息【" + new String(data, 0, len) + "】");
        }

    }
}
  • 改造服务端程序:
package com.dfbz.demo02_使用Scanner改造;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo02_服务端 {
    public static void main(String[] args) throws Exception {
        // 创建一个服务器
        ServerSocket serverSocket = new ServerSocket(7777);

        // 接收到一个客户端(这一段代码会造成阻塞,如果没有客户端来连接服务器,那么代码会一直阻塞在这里)
        Socket client = serverSocket.accept();

        // 获取这个客户端的ip地址
        String hostAddress = client.getInetAddress().getHostAddress();

        // 获取这个客户端的端口
        int port = client.getPort();


        // 获取与这个客户端的输入/输出流(用于与这个客户端的读写操作)
        InputStream is = client.getInputStream();
        OutputStream os = client.getOutputStream();

        Scanner scanner = new Scanner(System.in);
        byte[] data = new byte[1024];

        while (true) {

            // 读取这个客户端的信息(如果客户端没有发送数据过来,这句代码会造成阻塞)
            int len = is.read(data);

            System.out.println("" + "来自客户端【" + hostAddress + "】" + "端口为【" + port + "】" + "的应用程序发送的消息【" + new String(data, 0, len) + "】");

            // 接收控制台的数据. 同样的,如果控制台没有输入数据,那么这一行代码也会阻塞当前线程的执行
            String str = scanner.nextLine();

            os.write(str.getBytes());
        }

    }
}

五、综合案例

5.1 图片上传案例

5.2.1 UDP实现图片上传

UDP图片上传流程:

需要注意的是:由于UDP的特点(面向无连接、不安全等),因此采用UDP协议上传的图片会造成数据丢失,图片失真、丢失像素等问题;

【发送端】

package com.dfbz.demo01_图片上传;

import java.io.FileInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_UDP_发送端 {
    public static void main(String[] args) throws Exception {
        // 创建一个套接字
        DatagramSocket socket = new DatagramSocket();

        // 从磁盘中读取文件
        FileInputStream fis = new FileInputStream("100.png");

        byte[] data = new byte[8192];

        // 创建一个UDP数据包
        DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("localhost"), 9999);

        int len;
        while ((len = fis.read(data)) != -1) {

            // 将从磁盘中读取到的数据设置到UDP报文中
            packet.setData(data);
            packet.setLength(len);

            // 发送UDP报文给接收端
            socket.send(packet);
        }

        // 发送一个空报文,作为结束标识
        packet.setLength(0);
        socket.send(packet);

        // 释放资源
        fis.close();
        socket.close();
    }
}

【接收端】

package com.dfbz.demo01_图片上传;

import java.io.FileOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo02_UDP_接收端 {

    public static void main(String[] args) throws Exception {

        // 创建一个套接字(用于接收UDP报文)
        DatagramSocket socket = new DatagramSocket(9999);

        byte[] data = new byte[8192];

        // 创建一个UDP数据包
        DatagramPacket packet = new DatagramPacket(data, data.length);

        // 创建一个文件输出流,将接收到的数据写入到这个文件中
        FileOutputStream fos = new FileOutputStream("udp.png");

        while (true) {
            // 接收UDP报文
            socket.receive(packet);

            // 获取报文数据长度
            int length = packet.getLength();

            if (length == 0) {
                // 说明读取到了末尾
                break;
            }
            fos.write(packet.getData(),0,length);
        }

        // 释放资源
        fos.close();
        socket.close();
    }
}

查看上传之前的原文件大小和上传之后的文件大小:

5.2.2 TCP实现图片上传

TCP图片上传流程:

1)客户端首先通过输入流将自己磁盘中的图片读取到内存中

2)客户端通过TCP连接的输出流,向服务器写出刚刚读取到的图片数据

3)服务器通过TCP连接的输入流,将客户端刚刚发送过来的图片数据读取到内存中

4)服务器通过输出流将内存中的数据写入到服务器的磁盘中

Tips:我们学习过程中,将服务器和客户端放在同一台机器。但实际开发中服务器和客户端不是在同一台机器,

【客户端代码实现】

package com.dfbz.demo02;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_Server {
    public static void main(String[] args) throws Exception {

        // 声明服务器
        ServerSocket serverSocket = new ServerSocket(8888);

        // 接收到一个客户端
        Socket client = serverSocket.accept();

        System.out.println(client.getInetAddress().getHostAddress() + "连接成功");

        // 读取客户端传递过来的数据
        InputStream is = client.getInputStream();

        // 随机生成一个文件名写出到磁盘
        FileOutputStream fos = new FileOutputStream(UUID.randomUUID().toString() + ".png");

        byte[] data = new byte[1024];

        int len;
        while ((len = is.read(data)) != -1) {

            // 写出到磁盘
            fos.write(data, 0, len);
        }

        // 释放资源
        fos.close();
        client.close();
    }
}

【服务端代码实现】

package com.dfbz.demo01_图片上传;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo04_TCP_服务端 {
    public static void main(String[] args) throws Exception {
        // 声明服务器
        ServerSocket serverSocket = new ServerSocket(8888);

        // 接收到一个客户端
        Socket client = serverSocket.accept();

        System.out.println(client.getInetAddress().getHostAddress() + "连接成功");

        // 读取客户端传递过来的数据
        InputStream is = client.getInputStream();

        // 随机生成一个文件名写出到磁盘
        FileOutputStream fos = new FileOutputStream("tcp.png");

        byte[] data = new byte[1024];
        int len;
        while ((len = is.read(data)) != -1) {

            // 写出到磁盘
            fos.write(data, 0, len);
        }

        // 释放资源
        fos.close();
        client.close();
    }
}

查看上传之前的原文件大小和上传之后的文件大小:

5.2.3 多线程改进TCP图片上传

实际开发中一个服务器对应N多个客户端,其他客户端均可以上传图片。我们的代码在同一时间只允许一个人上传图片,如果这个人上传的文件较大,那么势必会造成其他用户处于等待状态;针对这种情况我们可以使用多线程来解决。

服务器每次接受到一个客户端时,都开启一个线程来独立处理这个客户端的上传任务。这样在很多人同时来上传文件时,都可以一起上传。

  • 多线程改进服务器:
package com.dfbz.demo01_图片上传;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo05_TCP_服务端_多线程 {
    public static void main(String[] args) throws Exception {

        // 创建一个服务器
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            // 每接收到一个客户端都创建一个新的线程为它服务
            Socket client = serverSocket.accept();

            System.out.println("客户端【" + client.getInetAddress().getHostAddress() + "】连接成功啦!");
            new Thread() {
                @Override
                public void run() {

                    try {
                        // 获取与客户端的输入流(用于读取客户端发送过来的字节)
                        InputStream is = client.getInputStream();

                        // 关联本地的一个文件(随机生成一个名称)
                        FileOutputStream fos = new FileOutputStream(UUID.randomUUID() + ".png");

                        byte[] data = new byte[8192];
                        int len;
                        while ((len = is.read(data)) != -1) {
                            fos.write(data, 0, len);
                        }

                        fos.close();
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
}

5.2 在线聊天案例

我们刚刚使用了TCP完成了聊天功能的编写;我们会发现我们的程序是由问题的,就是读写是串行的!

我们整个应用程序只有一个线程,那就mian线程,代码都是从上往下执行,如果main线程当前在读操作,那么就不能写。而且如果此时一方如果没有发送信息给另一方,那么另一方的read方法将会一直处于阻塞状态,代码不会往下执行;此时想往对方写出数据肯定是不行的;

我们利用多线程技术来改造我们之前的代码,让我们的代码既一直读,又可以一直写;

5.2.1 多线程UDP在线聊天案例

【多线程改进发送端】

package com.dfbz.demo02_多线程实现聊天案例;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_UDP_发送端 {
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            try {
                // 1. 创建一个套接字,用于发送数据到接收端
                DatagramSocket socket = new DatagramSocket();

                // 2. 创建一个UDP报文,用于封装要发送到接收端的数据(接收端的端口为9999)
                byte[] data = new byte[8192];
                DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9999);

                // 3. 死循环发送信息给接收端
                Scanner scanner = new Scanner(System.in);
                while (true) {
                    // 接收键盘录入数据
                    String line = scanner.nextLine();

                    // 将数据设置到UDP报文中
                    packet.setData(line.getBytes());
                    packet.setLength(line.getBytes().length);

                    // 将此UDP报文发送给接收端
                    socket.send(packet);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }).start();


        new Thread(() -> {
            try {
                // 1. 创建一个套接字,用于接收来自接收端的数据(发送端的端口为7777)
                DatagramSocket socket = new DatagramSocket(7777);

                // 2. 创建一个UDP报文,用于接收来自接收端的数据
                byte[] data = new byte[8192];
                DatagramPacket packet = new DatagramPacket(data, data.length);

                // 3. 死循环接收来自接收端响应的信息
                while (true) {

                    // 接收来自接收端的UDP报文
                    socket.receive(packet);
                    data = packet.getData();
                    int len = packet.getLength();

                    System.out.println("来自接收端【" + packet.getAddress().getHostAddress() + "】的信息: 【" + new String(data, 0, len) + "】");
                }

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

        }).start();
    }
}

【多线程改进接收端】

package com.dfbz.demo02_多线程实现聊天案例;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo02_UDP_接收端 {
    public static void main(String[] args) throws Exception {

        // 接收端的读线程
        new Thread(() -> {

            try {

                // 1. 创建一个套接字,用于接收来自发送端的数据(接收端的端口为9999)
                DatagramSocket socket = new DatagramSocket(9999);

                // 2. 创建一个UDP报文,用于接收来自发送端的数据
                byte[] data = new byte[8192];
                DatagramPacket packet = new DatagramPacket(data, data.length);

                // 3. 死循环接收来自发送端响应的信息
                while (true) {

                    // 接收来自发送端发送的UDP报文
                    socket.receive(packet);
                    data = packet.getData();
                    int len = packet.getLength();

                    System.out.println("来自发送端的【" + packet.getAddress().getHostAddress() + "】的信息: 【" + new String(data, 0, len) + "】");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        // 接收端的写线程
        new Thread(() -> {
            try {
                // 1. 创建一个套接字,用于发送数据给发送端
                DatagramSocket socket = new DatagramSocket();

                // 2. 创建一个UDP报文,用于封装要发送到发送端的数据(发送端的端口为7777)
                byte[] data = new byte[8192];
                DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 7777);

                // 3. 死循环发送信息给发送端
                Scanner scanner = new Scanner(System.in);
                while (true) {
                    // 接收键盘录入数据
                    String line = scanner.nextLine();

                    // 将数据设置到UDP报文中
                    packet.setData(line.getBytes());
                    packet.setLength(line.getBytes().length);

                    // 将此UDP报文发送给发送端
                    socket.send(packet);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }
}

5.2.2 多线程TCP在线聊天案例

【多线程改进客户端】

package com.dfbz.demo02_多线程实现聊天案例;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo03_TCP_客户端 {
    public static void main(String[] args) throws IOException {
        // 创建一个客户端,去连接服务器
        Socket socket = new Socket("127.0.0.1", 9999);

        // 获取与服务器的输入输出流
        InputStream is = socket.getInputStream();
        OutputStream os = socket.getOutputStream();

        // 读线程,专门用于读取服务器的信息
        new Thread() {

            @Override
            public void run() {
                try {
                    byte[] data = new byte[1024];

                    while (true) {
                        /*
                        一直死循环读取服务器发送过来的数据
                        如果服务器没有数据来也只是阻塞当前线程,并不会影响其他线程
                         */
                        int len = is.read(data);
                        System.out.println("接收到来自服务器的信息【" + new String(data, 0, len) + "】");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();

        // 写线程,专门向服务器写出数据
        new Thread() {
            @Override
            public void run() {
                try {
                    Scanner scanner = new Scanner(System.in);
                    while (true) {
                        /*
                        一直死循环读取键盘录入的数据
                        如果键盘没有输入数据,也只是阻塞当前线程,并不会影响其他线程
                         */
                        String str = scanner.nextLine();
                        os.write(str.getBytes());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

【多线程改进服务端】

package com.dfbz.demo02_多线程实现聊天案例;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo04_TCP_服务端 {
    public static void main(String[] args) throws Exception {
        // 创建一台服务器
        ServerSocket serverSocket = new ServerSocket(9999);

        // 接收一个客户端
        Socket client = serverSocket.accept();

        // 获取与这个客户端的输入输出流
        InputStream is = client.getInputStream();
        OutputStream os = client.getOutputStream();

        // 获取客户端的IP地址
        String hostAddress = client.getInetAddress().getHostAddress();

        // 获取客户端的应用程序端口
        int port = client.getPort();
        System.out.println("有一个客户端来连接了,地址【" + hostAddress + "】,端口【" + port + "】");

        // 读线程
        new Thread() {
            @Override
            public void run() {
                try {
                    byte[] data = new byte[1024];

                    while (true) {
                          /*
                        一直死循环读取客户端发送过来的数据
                        如果客户端没有数据来也只是阻塞当前线程,并不会影响其他线程
                         */
                        int len = is.read(data);
                        System.out.println("接收到了来自客户端【" + hostAddress + "】的信息【" + new String(data, 0, len) + "】");
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();

        // 写线程
        new Thread() {
            @Override
            public void run() {
                try {
                    Scanner scanner = new Scanner(System.in);
                    while (true) {
                        /*
                        一直死循环读取键盘录入的数据
                        如果键盘没有输入数据,也只是阻塞当前线程,并不会影响其他线程
                         */
                        String str = scanner.nextLine();
                        os.write(str.getBytes());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

http://www.kler.cn/a/465608.html

相关文章:

  • 2022浙江大学信号与系统笔记
  • DeepSeek v3为何爆火?如何用其集成Milvus搭建RAG?
  • 树莓派4b如何连接ov7670摄像头
  • 开源数据集成平台白皮书重磅发布《Apache SeaTunnel 2024用户案例合集》!
  • 设计模式之桥接设计模式
  • 深入 Redis:高级特性与最佳实践
  • python+PyMuPDF库:(三)pdf文件的选择性合并、其他格式文件转pdf
  • React Class 组件与函数组件全方位对比
  • 网关的主要类型和它们的特点
  • [网络安全] DVWA之 Open HTTP Redirect 攻击姿势及解题详析合集
  • AI机器人与政务服务机器人的服务局限问题分析
  • vue2、element的el-select 选项框的宽度设置、文本过长问题
  • 企业微信——智能表格学习
  • 实战项目模块之1------单个按键短按切换不同工作模式
  • 《HarmonyOS第一课》焕新升级,赋能开发者快速掌握鸿蒙应用开发
  • 【前端系列】Pinia状态管理库
  • 测试岗位面试常见的数据库问题及解答
  • gitlab高级功能之 CICD Steps
  • 微前端Webpack集成Vite子应用避坑指南
  • 微信小程序获取后端数据
  • PS4代理伺服器指南
  • JavaScript 基础2
  • 【网络安全】PostMessage:分析JS实现XSS
  • .e01, ..., .e0n的分卷压缩包怎么解压
  • 多目标优化算法——基于分解的多目标进化算法(MOEA-D)
  • [C++]vector(超详细)