Socket通信与WebSocket协议

文章目录

目录

文章目录

前言

一、Socket通信

1.1 BIO

1.2 NIO

1.3 AIO

二、WebSocket协议

总结



前言


 

一、Socket通信

Socket是一种用于网络通信的编程接口(API),它提供了一种机制,使不同主机之间可以通过网络进行数据传输和通信。Socket是支持TCP/IP协议栈的应用层与传输层之间的接口。

在Socket编程中,有两种常见的角色:客户端和服务器端。客户端负责发起连接请求,服务器端负责接收并处理连接请求。

Socket通信的基本流程如下:

  1. 服务器端创建一个ServerSocket对象,并指定一个端口号。该对象会监听该端口上的连接请求。
  2. 客户端创建一个Socket对象,并指定要连接的服务器的IP地址和端口号。
  3. 客户端使用Socket对象发起连接请求,向服务器发送连接请求。
  4. 服务器监听到连接请求后,通过accept()方法接受客户端的连接,创建一个Socket对象与客户端建立连接。
  5. 客户端和服务器端通过各自的Socket对象进行数据的读取和写入,实现双向的数据交换。
  6. 数据交换完成后,可以关闭连接。客户端和服务器端都可以使用close()方法关闭自己的Socket对象。

通过Socket编程,可以实现不同设备之间的网络通信。例如,可以使用Socket编程来开发基于TCP/IP的客户端-服务器应用、聊天程序、文件传输程序等。

需要注意的是,Socket编程只提供了底层的网络通信接口,对于数据的格式、协议、解析等需要自行定义和处理。在Java中,可以使用Java标准库中的java.net.Socketjava.net.ServerSocket来实现Socket编程。

socket编程步骤:

  1. 服务器监听:服务器启动后,它会有一个线程一直启动,等待着客户端端连接。它会定义好自己的端口号。
  2. 客户端请求:客户端端套接字提出连接请求,要连接的目标是服务端的套接字。客户端必须要指明服务端套接字的地址和端口号。
  3. 连接确认:当服务端收到客户端的连接请求就会响应客户端套接字的请求,建立一个新的线程处理客户端的请求。

1.1 BIO

BIO(Blocking I/O)是Java中的一种阻塞式I/O模型,也称为传统的I/O模型。在BIO中,每个I/O操作都会阻塞当前线程,直到数据准备好或者操作完成。

BIO的工作原理如下:

  1. 服务器端创建一个ServerSocket对象并监听指定的端口。
  2. 服务器通过accept()方法等待客户端发起连接请求。
  3. 当有客户端连接请求到达时,服务器通过accept()方法接受客户端的连接,并返回一个新的Socket对象。
  4. 服务器使用新的Socket对象与客户端进行数据的读取和写入。
  5. 客户端使用Socket对象与服务器进行通信,发送请求并接收响应。
  6. 服务器端和客户端通过读写操作进行数据的交互,但是这些操作都是阻塞的,直到数据完全发送或接收完毕。

BIO的特点:

  1. 阻塞:BIO的I/O操作是阻塞的,当没有数据可读或写时,线程会一直阻塞在相应的读写操作上,无法去处理其他任务。
  2. 线程池限制:由于每个连接都需要独占一个线程进行处理,当并发连接数很大时,线程资源会被耗尽,导致性能下降。
  3. 可靠性:由于阻塞的特性,BIO在网络不稳定或出现异常时可能会导致程序挂起或阻塞。

虽然BIO具有易于理解和使用的优点,但其在高并发应用场景下的性能较差。随着网络应用的发展,为了提高性能和扩展性,非阻塞I/O模型如NIO(New I/O)和异步I/O模型如AIO(Asynchronous I/O)逐渐成为主流。

需要注意的是,BIO仍然适用于某些特定的应用场景,特别是在连接数较少且对实时性要求不高的情况下。

服务器端

package com.rcg.testtwo;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author :1452863884@qq.com rcg
 * @date :Created in 2023/8/24 14:47
 * @description:服务端
 * @modified By:
 * @version:
 */
public class BioServer {
    public static void main(String[] args) {
        //定义端口号
        int port = 9999;
        //定义服务器套接字
        ServerSocket serverSocket = null;
        try {
            //创建服务器套接字
            serverSocket = new ServerSocket(port);
            //一直监听,是否有客户端请求过来
            while (true) {
                //每次都会新建一个线程,来处理接收到到请求
                Socket socket = serverSocket.accept();
                //每次都会新建一个线程,来处理接收到到请求
                new Thread(new SocketHandler(socket)).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //如果服务器套接字不为空,则关闭
            if (serverSocket!= null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                serverSocket = null;
            }
        }
    }
    static class SocketHandler implements Runnable {
        //定义socket
        Socket socket = null;
     public SocketHandler(Socket socket) {
         this.socket = socket;
     }
     //处理读取的数据
     @Override
        public void run() {
            BufferedReader reader = null;
            PrintWriter writer = null;
            try {
                //读取数据,BIO 是面向流到,所以定义流 BufferedReader 来读取数据
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                //将收到的数据返回给客户端
                writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
                String readMessage = null;
                //循环读取数据
                while (true) {
                    if ((readMessage = reader.readLine()) == null) {
                        break;
                    }
                    System.out.println("server reading........" + readMessage);
                    //将数据返回给客户端
                    writer.println("server recive : " + readMessage);
                    writer.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (socket!= null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    socket = null;
                }
                if (reader!= null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    reader = null;
                }
                if (writer!= null) {
                    writer.close();
                    writer = null;
                }
            }
        }
    }
}

客户端

package com.rcg;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author :1452863884@qq.com rcg
 * @date :Created in 2023/8/24 14:54
 * @description:客户端
 * @modified By:
 * @version:
 */
public class BioClient {
    public static void main(String[] args) {
        //服务端到 ip 地址
        String host = "127.0.0.1";
        //和服务端到端口号一致
        int port = 9999;
        Socket socket = null;
        BufferedReader reader = null;
        PrintWriter writer = null;
        //接收键盘输入数据
        Scanner scanner = new Scanner(System.in);
        try {
            //创建 socket 对象
            socket = new Socket(host, port);
            //创建 BufferedReader 对象
            String message = null;
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
            writer = new PrintWriter(socket.getOutputStream(), true);
            //循环接收数据
            while (true) {
                message = scanner.nextLine();
                //如果接收到 exit 则退出
                if (message.equals("exit")) {
                    break;
                }
                //数据发送服务端
                writer.println("客户端输入:" + message);
                writer.flush();
                //接收服务端的响应
                System.out.println(reader.readLine());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭 socket 对象
            if (socket!= null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                socket = null;
            }
            //关闭 BufferedReader 对象
            if (reader!= null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                reader = null;
            }
            //关闭 PrintWriter 对象
            if (writer!= null) {
                writer.close();
                writer = null;
            }
        }
    }
}

效果 

 

 

 

 

 

1.2 NIO

NIO(New I/O)是Java中提供的一种非阻塞式I/O模型,与传统的阻塞式I/O(BIO)相比,NIO能更高效地处理I/O操作和并发连接。

NIO的关键组件包括通道(Channel),缓冲区(Buffer)、选择器(Selector)和非阻塞模式。下面对每个组件进行简要介绍:

  1. 通道(Channel):通道是数据源和目标之间的连接,可以用于读取和写入数据。在NIO中,所有I/O操作都是通过通道进行的。不同类型的通道,如文件通道、套接字通道等,适用于不同的I/O场景。

  2. 缓冲区(Buffer):缓冲区是一个连续的内存块,用于存储数据。它使得读取和写入数据更加高效。在NIO中,所有数据的读取和写入都是通过缓冲区进行的。

  3. 选择器(Selector):选择器是用于检测通道上的事件的对象。通过选择器,可以实现单个线程管理多个通道,从而高效地处理并发连接。选择器可以监控通道上的事件类型,如接受连接、读取数据、写入数据等,并根据事件的发生情况来执行相应的操作。

  4. 非阻塞模式:NIO使用非阻塞模式进行通信。在非阻塞模式下,当一个通道没有数据可读取时,线程不会被阻塞,而是可以继续处理其他任务。这样可以避免每个连接都需要独占一个线程的资源浪费问题,实现更高效的并发连接处理。

NIO的工作原理如下:

  1. 服务器创建一个选择器,并将其注册到一个或多个通道上。
  2. 当有事件发生(如连接、读取、写入等),选择器会通过轮询的方式检测到事件的发生。
  3. 当事件发生时,选择器会返回一个包含已就绪事件的键集合,程序可以通过这些键来获取感兴趣的事件和相应的通道。
  4. 程序根据事件类型执行相应的操作,如接受连接、读取数据、写入数据等。

NIO相比于BIO具有以下优点:

  1. 高并发性:NIO使用选择器和非阻塞模式,能够高效地处理并发连接,减少线程的使用,提高系统的并发性能。
  2. 可扩展性:NIO支持单线程管理多个通道,适用于需要管理大量连接的场景,提供了更好的可扩展性。
  3. 非阻塞式:NIO采用非阻塞模式,不会因为一个通道的读写操作导致阻塞,可以同时处理多个通道的操作,提高了系统的响应速度。

需要注意的是,NIO的实现相对复杂,需要合理地使用和管理缓冲区、处理事件等。在Java中,可以使用java.nio包下的类来进行NIO编程,如SelectableChannelByteBufferSelector等。

总的来说,NIO是一种更高效和灵活的I/O模型,适用于需要处理大量并发连接的场景,如网络服务器、聊天程序、游戏服务器等。

服务器端

package com.rcg.testtwo;

/**
 * @author :1452863884@qq.com rcg
 * @date :Created in 2023/8/24 15:49
 * @description:
 * @modified By:
 * @version:
 */

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NioServer {
    private static ByteBuffer readBuf = ByteBuffer.allocate(1024);
    private static ByteBuffer writeBuf = ByteBuffer.allocate(1024);

    public static void main(String[] args) {
        int port = 9999;
        Selector selector;
        try {
            //打开多路复用器
            selector = Selector.open();
            //定义一个 Channel
            ServerSocketChannel channel = ServerSocketChannel.open();
            //非阻塞模型
            channel.configureBlocking(false);
            //绑定端口号
            channel.bind(new InetSocketAddress(port));
            //channel 注册到 Selector 上面
            channel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务已经启动了......");
            while (true) {
                //复用器开始监听
                selector.select();
                Iterator<SelectionKey> seletionKeys = selector.selectedKeys().iterator();
                while (seletionKeys.hasNext()) {
                    SelectionKey key = seletionKeys.next();
                    if (key.isValid()) {
                        //监听客户端第一次端连接信息
                        if (key.isAcceptable()) {
                            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                            SocketChannel sc = ssc.accept();
                            sc.configureBlocking(false);
                            sc.register(selector, SelectionKey.OP_READ);
                            System.out.println("accept a client : " + sc.socket().getInetAddress().getHostName());
                        } else {
                            read(key);
                        }
                    }
                    seletionKeys.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void read(SelectionKey key) {
        try {
            //清空缓冲区
            readBuf.clear();
            //获取注册在通道里面的 key 对象
            SocketChannel sc = (SocketChannel) key.channel();
            //读取数据
            int count = sc.read(readBuf);
            //如果没有数据,则关闭连接
            if (count == -1) {
                key.channel().close();
                key.cancel();
                return;
            }
            //有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
            readBuf.flip();
            //6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
            byte[] bytes = new byte[readBuf.remaining()];
            //7 接收缓冲区数据
            readBuf.get(bytes);
            String body = new String(bytes).trim();
            System.out.println("服务端接受到客户端请求的数据: " + body);
            //9 告诉客户端已收到数据
            writeBuf.put(("你好,客户端,我已收到数据:" + body).getBytes());
            //对缓冲区进行复位
            writeBuf.flip();
            //写出数据到服务端
            sc.write(writeBuf);
            //清空缓冲区数据
            writeBuf.clear();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

客户端

package com.rcg;

/**
 * @author :1452863884@qq.com rcg
 * @date :Created in 2023/8/24 15:53
 * @description:
 * @modified By:
 * @version:
 */
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NioClient {
    private static final String host = "127.0.0.1";
    private static final Integer port = 9999;
    public static void main(String[] args) {
        try {
            //创建一个SocketChannel对象
            SocketChannel channel = SocketChannel.open();
            //连接服务端
            channel.connect(new InetSocketAddress(host, port));
            //设置为非阻塞模式
            channel.configureBlocking(false);
            //创建一个Selector对象
            Selector selector = Selector.open();
            //注册channel,可读,可写
            channel.register(selector, SelectionKey.OP_READ, SelectionKey.OP_WRITE);
            //开启新的线程监听,否则的话,客户端在控制台输入信息不好操作。
            new Thread(() -> {
                while (true) {
                    try {
                        //多路复用器开始监听
                        selector.select();
                        Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
                        while (selectionKeys.hasNext()) {
                            SelectionKey selectionKey = selectionKeys.next();
                            if (selectionKey.isValid() && selectionKey.isReadable()) {
                                //建立写缓冲区
                                ByteBuffer readBuf = ByteBuffer.allocate(1024);
                                //2 获取之前注册的socket通道对象
                                SocketChannel sc = (SocketChannel) selectionKey.channel();
                                //3 读取数据
                                int count = sc.read(readBuf);
                                if (count == -1) {
                                    selectionKey.channel().close();
                                    selectionKey.cancel();
                                    return;
                                }
                                //5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
                                readBuf.flip();
                                //6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
                                byte[] bytes = new byte[readBuf.remaining()];
                                //7 接收缓冲区数据
                                readBuf.get(bytes);
                                String body = new String(bytes).trim();
                                System.out.println("收到服务端的数据:" + body);
                            }
                            //移除未处理的key
                            selectionKeys.remove();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            //设置缓冲区大小
            ByteBuffer writebuf = ByteBuffer.allocate(1024);
            while (true) {
                byte[] bytes = new byte[1024];
                System.in.read(bytes);
                //把数据放到缓冲区中
                writebuf.put(bytes);
                //对缓冲区进行复位
                writebuf.flip();
                //写出数据到服务端
                channel.write(writebuf);
                //清空缓冲区数据
                writebuf.clear();
            }
        } catch (
                IOException e) {
            e.printStackTrace();
        }
    }
}

 测试

 

1.3 AIO

AIO(Asynchronous I/O)是Java中提供的一种异步I/O模型,与传统的阻塞式I/O(BIO)和非阻塞式I/O(NIO)相比,AIO能更高效地处理I/O操作和并发连接。

AIO的关键组件包括通道(Channel)、缓冲区(Buffer)和完成通知机制。下面对每个组件进行简要介绍:

  1. 通道(Channel):通道是数据源和目标之间的连接,可以用于读取和写入数据。在AIO中,所有I/O操作都是通过通道进行的。不同类型的通道,如文件通道、套接字通道等,适用于不同的I/O场景。

  2. 缓冲区(Buffer):缓冲区是一个连续的内存块,用于存储数据。它使得读取和写入数据更加高效。在AIO中,所有数据的读取和写入都是通过缓冲区进行的。

  3. 完成通知机制:AIO使用回调和事件驱动的方式来处理I/O操作。当一个I/O操作完成时,操作系统会通知应用程序,并触发预先注册的回调函数,从而进行相应的处理。这种机制可以避免线程阻塞,提高系统的并发性能。

AIO的工作原理如下:

  1. 服务器创建一个通道,并注册一个或多个感兴趣的I/O事件和回调函数。
  2. 当有I/O事件发生时,操作系统会通知应用程序,并调用相应的回调函数。
  3. 在回调函数中,应用程序可以获取已完成的I/O操作的结果,并进行相应的处理,如读取数据、写入数据等。

AIO相比于BIO和NIO具有以下优点:

  1. 异步性:AIO通过使用回调和事件驱动的方式,实现真正的异步I/O操作。这意味着应用程序无需等待操作完成,而是可以继续执行其他任务,提高了系统的并发性能。

  2. 简化编程模型:AIO的异步特性可以简化编程模型,避免了繁琐的线程管理和同步操作。开发者只需要关注回调函数的处理即可,让操作系统来处理底层的I/O操作。

  3. 高性能:由于AIO的异步特性,可以充分利用系统资源,提供更高的并发性能和吞吐量。

需要注意的是,AIO在Java中是通过AsynchronousChannelCompletionHandler来实现的。可以使用java.nio.channels.AsynchronousChannelGroupjava.nio.channels.AsynchronousServerSocketChannel等类来创建和管理异步通道,使用java.nio.channels.CompletionHandler来定义回调函数。

总的来说,AIO适用于需要处理大量并发连接且对性能要求较高的场景。然而,AIO在某些平台上的性能可能不如NIO,具体取决于操作系统和硬件的支持程度。在选择使用AIO还是NIO时,需要考虑特定的应用需求和目标平台的特性。

服务端

package com.rcg.testtwo;

/**
 * @author :1452863884@qq.com rcg
 * @date :Created in 2023/8/24 16:02
 * @description:
 * @modified By:
 * @version:
 */
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AioServer {
    private final int port;
    public static void main(String[] args) {
        // 创建一个端口号为9999的AioServer对象
        int port = 9999;
        // 创建一个AioServer对象,并传入端口号
        new AioServer(port);
    }
    public AioServer(int port) {
        // 将端口号传入AioServer对象
        this.port = port;
        // 监听
        listen();
        // 循环
        while (true) {
            try {
                // 线程休眠1000000毫秒
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
            }
        }
    }
    private void listen() {
        try {
            ExecutorService executorService = Executors.newCachedThreadPool();
            AsynchronousChannelGroup threadGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);
            //创建一个异步的服务端套接字
            final AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open(threadGroup);
            //绑定端口
            serverSocketChannel.bind(new InetSocketAddress(port));
            System.out.println("服务已经启动,监听端口:" + port);
            //监听
            serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
                final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                @Override
                public void completed(AsynchronousSocketChannel result, Object attachment) {
                    System.out.println("I/O 操作成功,开始获取数据");
                    try {
                        //清空缓冲区
                        byteBuffer.clear();
                        //从异步缓冲区中读取数据
                        result.read(byteBuffer).get();
                        //将缓冲区的数据转换为字符串
                        byteBuffer.flip();
                        System.out.println("服务端接收到数据:" + new String(byteBuffer.array()).trim());
                        //将数据写入异步缓冲区
                        result.write(byteBuffer);
                        //将缓冲区的数据转换为字符串
                        byteBuffer.flip();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            //关闭异步套接字
                            result.close();
                            //接收下一个操作
                            serverSocketChannel.accept(null, this);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("操作完成");
                }
                @Override
                public void failed(Throwable exc, Object attachment) {
                    System.out.println("I/O 操作失败:" + exc);
                }
            });
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

客户端

package com.rcg;

/**
 * @author :1452863884@qq.com rcg
 * @date :Created in 2023/8/24 16:02
 * @description:
 * @modified By:
 * @version:
 */
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class AioClient {
    private AsynchronousSocketChannel clientChannel;
    // 定义一个AsynchronousSocketChannel类型的变量clientChannel
    private static final String host = "127.0.0.1";
    // 定义一个字符串类型的变量host
    private static final Integer port = 9999;
    // 定义一个整型类型的变量port
    public AioClient() throws Exception {
        // 创建一个AsynchronousSocketChannel实例
        clientChannel = AsynchronousSocketChannel.open();
    }
    // 定义一个AioClient类的构造函数
    public void connect(String host, int port) {
        try {
            // 调用AsynchronousSocketChannel的open()方法,创建一个AsynchronousSocketChannel实例
            clientChannel.connect(new InetSocketAddress(host, port), null, new CompletionHandler<Void, Void>() {
                // 定义一个CompletionHandler类型的变量attachment,用于存放连接结果
                @Override
                public void completed(Void result, Void attachment) {
                    try {
                        // 调用AsynchronousSocketChannel的write()方法,发送数据
                        clientChannel.write(ByteBuffer.wrap("你好师姐,客户端链接成功了".getBytes())).get();
                        System.out.println("数据已经发送成功!");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                // 定义一个CompletionHandler类型的变量attachment,用于存放连接失败的异常
                @Override
                public void failed(Throwable exc, Void attachment) {
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            // 等待1秒
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 定义一个ByteBuffer准备读取数据
        final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        // 调用AsynchronousSocketChannel的read()方法,读取数据
        clientChannel.read(byteBuffer, null, new CompletionHandler<Integer, Object>() {
            // 定义一个CompletionHandler类型的变量attachment,用于存放读取结果
            @Override
            public void completed(Integer result, Object attachment) {
                System.out.println("I/O操作完成" + result);
                System.out.println("获取返回结果:" + new String(byteBuffer.array()).trim());
            }
            // 定义一个CompletionHandler类型的变量attachment,用于存放读取失败的异常
            @Override
            public void failed(Throwable exc, Object attachment) {
                exc.printStackTrace();
            }
        });
    }
    // 定义一个main()方法,用于连接服务器
    public static void main(String[] args) throws Exception {
        // 创建一个AioClient实例
        AioClient aioClient = new AioClient();
        // 调用AioClient的connect()方法,连接服务器
        aioClient.connect(host, port);
    }
}

 

测试

 

 

 

二、WebSocket协议

WebSocket是一种在Web浏览器和服务器之间进行全双工通信的协议。它提供了一种持久连接的机制,允许客户端和服务器之间实时地交换数据,而不需要频繁地发起HTTP请求。

与传统的HTTP请求相比,WebSocket连接通过一个初始的HTTP握手阶段建立,并使用一种特殊的数据帧格式来传输数据。一旦建立了WebSocket连接,客户端和服务器之间可以随时互相发送消息,这样就实现了实时的双向数据交流。

WebSocket具有以下特点:

  1. 实时性:WebSocket连接保持持久性,客户端和服务器之间可以实时地发送和接收数据,避免了短轮询或长轮询的延迟。
  2. 双向通信:WebSocket连接支持全双工通信,即客户端和服务器可以同时发送和接收数据。
  3. 轻量级:WebSocket协议采用了更轻量级的数据帧格式,相对于HTTP请求来说,数据传输的开销更小。
  4. 更少的资源消耗:由于WebSocket连接的持久性,服务器端不需要为每个客户端连接创建一个新的线程或进程,从而减少了服务器资源的消耗。
  5. 跨域支持:WebSocket连接支持跨域通信,可以在不同域名下的客户端和服务器之间进行通信。

WebSocket广泛用于实时聊天应用、多人游戏、实时数据传输等需要高实时性和双向通信的Web应用场景。在前端开发中,可以使用JavaScript提供的WebSocket API来创建和管理WebSocket连接。在后端开发中,可以使用各种语言和框架提供的WebSocket库来处理WebSocket连接和消息的收发。

要在Web应用程序中使用WebSocket,你需要在客户端和服务器端分别进行相应的代码编写。下面是一个示例,展示了如何在JavaScript和Java中使用WebSocket。

客户端

// 创建WebSocket对象并指定服务器的URL
var socket = new WebSocket('ws://localhost:8080/mywebsocket');

// 连接建立时触发事件
socket.onopen = function(event) {
  console.log('WebSocket连接已建立');
  
  // 向服务器发送消息
  socket.send('Hello Server!');
};

// 收到服务器消息时触发事件
socket.onmessage = function(event) {
  var message = event.data;
  console.log('收到服务器消息:' + message);
  
  // 在此处对收到的消息进行处理
};

// 连接关闭时触发事件
socket.onclose = function(event) {
  console.log('WebSocket连接已关闭');
};

// 发生错误时触发事件
socket.onerror = function(event) {
  console.error('WebSocket出现错误');
};

服务器端(使用Java):

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint("/mywebsocket")
public class MyWebSocket {

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("WebSocket连接已建立");
        
        // 向客户端发送消息
        try {
            session.getBasicRemote().sendText("Hello Client!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("收到客户端消息:" + message);
        
        // 在此处对收到的消息进行处理
        
        // 向客户端发送消息
        try {
            session.getBasicRemote().sendText("Got your message: " + message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnClose
    public void onClose() {
        System.out.println("WebSocket连接已关闭");
    }

    @OnError
    public void onError(Throwable error) {
        System.err.println("WebSocket发生错误");
        error.printStackTrace();
    }
}

 在示例中,客户端使用JavaScript的WebSocket对象创建WebSocket连接,并通过相应的事件处理函数来处理连接建立、消息收发、连接关闭和错误等事件。服务器端使用Java的javax.websocket库,使用@ServerEndpoint注解指定WebSocket的URL,并编写相应的方法来处理连接建立、消息收发和连接关闭等事件。


总结

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

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

相关文章

【校招VIP】CSS校招考点之水平/垂直居中

考点介绍&#xff1a; 前端布局非常重要的一环就是页面框架的搭建&#xff0c;也是最基础的一环。在页面框架搭建之中&#xff0c;又有居中布局/多列布局/全局布局。今天介绍一下居中布局的水平居中和垂直居中。 『CSS校招考点之水平/垂直居中』相关题目及解析内容可点击文章末…

学习左耳听风栏目90天——第八天 8/90(学习左耳朵耗子的工匠精神,对技术的热爱)【答疑解惑:渴望、热情和选择】

答疑解惑&#xff1a;渴望、热情和选择 如何抽出时间学习&#xff1f;1. 要热爱学习&#xff0c;渴望学习&#xff0c;从学习中获取快乐 如何抽出时间学习&#xff1f; 1. 要热爱学习&#xff0c;渴望学习&#xff0c;从学习中获取快乐

linux服务TCP参数配置

Linux TCP参数配置 阿里云规范 1.【推荐】高并发服务器建议调小 TCP 协议的 time_wait 超时时间。 说明&#xff1a;操作系统默认 240 秒后&#xff0c;才会关闭处于 time_wait 状态的连接&#xff0c;在高并发访问下&#xff0c;服务器端会因为处于 time_wait 的连接数太多&am…

消息队列——RabbitMQ(一)

MQ的相关概念 什么事mq MQ(message queue)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是 message 而已&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递消息。在互联网架构中&#xff…

创建web应用程序,React和Vue怎么选?

React和Vue都是创建web应用程序的绝佳选择。React得到了科技巨头和庞大的开源社区的支持&#xff0c;代码库可以很大程度地扩展&#xff0c;允许你创建企业级web应用程序。React拥有大量合格甚至优秀的开发人员粉丝&#xff0c;可以解决你在开发阶段可能遇到的任何问题。 毫无疑…

OpenEuler华为欧拉系统安装

OpenEuler华为欧拉系统安装 一、OpenEuler简介1、OpenEuler概述2、OpenEuler特性 二、OpenEuler部署安装1、安装前配置2、安装引导3、选择语言4、安装信息摘要 三、欧拉系统安装图形化界面1、需要在超级管理员&#xff0c;在root权限下操作2、启动图形化界面 四、手动安装VMwar…

Go framework-Kratos

一、Go framework 框架Github开源时间开源方Kratoshttps://github.com/go-kratos/kratos2019Bilibiligo-kithttps://github.com/go-kit/kit/2015团队开源go-zerohttps://github.com/tal-tech/go-zero2020团队开源TarsGohttps://github.com/TarsCloud/TarsGo2018腾讯Jupiterhtt…

计算机竞赛 基于人工智能的图像分类算法研究与实现 - 深度学习卷积神经网络图像分类

文章目录 0 简介1 常用的分类网络介绍1.1 CNN1.2 VGG1.3 GoogleNet 2 图像分类部分代码实现2.1 环境依赖2.2 需要导入的包2.3 参数设置(路径&#xff0c;图像尺寸&#xff0c;数据集分割比例)2.4 从preprocessedFolder读取图片并返回numpy格式(便于在神经网络中训练)2.5 数据预…

Linux 定时任务 crontab 用法学习整理

一、linux版本 lsb_release -a 二、crontab 用法学习 2.1&#xff0c;crontab 简介 linux中crontab命令用于设置周期性被执行的指令&#xff0c;该命令从标准输入设备读取指令&#xff0c;并将其存放于“crontab”文件中&#xff0c;以供之后读取和执行。cron 系统调度进程。…

简述docker的网络模式

Docker 提供了多种网络模式&#xff0c;用于控制容器之间以及容器与主机之间的网络通信。以下是 Docker 的一些常见网络模式 briage模式&#xff1a; docker容器启动时默认就是该模式,在该模式下&#xff0c;docker容器会连接到一个名为docker0的虚拟以太网桥上&#xff0c;通…

脑机接口里程碑!一天2篇Nature!

2023年8月23日&#xff0c;《Nature》期刊一口气发表了两项独立的脑机接口方向的研究。 一项来自加州大学旧金山分校华裔科学家张复伦团队&#xff0c;另一项来自斯坦福大学的神经科学家弗朗西斯威利特(Francis Willett)团队。两项研究都旨在帮助那些因脑损伤和疾病而失去语言能…

Ubuntu虚拟机网络无法连接的几种解决方法

虚拟机网络无法连接的几种解决方法 问题状况描述可能的解决方案 问题状况描述 Ubuntu虚拟机没有网络&#xff0c;无法ping通互联网&#xff0c;左上角网络连接图标消失等情况可能的解决方案 1.重启虚拟机网络编辑器 2.重启虚拟机网络适配器 3.重启虚拟机网络服务器1.重启网络…

day-31 代码随想录算法训练营(19)贪心part01

455.分发饼干 思路一&#xff1a;贪心思路&#xff0c;大饼干先分给大胃口 思路二&#xff1a;小饼干先分给小胃口 376.摆动序列 分析摆动&#xff1a;记 presub 为前面与当前数之差&#xff0c;lastsub 为当前与后面数之差 思路&#xff1a; 1.正常摆动时&#xff0c;需要 …

5个流程图模板网站,帮你轻松绘制专业流程图

在复杂的项目管理和团队协作中&#xff0c;流程图成为了一个必不可少的工具。从零开始创建流程图可能会很耗时&#xff0c;同时也需要一定的技能。使用模板可以让流程图方便制作又保持高颜值&#xff0c;降低制作的成本&#xff0c;一款模板众多、功能强大、具有丰富编辑工具的…

python自动化入门之Python编写脚本实现自动化爬虫详解

想知道如何使用Python轻松高效地获取网络上的信息&#xff1f; 本篇文章将探索Python自动化爬虫&#xff0c;并展示如何编写实用的脚本。 1. 什么是Python爬虫&#xff1f; 爬虫顾名思义&#xff0c;就是像蜘蛛一样在网络上爬行&#xff0c;抓取各种有用信息的一种程序。而Pyt…

Qt应用开发(拓展篇)——示波器/图表 QCustomPlot

一、介绍 QCustomPlot是一个用于绘图和数据可视化的Qt C小部件。它没有进一步的依赖关系&#xff0c;提供友好的文档帮助。这个绘图库专注于制作好看的&#xff0c;出版质量的2D绘图&#xff0c;图形和图表&#xff0c;以及为实时可视化应用程序提供高性能。 QCustomPl…

【点云分割】points3d框架学习01 —— 安装和配置

安装 $ pip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://download.pytorch.org/whl/cu113 $ pip install torch-points3d $ pip install ipython $ pip install trame $ pip install h5py $ pip install gdown案例 from to…

Docker拉取并配置Grafana

Linux下安装Docker请参考&#xff1a;Linux安装Docker 安装准备 新建挂载目录 /opt/grafana/data目录&#xff0c;准备用来挂载放置grafana的数据 /opt/grafana/plugins目录&#xff0c;准备用来放置grafana的插件 /opt/grafana/config目录&#xff0c;准备用来挂载放置graf…

Python Opencv实践 - 图像直方图自适应均衡化

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/cat.jpg", cv.IMREAD_GRAYSCALE) print(img.shape)#整幅图像做普通的直方图均衡化 img_hist_equalized cv.equalizeHist(img)#图像直方图自适应均衡化 #1. 创…

yolov5的xml文件转txt文件格式(详细解释与完整代码供应)

文章目录 前言一、yolov5训练数据格式介绍1、txt的类别对应说明2、txt的文件说明3、txt文件格式3、yolov5训练文件形式 二、xml文件读取代码解读三、xml文件转txt文件1、xml转txt代码解读2、保存txt文件代码解读 四、完整代码 前言 本文章实现xml数据格式转yolov5的txt格式&am…