[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[备忘]java调用socket的write时,发生了什么

上一篇:[备忘]No buffer space available (maximum connections reached
下一篇:[备忘]关于MTU,MSS

添加日期:2022/4/17 23:39:35 快速返回   返回列表 阅读438次
write时调用native方法,windows的话,找到这个:

https://github.com/JetBrains/jdk8u_jdk/blob/master/src/windows/native/java/net/SocketOutputStream.c


/*
 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include <windows.h>
#include <winsock2.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>

#include "java_net_SocketOutputStream.h"

#include "net_util.h"
#include "jni_util.h"

/************************************************************************
 * SocketOutputStream
 */
static jfieldID IO_fd_fdID;

/*
 * Class:     java_net_SocketOutputStream
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL
Java_java_net_SocketOutputStream_init(JNIEnv *env, jclass cls) {
    IO_fd_fdID = NET_GetFileDescriptorID(env);
}

/*
 * 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;

    if (IS_NULL(fdObj)) {
        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
        return;
    } else {
        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
    }
    if (IS_NULL(data)) {
        JNU_ThrowNullPointerException(env, "data argument");
        return;
    }

    /*
     * Use stack allocate buffer if possible. For large sizes we allocate
     * an intermediate buffer from the heap (up to a maximum). If heap is
     * unavailable just use our stack buffer.
     */
    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;

        (*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);
        if ((*env)->ExceptionCheck(env)) {
            break;
        } else {
            while(llen > 0) {
                int n = send(fd, bufP + loff, llen, 0);
                if (n > 0) {
                    llen -= n;
                    loff += n;
                    continue;
                }

                /*
                 * Due to a bug in Windows Sockets (observed on NT and Windows
                 * 2000) it may be necessary to retry the send. The issue is that
                 * on blocking sockets send/WSASend is supposed to block if there
                 * is insufficient buffer space available. If there are a large
                 * number of threads blocked on write due to congestion then it's
                 * possile to hit the NT/2000 bug whereby send returns WSAENOBUFS.
                 * The workaround we use is to retry the send. If we have a
                 * large buffer to send (>2k) then we retry with a maximum of
                 * 2k buffer. If we hit the issue with <=2k buffer then we backoff
                 * for 1 second and retry again. We repeat this up to a reasonable
                 * limit before bailing out and throwing an exception. In load
                 * conditions we've observed that the send will succeed after 2-3
                 * attempts but this depends on network buffers associated with
                 * other sockets draining.
                 */
                if (WSAGetLastError() == WSAENOBUFS) {
                    if (llen > MAX_BUFFER_LEN) {
                        buflen = MAX_BUFFER_LEN;
                        chunkLen = MAX_BUFFER_LEN;
                        llen = MAX_BUFFER_LEN;
                        continue;
                    }
                    if (retry >= 30) {
                        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
                            "No buffer space available - exhausted attempts to queue buffer");
                        if (bufP != BUF) {
                            free(bufP);
                        }
                        return;
                    }
                    Sleep(1000);
                    retry++;
                    continue;
                }

                /*
                 * Send failed - can be caused by close or write error.
                 */
                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);
    }
}




net_util_md.h
---------------------------
#define MAX_BUFFER_LEN          2048
#define MAX_HEAP_BUFFER_LEN     65536



所以,输出的字节数>2048的话,会拆为多次输出。
单次最大65536字节。
每次输出调用send方法,这个是系统api。

找到这个:
WinSock2.h
---------------------------
send(
    _In_ SOCKET s,
    _In_reads_bytes_(len) const char FAR * buf,
    _In_ int len,
    _In_ int flags
    );
#endif /* INCL_WINSOCK_API_PROTOTYPES */

具体说明可以参考这个:
https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send

大概意思就是:send方法可能阻塞,因为要等待底层缓冲区的空间。
然后程序不要多线程调用send方法,消息会乱。

底层是谁?网卡吗?


 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved