Java中的Socket你了解吗

news2024/9/24 13:19:55

☆* o(≧▽≦)o *☆嗨~我是小奥🍹
📄📄📄个人博客:小奥的博客
📄📄📄CSDN:个人CSDN
📙📙📙Github:传送门
📅📅📅面经分享(牛客主页):传送门
🍹文章作者技术和水平有限,如果文中出现错误,希望大家多多指正!
📜 如果觉得内容还不错,欢迎点赞收藏关注哟! ❤️

文章目录

  • Java中的Socket你了解吗?
    • 1. 普通Socket
      • (1) Server
      • (2) Client
      • (3) 结果演示
    • 2. NioSocket
      • (1) Channel
      • (2) Buffer
      • (3) Selector
      • (4) NioSocket

Java中的Socket你了解吗?

Java中的Socket可以分为普通SocketNioSocket两种。

1. 普通Socket

Java中的网络通信是通过Socket实现的。Socket分为ServerSocketSocket两大类 。

  • ServerSocket用于服务端,可以通过accept方法监听请求,监听请求后返回Socket
  • Socket用于具体完成数据传输,客户端直接使用Socket发起请求并传输数据;

(1) Server

ServerSocket的使用可以分为三步:

  • 创建ServerSocketServerSocket的构造方法一共有5个,用起来最方便的是ServerSocket(int port),只需要一个port(端口号)即可。
  • 调用创建的ServerSocketaccept方法进行监听accept方法是阻塞方法,也就是调用该方法后程序会停下来等待连接请求,不会继续执行,当接收到请求后accept方法会返回一个Socket
  • 使用accept方法返回的Socket与客户端进行通信。

一个ServerSocket简单使用示例

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @Author zal
 * @Date 2024/01/15  20:12
 * @Description: ServerSocket
 * @Version: 1.0
 */
public class Server {
    public static void main(String[] args) {
        try {
            // 创建一个ServerSocket监听8080端口
            ServerSocket server = new ServerSocket(8080);
            // 等待请求
            Socket socket = server.accept();
            // 接收到请求后使用socket进行通信,创建BufferedReader用于读取数据
            BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line = is.readLine();
            System.out.println("received from client: " + line);
            // 创建PrintWriter,用于发送数据
            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            pw.println("received data:" + line);
            pw.flush();
            // 关闭资源 
            pw.close();
            socket.close();
            server.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在上述的代码实现中,先创建了ServerSocket,然后调用accept方法等待请求,当接收到请求后,用返回的Socket创建ReaderWriter来接收和发送数据,Reader接收到数据后保存到line,然后打印到控制台,再将数据发送到client

(2) Client

Socket的使用也是一样的:

  • 创建Socket。使用Socket(String host, int port),把目标主机地址和端口号传给Socket;
  • Socket创建的过程就会跟服务端建立连接,然后进行通信即可。

一个Socket的简单使用示例

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * @Author zal
 * @Date 2024/01/15  20:27
 * @Description: Client
 * @Version: 1.0
 */
public class Client {
    public static void main(String[] args) {
        String msg = "Client Data";
        try {
            // 创建一个Socket。跟本机的8080端口连接
            Socket socket = new Socket("127.0.0.1", 8080);
            // 使用Socket创建的PrintWriter和BufferedReader进行读写数据
            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            // 发送数据
            pw.println(msg);
            pw.flush();
            // 接收数据
            String line = is.readLine();
            System.out.println("received from server:" + line);
            // 关闭资源
            pw.close();
            is.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在上述的代码实现中,创建Socket将msg发送给服务端,然后再接收服务端返回的数据并打印到控制台,最后释放资源关闭连接。

(3) 结果演示

先启动Server然后启动Clinet就可以完成一次通信。

Server运行结果:

在这里插入图片描述

Client运行结果:

在这里插入图片描述

2. NioSocket

从JDK1.4开始,Java增加了新的IO模式 —— nio(new IO),nio在底层采用了新的处理方式,极大地提高了IO的效率。

我们使用的Socket也是IO的一种,nio也提供了相应的工具:ServerSocketChannelSocketChannel,它们分别对应原来的ServerSocketSocket

想要理解NioSocket必须先理解三个概念:BufferChannelSelector

我们可以先举一个例子:

之前的送货上门的服务,过程是有客户打电话预约服务,然后服务人员就去处理,提供上门服务,然后完成服务后就继续等待电话,等待下一次服务。(我们假设只有一个服务人员)

这种模式其实就相当于普通Socket的处理请求的模式,是阻塞式的,每次只能处理一个请求。

但是当有很多请求时,这种模式的弊端就很明显了。

现在的电商网站配送都是以快递的形式,快递会有很多件汇总在一起,进行出库、分拣,并且还要经历中转站,中转站会有分拣员将同一区域的快件给区分开,最后到达每一个快递点。

这样的方式效率就很高了,这种模式就相当于NioSocket的处理模式,Buffer就是要送快件,Channel就是快递送货员,Selector就是中转站的分拣员。

下面我们来介绍一下它们的概念。

(1) Channel

channel 有一点类似于 stream,它就是读写数据的双向通道,可以从 channel 将数据读入 buffer,也可以将 buffer 的数据写入 channel,而之前的 stream 要么是输入,要么是输出,channel stream 更为底层。

channel
buffer

常见的 Channel 有

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

(2) Buffer

Buffer则用来缓冲读写数据,Buffer里面有四个属性非常重要。

  • capacity容量,也就是Buffer最多可以保存元素的数量,在创建时设置,使用过程中不可以改变。
  • limit可以使用的上限,开始创建时limitcapacity的值相同,如果给limit设置一个值之后,limit就变成了最大可以访问的值,其值不可以超过capacity
  • position当前所操作元素所在的索引位置position从0开始,随着getput方法自动更新;
  • mark用来暂时保存position的值position保存到mark后就可以修改并进行相关的操作,操作完后可以通过reset方法将mark的值恢复到position

这四个属性的大小关系是:mark <= position <= limit <= capacity

常见的 buffer 有

  • ByteBuffer
    • MappedByteBuffer
    • DirectByteBuffer
    • HeapByteBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
  • CharBuffer

(3) Selector

selector 单从字面意思不好理解,需要结合服务器的设计演化来理解它的用途。

多线程版设计

多线程版
socket1
thread
socket2
thread
socket3
thread

多线程版设计缺点:

  • 内存占用高
  • 线程上下文切换成本高
  • 只适合连接数少的场景

线程池版设计

线程池版
socket1
thread
socket2
thread
socket3
socket4

线程池版设计缺点:

  • 阻塞模式下,线程仅能处理一个 socket 连接
  • 仅适合短连接场景

selector 版设计

selector 的作用就是配合一个线程来管理多个 channel,获取这些 channel 上发生的事件,这些 channel 工作在非阻塞模式下,不会让线程吊死在一个 channel 上。适合连接数特别多,但流量低的场景(low traffic)

selector 版
selector
thread
channel
channel
channel

调用 selector 的 select() 会阻塞直到 channel 发生了读写就绪事件,这些事件发生,select 方法就会返回这些事件交给 thread 来处理。

(4) NioSocket

介绍完这三大组件,我们再来学习如何使用NioSocket

NioSocket的使用可以分为五步:

  • 创建ServerSocketChannel并设置相应参数
  • 创建Selector并注册到ServerSocketChannel
  • 调用Selectorselect方法等待请求
  • Selector接收到请求后使用selectedKeys返回selectionKey集合
  • 使用SelectionKey获取到ChannelSelector和操作类型并进行具体操作

创建ServerSocketChannel

ServerSocketChannel可以使用自己的静态工厂方法open获取。

每个ServerSocketChannel对应一个ServerSocket,可以调用其socket方法获取,但是要注意,需要通过configureBlocking()方法来设置是否采用阻塞模式,设置了非阻塞模式之后才能调用register方法注册Selector使用。(另外,阻塞模式不能使用Selector

创建Selector

Selector可以通过其静态工厂方法open创建,创建后通过Channelregister注册到ServerSocketChannel或者SocketChannel上,注册完成之后可以通过select方法来等待请求。

select方法有一个long类型参数,代表最长等待时间。如果在这段时间内接收到相应操作的请求则可以返回处理的请求的数量,否则超时后返回0.

SelectionKey

SelectionKey保存了处理当前请求的Channel和Selector,并且提供了不同的操作类型。

  • SelectionKey.OP_ACCEPT 接收请求操作
  • SelectionKey.OP_CONNECT 连接操作
  • SelectionKey.OP_READ 读操作
  • SelectionKey.OP_WRITE 写操作

只有在register方法中注册了相应的操作Selector才会关心相应类型操作的请求。

现在我们将普通SocketServer改写成使用Nio方式进行处理的NioServer,代码如下:

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.nio.charset.Charset;
import java.util.Iterator;

/**
 * @Author zal
 * @Date 2024/01/15  21:10
 * @Description: NioServer
 * @Version: 1.0
 */
public class NioServer {
    public static void main(String[] args) throws Exception {
        // 创建ServerSocketChannel,并监听8080端口
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress(8080));
        // 设置为非阻塞模式
        ssc.configureBlocking(false);
        // 为ssc注册选择器
        Selector selector = Selector.open();
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        // 创建处理器
        Handler handler = new Handler(1024);
        while (true) {
            // 等待请求,每次等待阻塞3s,超过3s后线程继续向下执行,如果传入0或者不传参数则一直阻塞
            if (selector.select(3000) == 0) {
                System.out.println("等待请求超时。。。。");
                continue;
            }
            System.out.println("处理请求。。。。");
            // 获取等待处理的SelectionKey
            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                try {
                    // 接收连接请求 
                    if (key.isAcceptable()) {
                        handler.handleAccept(key);
                    }
                    // 读数据 
                    if (key.isReadable()) {
                        handler.handleRead(key);
                    }
                } catch (IOException ex) {
                    keyIterator.remove();
                    continue;
                }
                // 处理完成后,从待处理的SelectionKey中移除当前使用的key
                keyIterator.remove();
            }
        }
    }

    /**
     * 静态内部类,用于处理连接和读取数据
     */
    private static class Handler {
        private int bufferSize = 1024;
        private String localCharset = "UTF-8";

        public Handler() {
        }

        public Handler(int bufferSize) {
            this(bufferSize, null);
        }

        public Handler(String localCharset) {
            this(-1, localCharset);
        }

        public Handler(int bufferSize, String localCharset) {
            // 如果指定了有效的缓冲区大小,则使用指定值
            if (bufferSize > 0) {
                this.bufferSize = bufferSize;
            }
            // 如果指定了有效的字符集,则使用指定值
            if (localCharset != null) {
                this.localCharset = localCharset;
            }
        }

        /**
         * 处理接受连接事件
         * @param key
         * @throws IOException
         */
        public void handleAccept(SelectionKey key) throws IOException {
            // 通过服务器套接字通道接受客户端连接
            SocketChannel sc = ((ServerSocketChannel) key.channel()).accept();
            // 配置为非阻塞模式
            sc.configureBlocking(false);
            // 将客户端套接字通道注册到选择器,关注事件为可读,同时附带一个缓冲区
            sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
        }

        /**
         * 处理读取数据事件
         * @param key
         * @throws IOException
         */
        public void handleRead(SelectionKey key) throws IOException {
            // 获取Channel
            SocketChannel sc = (SocketChannel) key.channel();
            // 获取附加到事件的缓冲区
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            buffer.clear(); // 清空缓冲区,准备读取数据
            // 从客户端通道读取数据到缓冲区,如果返回-1表示客户端关闭连接
            if (sc.read(buffer) == -1) {
                // 关闭channel
                sc.close();
            } else {
                // 切换buffer为读模式
                buffer.flip();
                // 将buffer中的数据解码为字符串后保存到receivedString
                String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();
                System.out.println("received from client: " + receivedString);

                // 返回数据给客户端
                String sendString = "received data: " + receivedString;
                buffer = ByteBuffer.wrap(sendString.getBytes(localCharset));
                sc.write(buffer);
                sc.close();
            }
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

stable diffusion使用相关

IP Adapter&#xff0c;我愿称之它为SD垫图 IP Adapter是腾讯lab发布的一个新的Stable Diffusion适配器&#xff0c;它的作用是将你输入的图像作为图像提示词&#xff0c;本质上就像MJ的垫图。 IP Adapter比reference的效果要好&#xff0c;而且会快很多&#xff0c;适配于各种…

任务14:使用MapReduce提取全国每年最低/最高气温

任务描述 知识点&#xff1a; 使用MapReduce提取数据 重 点&#xff1a; 开发MapReduce程序统计每年每个月的最低气温统计每年每个月的最高气温 内 容&#xff1a; 使用IDEA创建一个MapReduce项目开发MapReduce程序使用MapReduce统计每年每个月的最低气温使用MapReduce…

基于SSM的网上招聘系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

OpenCV-22高斯滤波

一、高斯函数的基础 要理解高斯滤波首先要直到什么是高斯函数&#xff0c;高斯函数是符合高斯分布的&#xff08;也叫正态分布&#xff09;的数据的概率密度函数。 高斯函数的特点是以x轴某一点&#xff08;这一点称为均值&#xff09;为对称轴&#xff0c;越靠近中心数据发生…

Ubuntu12.0安装g++过程及其报错

Ubuntu12.0安装g过程及其报错 https://blog.csdn.net/weixin_51286763/article/details/120703953 https://blog.csdn.net/dingd1234/article/details/124029945 2.报错二&#xff1a; [41/80] Building CXX object absl/synchronization/CMakeFiles/graphcycles_internal.di…

Java集合之LinkedList源码篇

☆* o(≧▽≦)o *☆嗨~我是小奥&#x1f379; &#x1f4c4;&#x1f4c4;&#x1f4c4;个人博客&#xff1a;小奥的博客 &#x1f4c4;&#x1f4c4;&#x1f4c4;CSDN&#xff1a;个人CSDN &#x1f4d9;&#x1f4d9;&#x1f4d9;Github&#xff1a;传送门 &#x1f4c5;&a…

微信小程序定义并获取日志/实时log信息

步骤一&#xff1a;开通实时日志 可以在开发者工具->详情->性能质量->实时日志&#xff0c;点击前往&#xff0c;在浏览器打开we分析界面&#xff1a; 也可登录小程序管理后台&#xff0c;点击统计进入we分析&#xff1a; 在we分析界面找到性能质量&#xff0c;打开实…

读书笔记——《未来简史》

前言 《未来简史》是以色列历史学家尤瓦尔赫拉利的人类简史三部曲之一。三部分别为《人类简史》《未来简史》《今日简史》。其中最为著名的当然是《人类简史》&#xff0c;非常宏大的一本关于人类文明历史的书籍&#xff0c;绝对可以刷新历史观&#xff0c;《人类简史》这本书…

Linux知识(未完成)

一、Linux 1.1 Linux 的应用领域 1.1.1 个人桌面领域的应用 此领域是 Linux 比较薄弱的环节但是随着发展&#xff0c;近几年 linux 在个人桌面领域的占有率在逐渐提高 1.1.2 服务器领域 linux 在服务器领域的应用是最高的 linux 免费、稳定、高效等特点在这里得到了很好的…

区间预测 | Matlab实现LSSVM-ABKDE的最小二乘支持向量机结合自适应带宽核密度估计多变量回归区间预测

区间预测 | Matlab实现LSSVM-ABKDE的最小二乘支持向量机结合自适应带宽核密度估计多变量回归区间预测 目录 区间预测 | Matlab实现LSSVM-ABKDE的最小二乘支持向量机结合自适应带宽核密度估计多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现…

MATLAB - 使用运动学 DH 参数构建机械臂

系列文章目录 前言 一、 使用 Puma560 机械手机器人的 Denavit-Hartenberg (DH) 参数&#xff0c;逐步建立刚体树形机器人模型。在连接每个关节时&#xff0c;指定其相对 DH 参数。可视化机器人坐标系&#xff0c;并与最终模型进行交互。 DH 参数定义了每个刚体通过关节与其父…

QT图表-折线图、曲线图

时间记录&#xff1a;2024/1/15 一、使用步骤 1.添加图表模块 .pro项目管理文件中添加charts模块 QChart类&#xff1a;图表类 QChartView类&#xff1a;图表显示类 2.ui文件中添加QChartView组件 &#xff08;1&#xff09;选择一个QGrapicsView组件将其拖拽到ui界面上合适位…

小程序基础学习(缓存)

目录 设置用户信息缓存(同步): wx.setStorageSync(key,value) ​编辑 获取用户信息缓存(同步): wx.getStorageSync(key) 删除用户信息缓存(同步): wx.removeStorageSync(key) 清空用户信息缓存(同步): wx.clearStorageSync() 设置用户信息缓存加密信息(异步): wx.setStorage(…

计算机导论05-计算机网络

文章目录 计算机网络基础计算机网络概述计算机网络的概念计算机网络的功能计算机网络的组成 计算机网络的发展计算机网络的类型 网络体系结构网络互联模型OSI/RM结构与功能TCP/IP结构模型TCP/IP与OSI/RM的比较 网络地址与分配IP地址构成子网的划分IPv6 传输介质与网络设备网络传…

JS | JS调用EXE

JS | JS调用EXE 网上洋洋洒洒一大堆文章提供,然我还是没找打合适的方案: 注册表方案做了如下测试(可行但是不推荐?): 先,键入文件名为 myprotocal.reg 的注册表,并键入一下信息: Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\openExe] //协议名…

产品标识为什么要选择激光打标机

选择激光打标机作为产品标识的原因主要有以下几点&#xff1a; ​ 1. 独特的标识效果&#xff1a;激光打标机采用激光束照射在材料表面&#xff0c;通过控制激光束的运动轨迹和能量密度&#xff0c;可以在材料表面形成各种文字、图案和二维码等标识。这些标识具有永久性、耐磨、…

硅双通道光纤低温等离子体蚀刻控制与SiGe表面成分调制

引言 在过去的几年中&#xff0c;MOSFET结构从平面结构改变为鳍型结构(FinFETs ),这改善了短沟道效应&#xff0c;并导致更高的驱动电流泄漏。然而&#xff0c;随着栅极长度减小到小于20nm&#xff0c;进一步小型化变得越来越困难&#xff0c;因为它需要非常窄的鳍宽度&#x…

深度学习笔记(七)——基于Iris/MNIST数据集构建基础的分类网络算法实战

文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解&#xff0c;如有遗漏或错误&#xff0c;欢迎评论或私信指正。 截图和程序部分引用自北京大学机器学习公开课 认识网络的构建结构 在神经网络的构建过程中&#xff0c;都避不开以下几个步骤&#xff1a; 导入网络和依…

pytorch集智-5手写数字识别器-卷积神经网络

1 简介 简称&#xff1a;CNN&#xff0c;convolutional neural network 应用场景&#xff1a;图像识别与分类&#xff08;CNN&#xff09;&#xff0c;看图说话&#xff08;CNNRNN&#xff09;等 优越性&#xff1a;和多层感知机相比&#xff0c;cnn可以识别独特的模式&…

设计模式—行为型模式之中介者模式

设计模式—行为型模式之中介者模式 中介者模式(Mediator Pattern)&#xff1a;用一个中介对象来封装一系列的对象交互&#xff0c;中介者使各对象不需要显式地相互引用&#xff0c;减少对象间混乱的依赖关系&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之…