Java中的Socket你了解吗

☆* 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("处理请求。。。。");// 获取等待处理的SelectionKeyIterator<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中移除当前使用的keykeyIterator.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 {// 获取ChannelSocketChannel sc = (SocketChannel) key.channel();// 获取附加到事件的缓冲区ByteBuffer buffer = (ByteBuffer) key.attachment();buffer.clear(); // 清空缓冲区,准备读取数据// 从客户端通道读取数据到缓冲区,如果返回-1表示客户端关闭连接if (sc.read(buffer) == -1) {// 关闭channelsc.close();} else {// 切换buffer为读模式buffer.flip();// 将buffer中的数据解码为字符串后保存到receivedStringString 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.rhkb.cn/news/240282.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32F103标准外设库——RCC时钟(六)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

GitLab Runner 实现项目 CI/CD 发布

Gitlab Runner简介 Gitlab实现CICD的方式有很多&#xff0c;比如通过Jenkins&#xff0c;通过Gitlab Runner等&#xff0c;今天主要介绍后者。Gitlab在安装的时候&#xff0c;就默认包含了Gitlab CI的能力&#xff0c;但是该能力只是用于协调作业&#xff0c;并不能真的去执行…

【计算机网络】网络层——详解IP协议

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】 本专栏旨在分享学习计算机网络的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 &#x1f431;一、I…

力扣电话号码的组合

文章目录 题目说明做题思路代码实现代码解析 题目链接 题目说明 首先我们先分析一下这个题目题目中说呢先给出一个字符串这个字符串其实就是这个九键数字我们要按照要求将数字所代表的字符进行自由组合形成一个字符串并且这个字符串的长度和输入的数字字符串长度相同&#xff0…

华为端口安全常用3种方法配置案例

安全动态mac地址学习功能 [Huawei]int g0/0/01 interface GigabitEthernet0/0/1 port-security enable //开启安全 port-security max-mac-num 2 //最多为2个mac地址学习 port-security protect-action restrict //丢包带警告 port-security aging-time 1 //mac地址的老化时间…

FFmpeg连载6-音频重采样

今天我们的实战内容是将音频解码成PCM&#xff0c;并将PCM重采样成特定的采样率&#xff0c;然后输出到本地文件进行播放。 什么是重采样&#xff1f; 所谓重采样&#xff0c;一句话总结就是改变音频的三元素&#xff0c;也就是通过重采样改变音频的采样率、采样格式或者声道数…

Github 2024-01-16 Python开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-01-16统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目10HTML项目1 精心策划的Python资源列表 创建周期&#xff1a;3490 天开发语言&#xff1a;Python…

在 ASP.NET Core Web API 中使用异常筛选器捕获和统一处理异常

前言 在 ASP.NET Core Web API 中&#xff0c;异常筛选器&#xff08;Exception Filter&#xff09;是一种用于处理发生在 Web API 控制器或管道中的异常的机制。 异常筛选器可以捕获和处理应用程序中发生的异常&#xff0c;当系统中出现未经处理的异常的时候&#xff0c;异常…

小程序基础学习(事件处理)

原理&#xff1a;组件内部设置点击事件&#xff0c;然后冒泡到页面捕获点击事件 在组件内部设置点击事件 处理点击事件&#xff0c;并告诉页面 页面捕获点击事件 页面处理点击事件 组件代码 <!--components/my-info/my-info.wxml--> <view class"title"…

基于Springboot的网上点餐系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的网上点餐系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&am…

docker-compose和docker compose的区别

在docker实际使用中&#xff0c;经常会搭配Compose&#xff0c;用来定义和运行多个 Docker 容器。使用时会发现&#xff0c;有时候的指令是docker-compose&#xff0c;有时候是docker compose&#xff0c;下面给出解释。 docker官方文档&#xff1a;https://docs.docker.com/c…

iphone 5s的充电时序原理图纸,iPAD充电讲解

上一篇写了iphone 5的时序。那是电池供电的开机时序。iphone 5s也是差不多的过程&#xff0c;不说了。现在看iphone5s手机充电时候的时序。iphone5s充电比iphone5充电简单了很多。 首先是usb接口接到手机上&#xff0c;usb线连接到J7接口上。J7接口不只是接usb&#xff0c;还能…

[NSSCTF Round#16 Basic]RCE但是没有完全RCE

RCE但是没有完全RCE wp 题目代码&#xff1a; 第一关 <?php error_reporting(0); highlight_file(__file__); include(level2.php); if (isset($_GET[md5_1]) && isset($_GET[md5_2])) {if ((string)$_GET[md5_1] ! (string)$_GET[md5_2] && md5($_GET[m…

IntelliJ IDEA使用学习

一、安装教程 网上自行下载&#xff0c;CSDN不然过审二、使用教程 2.1 快捷键操作与设置 设置 Setting——>按键映射——>选择顺手的系统快捷键 编写代码 CtrlShift Enter&#xff0c;语句完成。 “&#xff01;”&#xff0c;否定完成&#xff0c;输入表达式时按 …

现代控制理论基础

在学习卡尔曼滤波、粒子滤波、隐马尔可夫模型时候&#xff0c;经常会提到状态方程的概念&#xff0c;这边联想到当时学习过的一门课程现代控制理论&#xff0c;这边就简单回顾一下吧。在回顾之前&#xff0c;串联下高等数学中微分方程的知识点。 一. 微分方程 高等数学上册第…

C++ 最短路总结 朴素Dijkstra算法 || 模版题,求最短路

算法选择&#xff1a; 稠密图用邻接矩阵写&#xff0c;稀疏图用邻接表写。 朴素dijkstra&#xff1a; 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为正值。 请你求出 1 号点到 n 号点的最短距离&#xff0c;如果无法从 1 号点…

Win10电脑关闭OneDrive自动同步的方法

在Win10电脑操作过程中&#xff0c;用户想要关闭OneDrive的自动同步功能&#xff0c;但不知道具体要怎么操作&#xff1f;首先用户需要打开OneDrive&#xff0c;然后点击关闭默认情况下将文档保存到OneDrive选项保存&#xff0c;最后关闭在这台电脑上同步设置保存就好了。接下来…

认识监控系统zabbix

利用一个优秀的监控软件&#xff0c;我们可以: ●通过一个友好的界面进行浏览整个网站所有的服务器状态 ●可以在 Web 前端方便的查看监控数据 ●可以回溯寻找事故发生时系统的问题和报警情况 了解zabbix zabbix是什么&#xff1f; ●zabbix 是一个基于 Web 界面的提供分布…

Android Studio下载gradle反复失败

我的版本&#xff1a;gradle-5.1.1 首先检查设置路径是否正确&#xff0c;参考我的修改&#xff01; 解决方案 1.手动下载Gradle.bin Gradle Distributions 下载地址 注意根据编译器提示下载&#xff0c;我这要求下载的是bin 而不是all 2.把下载好的整个压缩包放在C:\Users\…

uni-app的学习【第三节】

五 运行环境判断与跨端兼容 uniapp为开发者提供了一系列基础组件,类似HTML里的基础标签元素,但uni-app的组件与HTML不同,而是与小程序相同,更适合手机端使用。 虽然不推荐使用 HTML 标签,但实际上如果开发者写了`div`等标签,在编译到非H5平台时也会被编译器转换为 `view`…