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();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/949671.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

信号处理-消除趋势项

matlab 版本 python 版本 import numpy as np import matplotlib.pyplot as plt from matplotlib import rcParams# 设置中文字体 rcParams[font.sans-serif] [SimHei] # 设置默认字体为黑体 rcParams[axes.unicode_minus] False # 解决负号显示问题def compute_time(n, f…

Linux 安装 meilisearch

前言 由于项目部分数据需要用到搜索引擎进行检索,但是服务器资源有限,安装elasticsearch过于笨重,不太符合现实情况,所以选择了meilisearch作为搜索引擎来使用,目前使用接近一年,运行良好。 安装 在/usr/…

【C++数据结构——查找】二叉排序树(头歌实践教学平台习题)【合集】

目录😋 任务描述 相关知识 1. 二叉排序树的基本概念 2. 二叉排序树节点结构体定义 3. 创建二叉排序树 4. 判断是否为二叉排序树 5. 递归查找关键字为 6 的结点并输出查找路径 6. 删除二叉排序树中的节点 测试说明 通关代码 测试结果 任务描述 本关任务&a…

TCP与DNS的报文分析

场景拓扑: 核心路由配置: 上(DNS):10.1.1.1/24 下(WEB):20.1.1.1/24 左(client):192.168.0.1/24 右(PC3):192.168.1.1/24Clint2配置&a…

OpenHarmony通过挂载镜像来修改镜像内容,RK3566鸿蒙开发板演示

在测试XTS时会遇到修改产品属性、SElinux权限、等一些内容,修改源码再编译很费时。今天为大家介绍一个便捷的方法,让OpenHarmony通过挂载镜像来修改镜像内容!触觉智能Purple Pi OH鸿蒙开发板演示。搭载了瑞芯微RK3566四核处理器,树…

linux ansible部署

ansible部署完后,执行报错 # ansible one -i hosts -m ping dataos193 | FAILED! > {"msg": "Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add …

【微服务】3、配置管理

微服务配置管理 已掌握的微服务组件及配置管理问题引出 已掌握注册中心、Openfan、远程调用、负载均衡、网关等组件,具备微服务开发能力,但仍存在其他问题待解决。微服务和网关存在大量配置文件,其中包含很多重复配置,如数据库、日…

C# 事件

目录 1、事件模型的5个组成部分2、使用内置委托类型声明事件2.1 EventHandler2.1.1 &#xff1f;2.1.2 this2.1.3 使用匿名函数和lamda表达式2.1.3.1 匿名函数2.1.3.2 lamda表达式 2.1.4 异常处理 2.2 EventHandler<TEventArgs> 3、使用自定义委托类型声明事件3.1 事件的…

php反序列化原生态 ctfshow练习 字符串逃逸

web262 拿着题审计一下 <?php error_reporting(0); class message{public $from;public $msg;public $to;public $tokenuser;public function __construct($f,$m,$t){$this->from $f;$this->msg $m;$this->to $t;} }$f $_GET[f]; $m $_GET[m]; $t $_GET[t…

【C语言程序设计——循环程序设计】利用循环求数值 x 的平方根(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 一、求平方根的迭代公式 1. 原理 2. 代码实现示例 二、绝对值函数fabs() 1. 函数介绍 2. 代码示例 三、循环语句 1. for循环 2. while循环 3. do - while循环 编程要求 测试说明 通关代码 测试结果 任务描述 本关任务&…

使用 Three.js 创建动态粒子效果

今天&#xff0c;带大家使用粒子实现一个粒子飞毯的效果&#xff0c;我们先来看一下效果。 实现 初始化场景 首先创建一个场景&#xff0c;所有 3D 对象都会被添加到这个场景中。 const scene new THREE.Scene();相机和渲染器 配置相机和渲染器来捕捉和显示场景。 相机…

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World 2025/1/3 14:06 百度&#xff1a;android studio helloworld android studio hello world kotlin helloword kotlin 串口 no run configurations added android studio no run configurations added 1、…

c#使用SevenZipSharp实现压缩文件和目录

封装了一个类&#xff0c;方便使用SevenZipSharp&#xff0c;支持加入进度显示事件。 双重加密压缩工具范例&#xff1a; using SevenZip; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.…

Ubuntu 20.04安装gcc

一、安装GCC 1.更新包列表 user596785154:~$ sudo apt update2.安装gcc user596785154:~$ sudo apt install gcc3.验证安装 user596785154:~$ gcc --version二 编译C文件 1.新建workspace文件夹 user596785154:~$ mkdir workspace2.进入workspace文件夹 user596785154:~…

网络协议安全的攻击手法

1.使用SYN Flood泛洪攻击&#xff1a; SYN Flood(半开放攻击)是最经典的ddos攻击之一&#xff0c;他利用了TCP协议的三次握手机制&#xff0c;攻击者通常利用工具或控制僵尸主机向服务器发送海量的变源端口的TCP SYN报文&#xff0c;服务器响应了这些报文后就会生成大量的半连…

晨辉面试抽签和评分管理系统之一:考生信息管理和编排

晨辉面试抽签和评分管理系统&#xff08;下载地址:www.chenhuisoft.cn&#xff09;是公务员招录面试、教师资格考试面试、企业招录面试等各类面试通用的考生编排、考生入场抽签、候考室倒计时管理、面试考官抽签、面试评分记录和成绩核算的面试全流程信息化管理软件。提供了考生…

鸿蒙的APP真机调试以及发布

目录&#xff1a; 1、创建好鸿蒙项目2、创建AGC项目3、实现自动签名3.1、手动方式创建签名文件和密码 4、运行项目5、无线真机调试 1、创建好鸿蒙项目 2、创建AGC项目 &#xff08;1&#xff09;在File->Project Structure->Project->Signing Configs中进行登录。(未…

H7-TOOL固件2.27发布,新增加40多款芯片脱机烧录,含多款车轨芯片,发布LUA API手册,CAN助手增加负载率,错误状态信息检测

H7-TOOL详细介绍&#xff08;含操作手册&#xff09;&#xff1a;H7-TOOL开发工具&#xff0c;1拖4/16脱机烧录&#xff0c;高速DAPLINK&#xff0c;RTOS Trace&#xff0c;CAN/串口助手, 示波器, RTT等&#xff0c;支持WiFi&#xff0c;以太网&#xff0c;高速USB和手持 - H7-…

SpringMVC(一)配置

目录 引入 第一章&#xff1a;Java web的发展历史 一、Model I和Model II 1.Model I开发模式 2.Model II开发模式 二. MVC模式 第二章&#xff1a;SpringMVC的入门案例 搭建SpringMVC的入门程序 1.创建新项目 2.等待加载导入坐标 3.处理xml文件和其他 导入tomcat 运…

Linux驱动开发 gpio_get_value读取输出io的电平返回值一直为0的问题

当时gpio子系统进行读取时返回必定是0 因此&#xff0c;首先必须使用platform驱动来管理gpio和pinctrl子系统&#xff0c;然后如果按照正点原子所教的设备树引脚设置为0x10B0则会导致读取到的电平值为0。 解决方法&#xff1a; 将设备树中的引脚设置为 pinctrl_gpioled: gpio…