Socketchannel은 왜 비 블로킹 소켓에서도 전체 금액에 대해 항상 완료됩니까?

StackOverflow https://stackoverflow.com/questions/158121

문제

Windows에서 Sun Java VM 1.5 또는 1.6을 사용하여 비 차단 소켓을 연결합니다. 그런 다음 a를 채 웁니다 ByteBuffer 출력에 대한 메시지와 함께 write() 양말 채널에.

작성할 금액이 소켓의 TCP 출력 버퍼의 공간 양보다 큰 경우에만 쓰기가 부분적으로 완료 될 것으로 기대합니다 (이것은 직관적으로 기대하는 것입니다. 문서), 그러나 그것은 일어나는 일이 아닙니다. 그만큼 write() 언제나 여러 메가 바이트 인 경우에도 기록 된 전체 금액을보고하는 것으로 보입니다 (소켓의 SO_SNDBUF는 8KB입니다.

여기서 문제는 출력이 부분적으로 작성된 사례를 처리하는 코드를 테스트 할 수 없다는 것입니다 (관심 집합 등록 WRITE 선택기에게 그리고 a select() 나머지가 쓸 수있을 때까지 기다리기 위해), 그 경우는 결코 일어나지 않는 것 같습니다. 내가 무엇을 이해하지 못하는가?

도움이 되었습니까?

해결책

나는 당신과 비슷한 상황을 재현 할 수있었습니다. 아이러니하게도, 수신자가 데이터를 작성하는 것보다 더 빨리 소비한다고 생각합니다.

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
  public static void main(String[] args) throws Exception {
    final ServerSocket ss = new ServerSocket(12345);
    final Socket cs = ss.accept();
    System.out.println("Accepted connection");

    final InputStream in = cs.getInputStream();
    final byte[] tmp = new byte[64 * 1024];
    while (in.read(tmp) != -1);

    Thread.sleep(100000);
  }
}



import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class MyNioClient {
  public static void main(String[] args) throws Exception {
    final SocketChannel s = SocketChannel.open();
    s.configureBlocking(false);
    s.connect(new InetSocketAddress("localhost", 12345));
    s.finishConnect();

    final ByteBuffer buf = ByteBuffer.allocate(128 * 1024);
    for (int i = 0; i < 10; i++) {
      System.out.println("to write: " + buf.remaining() + ", written: " + s.write(buf));
      buf.position(0);
    }
    Thread.sleep(100000);
  }
}

위의 서버를 실행 한 다음 위의 클라이언트가 128kb의 데이터 덩어리 10 덩어리를 쓰려고 시도하면 모든 쓰기 작업이 차단하지 않고 전체 버퍼를 작성하는 것을 알 수 있습니다. 그러나 위의 서버를 연결에서 읽지 않도록 위의 서버를 수정하면 클라이언트의 첫 번째 쓰기 작업 만 128 KB를 작성하는 반면 모든 후속 쓰기는 반환됩니다. 0.

출력 서버가 연결에서 읽을 때 :

to write: 131072, written:  131072
to write: 131072, written:  131072
to write: 131072, written:  131072
...

출력 서버가 연결에서 읽지 않을 때 :

to write: 131072, written:  131072
to write: 131072, written:  0
to write: 131072, written:  0
...  

다른 팁

나는 Java의 UDP와 함께 일해 왔으며 일반적으로 Java Nio에서 정말 흥미롭고 완전히 문서화되지 않은 행동을 보았습니다. 무슨 일이 일어나고 있는지 결정하는 가장 좋은 방법은 Java와 함께 제공되는 소스를 보는 것입니다.

또한 IBM과 같은 다른 JVM 구현에서 찾고있는 것의 더 나은 구현을 찾을 수 있다는 것을 높이기 위해 베팅 할 것입니다. 그러나 직접 보지 않고는 보장 할 수 없습니다.

어디에서나 문서화 된 것을 찾을 수 없지만 IIRC [1], send ()는 a) 제공된 버퍼를 완전히 보내거나 b) 실패합니다. 부분적으로 보내지 않을 것입니다.

1] 나는 여러 WinSock 구현 (Win 3.0, Win 95, Win NT 등)을 작성 했으므로 이는 일반 소켓이 아닌 Winsock에 따른 (일반 소켓이 아닌) 동작 일 수 있습니다.

나는 믿음의 큰 도약을하고 Java의 기본 네트워크 제공 업체가 C와 동일하다고 가정 할 것입니다. SO_SNDBUF 모든 소켓에 대해. 나는 당신이 당신의 보내기 코드를 (1,100000) 루프에 넣으면 결국 요청보다 작은 값으로 성공하는 글을 얻을 것입니다.

당신은 정말로 NIO 프레임 워크를보아야합니다 미나 또는 그리즐리. 엔터프라이즈 채팅 서버에서 큰 성공을 거두었습니다. 그것은 또한에 사용됩니다 오픈 파이어 채팅 서버. Grizzly는 Sun의 Javaee 구현에 사용됩니다.

데이터를 어디에서 보내고 있습니까? 네트워크는 SO_SNDBUF와 수신기의 SO_RCVBUF와 적어도 크기가 같은 버퍼 역할을합니다. Alexander가 언급 한 것처럼 수신기의 읽기 활동에 이것을 추가하면 많은 데이터를 흡수 할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top