태그 보관물: ByteBuffer

넌 블럭(Non-blocking) 소켓채널(SocketChannel)에서 읽기(read) 타임아웃 처리하기..

자바의 기본 소켓채널(SocketChannel)의 read 메서드는 타임아웃(timeout)을 제공하지 않는다. 소켓채널의 socket() 메서드로 받아온 소켓객체에 타임아웃을 설정해도 타임아웃이 먹지 않는다. 기존의 스트림 기반의 네트웍 통신을 하는 애플리케이션이라면 조금 생소할 수도 있으나, read 타임아웃은 미션 크리티컬한 서버에서는 매우 중요한 이슈이다. 타임아웃이 없어서 연결이 종료가 안 되면 서버나 클라이언트가 동작을 멈추게 될 수도 있으니 말이다.

그래서, 넌 블럭(Non-blocking)의 read 메서드에 호출하기 전에 while문으로 둘러싸서 타임아웃 기능을 구현할 수 있다. 이 코드는 busy waiting하는 형태의 간단한 타임아웃 기능이 추가되었다.

// 20초                
long timeout = 20 * 1000;   
long stime = new Date().getTime();
long rtime = 0L;

int rvalue =0;
int rvalues = 0;
while (rtime < timeout ) {
	rtime = System.currentTimeMillis() - stime;
	rvalue = channel.read(rbuffer);
	if(rvalue == -1)
		throw new Exception("connection closed");

	rvalues = rvalues + rvalue;
	if(rtime > timeout)
		throw new Exception("read timeout exception");

	if(rvalues >= 4)
		break;
}

넌 블러킹(non-blocking) 소켓채널(SocketChannel)에서 끝까지 읽어오기..

넌 블러킹(non-blocking) 소켓채널(SocketChannel)에서 스트림을 끝까지 읽어오는 코드이다. 개인적으로 헤더 8byte, 헤더의 앞의 4byte를 전체 패킷을 길이로 선언했다. 그리고, 아래처럼 SocketChannel에서 packet handling variables와 같이 패킷을 끝까지 다 읽어 들인다.

아래 내용은 non-block으로 대용량의 바이너리 데이타를 읽어들이기 위해서 꼭 숙지해야 되는 내용중에 하나이다.

* read() 내용

    //// packet handling variables
    private int fullsize = 0;
    private int readedsize =0;
    private int minsize = 8;

    @Override
    public void read() {        
        try {
            // 읽기
            int rbytes = this.channel.read(rbuffer);

            // 연결종료
            if (rbytes == -1) {
                ((ChannelListener)this.listener).disconnected(this);
                this.close();
                return;
            }

            // 버퍼에 내용이 없는 경우
            if (rbytes == 0) {
                this.channel.configureBlocking(false);
                this.handle();
                return;
            } 

            // 읽은 바이트 갯수를 증감시킨다. 
            readedsize += rbytes;    
            rbufferList.add(this.copy(this.rbuffer, rbytes));

            // 최소 패킷의 길이에서 패킷을 전체 길이를 리턴한다. 
            if(readedsize >= minsize && this.fullsize == 0) {
                this.fullsize = this.createResultBuffer().getInt();
                for(int i=0; i< this.rbufferList.size(); i++) {
                    this.rbufferList.get(i).position(0);
                }
            }

            // 패킷을 다 읽어 들였다. 
            if(fullsize == readedsize) {
                ((ChannelListener)this.listener).arrived(this,this.createResultBuffer());
            }

            this.channel.configureBlocking(false);
            this.handle();
        } catch(Exception e) { // disconnected
            try {
                this.close();
            } catch(IOException ie) {
                new CallbackExceptionHandler().handle(ie);
            }

            ((ChannelListener)this.listener).disconnected(this);
        }
    }

Get and put unsigned values to a ByteBuffer

자바(Java)는 기본적으로 unsigned primitive type을 지원하지 않는다. 만약, C/C++을 사용하는 서버/클라이언트의 프로토콜에 unsigned 타입이 선언되어 있다면 자바에서 unsigned type에 대한 처리가 필요하다. 찾아보니, 구글님이 답을 주셨다.

Unsigned.java

package com.ronsoft.books.nio.buffers;
import java.nio.ByteBuffer;

/**
 * Utility class to get and put unsigned values to a ByteBuffer object.
 * All methods here are static and take a ByteBuffer object argument.
 * Since java does not provide unsigned primitive types, each unsigned
 * value read from the buffer is promoted up to the next bigger primitive
 * data type.  getUnsignedByte() returns a short, getUnsignedShort() returns
 * an int and getUnsignedInt() returns a long.  There is no getUnsignedLong()
 * since there is no primitive type to hold the value returned.  If needed,
 * methods returning BigInteger could be implemented.
 * Likewise, the put methods take a value larger than the type they will
 * be assigning.  putUnsignedByte takes a short argument, etc.
 *
 * @author Ron Hitchens (ron@ronsoft.com)
 * @version $Id: Unsigned.java,v 1.1 2002/02/12 22:06:44 ron Exp $
 */
public class Unsigned {
   public static short getUnsignedByte (ByteBuffer bb){
      return ((short)(bb.get() & 0xff));
   }

   public static void putUnsignedByte (ByteBuffer bb, int value){
      bb.put ((byte)(value & 0xff));
   }

   public static short getUnsignedByte (ByteBuffer bb, int position){
      return ((short)(bb.get (position) & (short)0xff));
   }

   public static void putUnsignedByte (ByteBuffer bb, int position,int value){
      bb.put (position, (byte)(value & 0xff));
   }

   // —————————————————————

   public static int getUnsignedShort (ByteBuffer bb){
      return (bb.getShort() & 0xffff);
   }

   public static void putUnsignedShort (ByteBuffer bb, int value){
      bb.putShort ((short)(value & 0xffff));
   }

   public static int getUnsignedShort (ByteBuffer bb, int position){
      return (bb.getShort (position) & 0xffff);
   }

   public static void putUnsignedShort (ByteBuffer bb, int position, int value){
      bb.putShort (position, (short)(value & 0xffff));
   }

   // —————————————————————

   public static long getUnsignedInt (ByteBuffer bb){
      return ((long)bb.getInt() & 0xffffffffL);
   }

   public static void putUnsignedInt (ByteBuffer bb, long value){
      bb.putInt ((int)(value & 0xffffffffL));
   }

   public static long getUnsignedInt (ByteBuffer bb, int position){
      return ((long)bb.getInt (position) & 0xffffffffL);
   }

   public static void putUnsignedInt (ByteBuffer bb, int position, long value){
      bb.putInt (position, (int)(value & 0xffffffffL));
   }

   // —————————————————

   public static void main (String [] argv) throws Exception {
      ByteBuffer buffer = ByteBuffer.allocate (20);

      buffer.clear();
      Unsigned.putUnsignedByte (buffer, 255);
      Unsigned.putUnsignedByte (buffer, 128);
      Unsigned.putUnsignedShort (buffer, 0xcafe);
      Unsigned.putUnsignedInt (buffer, 0xcafebabe);

      for (int i = 0; i < 8; i++) {
         System.out.println ("" + i + ": "
            + Integer.toHexString ((int)getUnsignedByte (buffer, i)));
      }

      System.out.println ("2: " + Integer.toHexString (getUnsignedShort (buffer, 2)));
      System.out.println ("4: " + Long.toHexString (getUnsignedInt (buffer, 4)));
   }
}

* Reference
http://www.javafaq.nu/java-example-code-342.html

ByteBuffer to String

아래 예는 ByteBuffer를 String으로 변환하는 예제이다.

ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put(“aabcde”.getBytes());
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String s = new String(bytes);
위가 아니라 아래처럼 해야 됩니다. ^^;;

ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put(“aabcde”.getBytes());
byte[] bytes = new byte[buffer.position()];
buffer.flip();        
buffer.get(bytes);
String s = new String(bytes);
System.out.println(s);

Convert interchangeably between a ByteBuffer and a byte array

1. create a ByteBuffer from a byte array

byte[] bytes = new byte[10];
ByteBuffer buffer = ByteBuffer.wrap(bytes); 

2. retrieve bytes between the position and limit

bytes = new byte[buffer.remaining()];
buffer.get(bytes, 0, bytes.length);
buffer.clear(); 

3. retrieve all bytes in the buffer

bytes = new byte[buffer.capacity()];
buffer.get(bytes, 0, bytes.length);