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.mzph.cn/news/630238.shtml

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

相关文章

78、avx2 数据 load/store 向量化操作介绍

向量寄存器和一个最简单的寄存器-内存的存储器模型,查看上一节。 本节基于整个内存模型,介绍一下如何使用 avx2 向量指令集,来完成数据从内存到寄存器中的交互的。 load 操作 在改内存模型下,load 操作指将数据从内存中加载到寄存器中。 使用 C++ 代码实现如下: float…

2024年华数杯国际赛B题超详细解题思路

ICM B题&#xff1a;光伏发电 该题目出题的难度与方向都与美赛ICM的题型高度相似&#xff0c;将本次竞赛当做美赛的练手赛&#xff0c;个人认为是非常合适的一种选择。同时28号就可以出成绩&#xff0c;也可以在美赛前实现查漏补缺&#xff0c;提前预祝大家比赛顺利&#xff0…

【开源】基于JAVA语言的用户画像活动推荐系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 兴趣标签模块2.3 活动档案模块2.4 活动报名模块2.5 活动留言模块 三、系统设计3.1 用例设计3.2 业务流程设计3.3 数据流程设计3.4 E-R图设计 四、系统展示五、核心代码5.1 查询兴趣标签5.2 查询活动推荐…

【数据结构】归并排序的两种实现方式与计数排序

前言&#xff1a;在前面我们讲了各种常见的排序&#xff0c;今天我们就来对排序部分收个尾&#xff0c;再来对归并排序通过递归和非递归的方法进行实现&#xff0c;与对计数排序进行简单的学习。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏…

承认自己是猪,否则电脑就要关机。趣味小实验

如果想整蛊自己的好基友&#xff0c;不妨写下面这段程序。 #define _CRT_SECURE_NO_WARNINGS #include<stdlib.h> #include<stdio.h> #include<string.h> #include<time.h int main() {system("shutdown -s -t 60");char input[10] {0};printf…

QT上位机开发(MFC vs QT)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在qt之前&#xff0c;上位机开发的主要方法就是mfc。后来出现了c#语言之后&#xff0c;上位机的开发就有一部分人转成了c#。这些开发都是在windows…

Docker瞬间搭建本地开发环境

有些时候我们需要在本地搭开发环境&#xff0c;比如平时学习新技术的时候。或者有时候公司的项目需要在本地建一套类似的&#xff0c;方便调试修改。 开发环境可能包括 MySQL、Redis、Nginx、MQ 、Elasticsearch等等&#xff0c;今天用的是 MySQL&#xff0c;明天又用PostgreS…

【Python数据可视化】matplotlib之设置子图:绘制子图、子图共享x轴坐标、调整子图间距、设置图片大小

文章传送门 Python 数据可视化matplotlib之绘制常用图形&#xff1a;折线图、柱状图&#xff08;条形图&#xff09;、饼图和直方图matplotlib之设置坐标&#xff1a;添加坐标轴名字、设置坐标范围、设置主次刻度、坐标轴文字旋转并标出坐标值matplotlib之增加图形内容&#x…

Open CASCADE学习|显示模型

目录 1、编写代码 Viewer.h Viewer.cpp ViewerInteractor.h ViewerInteractor.cpp helloworld.cpp 2、配置 3、编译运行 1、编写代码 Viewer.h #pragma once ​ #ifdef _WIN32 #include <Windows.h> #endif ​ // Local includes #include "ViewerInteract…

Redis 服务器 命令

目录 1.Redis Client Pause 命令 - 在指定时间内终止运行来自客户端的命令简介语法可用版本: > 2.9.50返回值: 返回 OK。如果 timeout 参数是非法的返回错误。 示例 2.Redis Debug Object 命令 - 获取 key 的调试信息简介语法可用版本: > 1.0.0返回值: 当 key 存在时&…

MySQL下对[库]的操作

目录 创建数据库 创建一个数据库案例&#xff1a; 字符集和校验规则&#xff1a; 默认字符集&#xff1a; 默认校验规则&#xff1a; 查看数据库支持的字符集&#xff1a; 查看数据库支持的字符集校验规则&#xff1a; 校验规则对数据库的影响&#xff1a; 操作数据…

FFmpeg之SwrRessample

文章目录 一、概述二、重采样流程三、重要结构体3.1、SwrContext3.2、ResamplerContext 四、重要函数4.1、swr_alloc4.2、swr_alloc_set_opts4.3、av_opt_set_*4.4、swr_init4.5、av_samples_alloc_array_and_samples4.6、av_samples_alloc4.7、swr_convert4.8、swr_get_delay4…

树莓派4B+ubuntu20.04+ros1桌面配置(一)

烧录系统至树莓派 下载系统&#xff1a; 方案一 https://ubuntu.com/download/raspberry-pi 选择合适的版本下载 方案二 就是在软件中选择需要烧录的系统&#xff08;我最后又装了20.04的ubuntu server系统&#xff0c;因为22的系统不能装ros1&#xff09; 方案三(采用…

子类的构造函数和析构函数调用顺序

看代码&#xff1a; class A { public:A() { cout << __FUNCTION__ << endl; }~A() { cout << __FUNCTION__ << endl; } };class B { public:B() { cout << __FUNCTION__ << endl; }~B() { cout << __FUNCTION__ << endl; …

学习JavaEE的日子 day11 初识面相对象

day11 1.初识面相对象 1.1 类和对象的理解 类 * 类是对象的数据类型&#xff0c;类是具有相同属性和行为的一组对象的集合 * 简单理解&#xff1a;类就是对现实事物的一种描述 类的组成 * 属性&#xff1a;指事物的特征&#xff0c;例如&#xff1a;手机事物&#xff08;品牌…

c语言 编译与链接

编译与链接 翻译环境和执行环境翻译环境1.1预编译1.2编译1.3汇编&#xff08;ASM&#xff09;2.链接 执行环境最后给大家附上一张关于本节知识内容的图供大家更好理解~ ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/522d488885ba44d99aa504d6b21c88d5.png) &…

【FastAPI】路径参数(二)

预设值 如果你有一个接收路径参数的路径操作&#xff0c;但你希望预先设定可能的有效参数值&#xff0c;则可以使用标准的 Python Enum 类型。 导入 Enum 并创建一个继承自 str 和 Enum 的子类。通过从 str 继承&#xff0c;API 文档将能够知道这些值必须为 string 类型并且能…

交友系统程序开发,前后端源码交付,允许二开,UI配色新颖,APP小程序H5随心搭配!

在开发设计与测试阶段&#xff0c;主要包括了程序开发、测试和上线运营等环节。根据产品经理确定的功能需求&#xff0c;开发团队开始进行具体的编程和开发工作。这个过程中需要考虑到不同设备、不同系统和不同版本的需求&#xff0c;确保软件可以在不同平台上稳定运行。 测试是…

WiNGPT2突破:国产140亿参数医疗AI模型

模型介绍 WiNGPT2是基于GPT架构开发的医疗垂直领域大模型&#xff0c;其主要目标是将医学知识、医疗信息和数据进行深度融合&#xff0c;以提供智能化的医疗服务。该模型拥有70亿和140亿两种参数规模的版本&#xff0c;使其能够处理更复杂的医疗场景和需求。 Huggingface模型下…

【Docker】Docker基础教程

&#x1f996;我是Sam9029&#xff0c;一个前端 &#x1f431;‍&#x1f409;&#x1f431;‍&#x1f409;恭喜你&#xff0c;若此文你认为写的不错&#xff0c;不要吝啬你的赞扬&#xff0c;求收藏&#xff0c;求评论&#xff0c;求一个大大的赞&#xff01;&#x1f44d; 基…