博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SocketOutputStream和SocketChannel write方法的区别和底层实现
阅读量:6910 次
发布时间:2019-06-27

本文共 3412 字,大约阅读时间需要 11 分钟。

提到了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 JNICALLJava_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)。

转载地址:http://llbcl.baihongyu.com/

你可能感兴趣的文章
无法连接LINUX中的MYSQL
查看>>
HTTPS时代的到来是大势所趋!阿里云CDN如何助力企业网站进入HTTPS时代
查看>>
Linux 积极使用swap空间
查看>>
安装zibbix
查看>>
设计缓存系统该注意的问题
查看>>
svn服务器搭建
查看>>
[官方翻译]RabbitMQ生产上线前准备
查看>>
Haskell开发以太坊智能合约
查看>>
C++除零异常
查看>>
css的兼容问题汇总
查看>>
android apk 防止反编译技术第五篇-完整性校验(转)
查看>>
ios优秀开发者笔记汇总
查看>>
CSS 异步加载技术 不影响页面渲染
查看>>
我的友情链接
查看>>
angular学习资源
查看>>
我的友情链接
查看>>
Python [3] optparse、sys、hashlib模块
查看>>
等待事件之Log File Sync
查看>>
DML并行度限制
查看>>
python mix-in
查看>>