[백업][가리사니] java.nio.channels.writependingexception
java

이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.

java.nio.channels.AsynchronousSocketChannel 사용시 java.nio.channels.WritePendingException 가 발생한 경우는 아직 쓰기를 완료하지 않았는데 쓰기를 시도하는 경우입니다.

public static void write(SocketUnit socketUnit, ByteBuffer bb)
{
	socketUnit.asc.write(bb, socketUnit, new CompletionHandler<Integer, SocketUnit>()
	{
		@Override
		public void completed(Integer result, SocketUnit socketUnit)
		{
			if (bb.hasRemaining())
			{
				socketUnit.asc.write(bb, socketUnit, this);
			}
			else
			{
				bb.clear();
			}
		}

		@Override
		public void failed(Throwable exc, SocketUnit socketUnit)
		{
			exc.printStackTrace();
			socketUnit.close();
		}
	});
}

위와 같은 경우 같은 접속자에게 write 를 동시에 실행시키면 발생할 수 있습니다.

짜잔... 아래와 같이 하면 해결될까요?

public synchronized static void write(SocketUnit socketUnit, ByteBuffer bb)
{
	socketUnit.asc.write(bb, socketUnit, new CompletionHandler<Integer, SocketUnit>()
	{
		@Override
		public synchronized void completed(Integer result, SocketUnit socketUnit)
		{
			if (bb.hasRemaining())
			{
				socketUnit.asc.write(bb, socketUnit, this);
			}
			else
			{
				bb.clear();
			}
		}

		@Override
		public void failed(Throwable exc, SocketUnit socketUnit)
		{
			exc.printStackTrace();
			socketUnit.close();
		}
	});
}

synchronized로 떡칠했잖아요!! 불행이도 아닙니다. 저 시스템 콜이 완전히 종료될 때 까지 기다려야합니다.

바로 아래와 같이 말이죠! 다만, 이 소스는 어떻게 처리를 해야하는지를 보여주는 단순 예제라 연결에 상관없이 무조건 막아버리고 있습니다. (거기다가 한눈에 보여주기위해.. public static class TmpTest와 같은 괴상한 클래스를 생성해내고 있습니다...) 실제로 구현한다면 SocketUnit내부에 각각 쓰는중 인지를 확인하는 객체와 함께 다음 쓰기에 대한 리스트를 가지고 있어야 할 겁니다.!! 또한 isWriting 가 true로 바뀐 짧은 순간에도 아직 writeList 가 비워져 있지 않다면 무조건 마지막에 add 하여 보내는 순서가 꼬이지 않게 해야합니다.!! 위 강조한 말처럼 어떻게 처리해야할지를 한눈에 보여주기 위한 예제로 실제로... 이 소스를 그대로 사용하시면 ... 안되겠죠!!

하지만 여러분들의 시간은 중요하기 때문에!!

private static boolean isWriting = false;
public static class TmpTest
{
	public SocketUnit socketUnit;
	public ByteBuffer bb;
}
final static public Deque<TmpTest> writeList = new LinkedList<>();
public static void loadWriteList()
{
	if (!writeList.isEmpty())
	{
		TmpTest t = writeList.poll();
		write(t.socketUnit, t.bb);
	}
}

public static void write(SocketUnit socketUnit, ByteBuffer bb)
{
	synchronized (writeList)
	{
		if (!writeList.isEmpty() || isWriting)
		{
			System.out.println("쓰기불가");
			TmpTest t = new TmpTest();
			t.socketUnit = socketUnit;
			t.bb = bb;
			writeList.addLast(t);
			return;
		}
		System.out.println("쓰기가능");
		isWriting = true;
	}

	socketUnit.asc.write(bb, socketUnit, new CompletionHandler<Integer, SocketUnit>()
	{
		@Override
		public void completed(Integer result, SocketUnit socketUnit)
		{
			if (bb.hasRemaining())
			{
				socketUnit.asc.write(bb, socketUnit, this);
			}
			else
			{
				bb.clear();
			}

			System.out.println("쓰기완료");
			synchronized (writeList)
			{
				isWriting = false;
				loadWriteList();
			}
		}

		@Override
		public void failed(Throwable exc, SocketUnit socketUnit)
		{
			exc.printStackTrace();
			socketUnit.close();
		}
	});
}