SocketOutputStream和SocketChannel write方法的区别和底层实现

2023-11-18

Java直接内存原理提到了SocketChannel#write的实现原理。
通过IOUtil#write将java堆内存拷贝到了直接内存,然后再把地址传给了I/O函数。
那么 BIO 是怎么实现往socket里面写数据的呢?

BIO

Socket#getOutputStream()获得SocketOutputStream
三个write方法最后都会调用native方法SocketOutputStream#socketWrite0

SocketOutputStream.c#Java_java_net_SocketOutputStream_socketWrite0方法

/*
 * Class:     java_net_SocketOutputStream
 * Method:    socketWrite
 * Signature: (Ljava/io/FileDescriptor;[BII)V
 */
JNIEXPORT void JNICALL
Java_java_net_SocketOutputStream_socketWrite0(JNIEnv *env, jobject this,
                                              jobject fdObj, jbyteArray data,
                                              jint off, jint len) {
    char *bufP; //中间临时缓冲区
    char BUF[MAX_BUFFER_LEN]; //如果堆栈可以存下,直接使用堆栈内存
    int buflen; //缓冲区大小,就是需要发送的数据大小
    int fd;

    /* 省略,入参校验*/

    /*
     * 尽可能使用堆栈分配缓冲区。
     * 对于large sizes,我们从堆中分配一个中间缓冲区(达到最大值)。
     * 如果堆不可用,我们只使用的堆栈缓冲区。
     */
    if (len <= MAX_BUFFER_LEN) {
        bufP = BUF;
        buflen = MAX_BUFFER_LEN;
    } else {
        buflen = min(MAX_HEAP_BUFFER_LEN, len);
        bufP = (char *)malloc((size_t)buflen);
        if (bufP == NULL) {
            bufP = BUF;
            buflen = MAX_BUFFER_LEN;
        }
    }

    while(len > 0) {
        int loff = 0;
        int chunkLen = min(buflen, len);
        int llen = chunkLen;
        int retry = 0;
        /*
         * 这个方法复制了java数组到native堆中!!!
         */
        (*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);
		/*
         * 由于Windows套接字中的错误(在NT和Windows 2000上观察到),可能需要重试发送。
         */
        while(llen > 0) {
        	/* 循环调用发送*/
            int n = send(fd, bufP + loff, llen, 0);
            if (n > 0) {
                llen -= n;
                loff += n;
                continue;
            }

            /*
             * 由于Windows套接字中的错误(在NT和Windows 2000上观察到),可能需要重试发送。
             */
            if (WSAGetLastError() == WSAENOBUFS) {
                /* 省略,失败重试机制*/
            }

            /*
             * 发送失败 - 可能由关闭或写入错误引起。
             */
            if (WSAGetLastError() == WSAENOTSOCK) {
                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
            } else {
                NET_ThrowCurrent(env, "socket write error");
            }
            /* 释放临时缓冲区内存*/
            if (bufP != BUF) {
                free(bufP);
            }
            return;
        }
        len -= chunkLen;
        off += chunkLen;
    }
	/* 释放临时缓冲区内存*/
    if (bufP != BUF) {
        free(bufP);
    }
}

jni.cpp宏定义

#define DEFINE_GETSCALARARRAYREGION(ElementTag,ElementType,Result, Tag) \
  DT_VOID_RETURN_MARK_DECL(Get##Result##ArrayRegion);\
\
JNI_ENTRY(void, \
jni_Get##Result##ArrayRegion(JNIEnv *env, ElementType##Array array, jsize start, \
             jsize len, ElementType *buf)) \
  /* 省略,动态判断应该去调用byte、int等哪个方法;还有一些动态追踪的逻辑?*/
  typeArrayOop src = typeArrayOop(JNIHandles::resolve_non_null(array)); \
  if (start < 0 || len < 0 || ((unsigned int)start + (unsigned int)len > (unsigned int)src->length())) { \
    THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); \
  } else { \
    if (len > 0) { \
      int sc = TypeArrayKlass::cast(src->klass())->log2_element_size(); \
      /* 内存拷贝*/
      memcpy((u_char*) buf, \
             (u_char*) src->Tag##_at_addr(start), \
             len << sc);                          \
    } \
  } \
JNI_END

所以除了直接使用ByteBuffer#allocateDirect分配堆外内存之外,不管是BIO和NIO都需要将java堆内存拷贝到native堆(堆外内存)。
当然都不能避免从native堆拷贝到socket buffer(SO_RCVBUF和SO_SNDBUF)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SocketOutputStream和SocketChannel write方法的区别和底层实现 的相关文章

  • 使用IDEA实现java生成随机验证码

    package test200 checkCode 需求 定义方法实现随机产生一个5位的验证码 每位可能是数字 大写字母 小写字母 分析 定义一个方法 生成验证码返回 方法参数是位数 方法的返回值类型是String 在方法内部使用for循环
  • Java NIO 的前生今世 之二 NIO Channel 小结

    Java NIO Channel 通常来说 所有的 NIO 的 I O 操作都是从 Channel 开始的 一个 channel 类似于一个 stream java Stream 和 NIO Channel 对比 我们可以在同一个 Chan
  • 为什么使用start方法启动Java的Thread线程?

    一 简介 在Java代码当中 当我们需要开启子线程去处理一些任务的时候 往往是调用Thread对象的start方法 这样Thread实例中的Runnable对象的run方法就会在一个新的线程当中执行 创建一个线程 Thread thread
  • socket编程之服务器端与客户端(代码实例)

    在我们学习的过程中 对TCP IP UDP Socket编程这些词应该有所了解了 随着网络技术的发展 这些词充斥着我们的耳朵 那么我想介绍一下 什么是TCP IP UDP socket在哪里呢 socket通信是什么呢 socket接口函数
  • node socket 简易聊天室

    服务端 const net require net const server net createServer 用户列表 let clients 监听连接 server on connection client gt client on d
  • 解决前端websocket数据帧接收数据大小限制(数据分帧)问题

    websocket前后台出现问题解决方法 一开始通过限制后台返回数据帧以125字节分隔分段数据返回给前台 但调试时发现只要加上其他的一些信息返回json string很容易就会超过了125字节 于是在后台修改了这个限制大小为2048 但是这
  • 调试代码

    1 用浏览器打开需要调试的页面 2 打开调试模式 在谷歌浏览器中 点击F12 可以打开开发者模式 刷新页面就可以看到数据的传输 也可以通过鼠标右键 选择 重新加载框架 可以查到某个部分的刷新 找到调用的接口 通过该接口去原程序中找到对应的方
  • AF_INET和PF_INET的区别

    在写网络程序的时候 建立TCP socket一般是这样的 sock socket PF INET SOCK STREAM 0 然后在绑定本地地址或连接远程地址时需要初始化sockaddr in结构 其中指定address family时一般
  • Java 基本数据类型之间的运算规则

    博主前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住也分享一下给大家 点击跳转到网站 前言 这里只讨论七种基本数据类型变量间的运算 不包含boolean类型的 1 自动类型提升 结论 当容量小的数据类型的变量与容量大的数据
  • 项目3:(9)与安川控制器P3000通信模块代码

    1 common h 通信传输的IP地址 typedef struct char serverIp 16 int iPort CONNINFO 定义连接信息数据结构 定义发送数据的结构体 typedef struct double R Bo
  • golang之跨语言ipc通信

    1 golang之跨语言ipc通信 文章目录 1 golang之跨语言ipc通信 1 1 unix domain Socket unix域套接字 介绍 1 2 IPC SOCKET通信 1 2 1 函数及地址定义介绍 1 2 2 UNIX
  • .gitignore是干什么的?

    Git忽略文件 gitignore详解 在工程中 并不是所有文件都需要保存到版本库中的 例如 target 目录及目录下的文件就可以忽略 在Git工作区的根目录下创建一个特殊的 gitignore文件 然后把要忽略的文件名填进去 Git就会
  • StreamingResponseBody 中的异常处理不起作用

    我试图捕获在我的实现中抛出的异常StreamingResponseBody 我可以看到类内部抛出异常 但是我的处理似乎有效 但是我无法获取自定义消息 我的响应代码是200 GetMapping path test produces appl
  • Java 文件下载挂起

    我有一个用于下载文件的网络界面 当请求到来时 我的 glassfish 服务器从 Web 服务流式传输文件 然后将内容写入输出流 我的代码工作正常 除非文件大小变得非常大 例如超过 200 MB 它会挂起 在浏览器中显示 0 下载 并且文件
  • 如何将字符串写入 Scala Process?

    我启动并运行了一个 Scala 进程 val dir path to working dir val stockfish Process Seq wine dir stockfish 8 x32 exe val logger Process
  • 如何关闭InputStream和OutputStream?

    我使用以下代码来关闭与服务器的连接的 InputStream 和 OutputStream try if mInputStream null mInputStream close mInputStream null if mOutputSt
  • NIO SocketChannel 读取超时? [复制]

    这个问题在这里已经有答案了 如果连接建立后一段时间内没有收到数据 设置超时关闭 NIO SocketChannel 的最佳方法是什么 Either 您正在使用一个Selector 在这种情况下 您可以选择一个可以使用的超时 如果超时 sel
  • 使用 Java 发送零数据 TCP/IP 数据包

    我的目标是发送一个带有空数据字段的 TCP 数据包 以便测试与远程计算机的套接字 我正在使用 OutputStream 类的 write byte b 方法 我的尝试 outClient ClientSocket getOutputStre
  • 将输出读取到文件 Node.js

    如何将输出写入文件 我尝试代替process stdout use fs createWriteStream temp export2 json 但它不起作用 var rl readline createInterface input fs
  • 为什么在android中的输入流之后要关闭OutputStream

    我正在以这种方式从 android 连续调用我的 servlet 两次 FIRST CONNECTION URL url new URL http 172 16 32 160 8080 xyz check availability Http

随机推荐

  • ctfshow---sql注入(214-253)

    目录 web214 web215 web216 web217 web218 web219 web220 web221 web222 web223 web224 web225 web226 web227 web228 229 230 web2
  • 解决git速度慢的问题

    git clone特别慢是因为github global ssl fastly net域名被限制了 只要找到这个域名对应的ip地址 然后在hosts文件中加上ip gt 域名的映射 刷新DNS缓存便可 github com的ip 打开hos
  • 【Linux实操】vi和vim编辑器的使用(vim三种模式的切换)

    vim三种模式介绍及切换 一 Linux实操vi和vim编辑器的使用 1 正常模式 2 插入模式 编辑模式 3 命令行模式 二 vim的三种模式的相互转换 三 vi vim快捷键一览图 一 Linux实操vi和vim编辑器的使用 所有的 L
  • 获取剪贴板内容

  • MySQL命令alter add:增加表的字段

    alter add命令用来增加表的字段 alter add命令格式 alter table 表名 add字段 类型 其他 例如 在表MyClass中添加了一个字段passtest 类型为int 4 默认值为0 mysql gt alter
  • centos通过rpm包实现内核升级

    一 查看当前内核版本 root lvs uname r 3 10 0 1160 el7 x86 64 二 前往链接 elrepo获取最新的repo包 rpm import https www elrepo org RPM GPG KEY e
  • 高安全等级密码模块安全技术设计

    摘 要 随着金融 大数据等行业的普及和发展 对密码设备的依赖与日俱增 并且业内在数据安全领域提出了多方面更高的要求 例如密码模块的物理安全 抗非入侵式攻击 抗环境失效等 迫切需要更高安全等级的密码模块来支撑行业的实际应用需求 依托安全二级密
  • 【Linux命令—shell】正则表达式

    正则表达式 regular expression 描述一个字符集合的表达方式 模糊匹配 目录 1 基本正则 2 扩展正则 3 兼容的正则 perl 4 综合案例练习 1 基本正则 演示如下 2 扩展正则 注意 grep不支持扩展正则 如果需
  • Python中os.listdir和os.walk的区别

    os listdir和os walk都是获取指定目录下的文件内容 两者有一定的区别 现在举例说明 如下图所示目录结构 os walk import os def file name file site for root dirs files
  • pandas学习笔记(一)---创建dataframe的4种常用方式

    一 使用numpy创建 import pandas as pd import numpy as np df pd DataFrame np arange 16 reshape 4 4 index list abcd columns one
  • 【Python学习笔记】Python中的heapq

    Python中的heapq 1 基本介绍 堆是非线性的树形的数据结构 有两种堆 大根堆与小根堆 大根堆 树中各个父节点的值总是大于或等于任何一个子节点的值 小根堆 树中各个父节点的值总是小于或等于任何一个子节点的值 我们一般使用二叉堆来实现
  • python稳定版本是哪些_python3哪个版本稳定_后端开发

    C语言中关系表达式和逻辑表达式的值是什么 后端开发 关系表达式和逻辑表达式的值是布尔型 分别为真 true 或假 false 即0或1 但c语言没有布尔类型 以0为假 非0即真 python3哪个版本稳定 python3中3 4比较稳定 基
  • android状态栏(沉浸式状态栏,改变状态栏字体颜色,背景颜色)

    通过主题设置状态栏 在API21 android 5 0 之后 设置状态栏透明效果为半透明 并且为了保证在API19 android 4 4 正常使用 所以需要3份不同的style文件 即values v19 android 4 4之后使用
  • ajax请求,进行ajax处理后端特殊字符串

    前端传入officeId的值 将office对应ip地址传入到 function getinipaddress var officeId document getElementById officeId value var isinheri
  • Linux学习第16天:Linux设备树下的LED驱动开发:举一反三 专注专心专业

    Linux版本号4 1 15 芯片I MX6ULL 大叔学Linux 品人间百味 思文短情长 在开题之前 先说一下这次的题目 尤其是后面的 举一反三 专注专心专业 到底想给大家传递什么信息 LED驱动开发 目前为止已经学了好几种方法 包括裸
  • python实现常用数据结构

    本文基于Python实现以下几种常用的数据结构 栈 队列 优先队列 二叉树 单链表 双向链表 栈 基于List实现 class Stack 栈 def init self self arr self size 0 def push self
  • windows 10自带命令查看文件的哈希值

    windows的powershell自带了查看文件哈希值的命令 Get FileHash 文件名 Algorithm MD5 SHA1 SHA256 案例 查看文件的MD5值 查看文件的SHA1值 查看文件的SHA256值
  • springboot调整请求头大小_SpringBoot http post请求数据大小设置操作

    背景 使用http post请求方式的接口 使用request getParameter XXX 的方法获取参数的值 当数据量超过几百k的时候 接口接收不到数据或者接收为null RequestMapping value rcv metho
  • GitHub Flavored Markdown 规范

    Markdown是一种轻量级标记语言 它以纯文本形式编写文档 易读 看起来舒服 易写 语法简单 易更改 并最终以HTML格式发布 由于markdown没有明确指定语法 随着编译器不一样 实现方式有很大差异 GitHub Flavored M
  • SocketOutputStream和SocketChannel write方法的区别和底层实现

    Java直接内存原理提到了SocketChannel write的实现原理 通过IOUtil write将java堆内存拷贝到了直接内存 然后再把地址传给了I O函数 那么 BIO 是怎么实现往socket里面写数据的呢 BIO Socke