package or.kr.TServer;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Vector;
//sun.nio.ch.DirectByteBuffer 정리가필요
//인터페이스를 추가하여 callback이 되는 기능이 추가되면은 좋음
public class ServerListenImple implements IServerListen {
private ServerSocketChannel serverSocketChannel;
private ServerSocket serverSocket;
private Selector selector;
private final int INT_ZERO = 0;
private int iPort ;
private boolean bBlocking = false; //nonBlocking
private boolean bRunValue = true;
private final int INT_READ_CAPACITY = 1024*10;
//메세지를 읽어 저장하는 기능
private ByteBuffer readByteBuffer = ByteBuffer.allocate(INT_READ_CAPACITY);
//메세지를 임시저장하는 기능
private StringBuffer readStringBuffer ;
//메세지를 담는기능
private Vector<StringBuffer> vectorStringBuffer ;
//charSet
private final String UTF_8 = "utf-8";
private final String STR_EMPTY = "";
//클라이언트 socket를 담는기능을 제공
private Vector<SocketChannel> vectorClientSockets ;
//접속하는 클라이언트최대수
private int INT_MAX_CLIENT = 100; ;
//서버를 오픈
@Override
public void doOpen() throws IOException {
// TODO Auto-generated method stub
serverSocketChannel = ServerSocketChannel.open();
serverSocket = serverSocketChannel.socket();
createSelect();
registSelector();
setBlockingYN(bBlocking);
}
//selector 오픈
private void createSelect() throws IOException
{
selector = Selector.open();
}
//selector 등록
private void registSelector() throws ClosedChannelException
{
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT,null);
}
//서버socket binding
@Override
public void doBind() throws IOException{
// TODO Auto-generated method stub
if(INT_ZERO >= iPort) throw new IOException("please sock listen port check ");
serverSocket.bind(new InetSocketAddress(iPort));
}
//flase는 nonblocking
private void setBlockingYN(boolean pBlocking) throws IOException
{
serverSocketChannel.configureBlocking(pBlocking);
}
//서버에서 client의 반응을 감지하는 기능
public void doAccept() throws IOException
{
while(bRunValue)
{
int keyCounter = selector.select();
if(keyCounter>0)
{
getSelectionKey();
}
}
}
//selector의 반응을 보면서 동작
private void getSelectionKey() throws IOException
{
Iterator<SelectionKey> iteratorSelectionKey = selector.selectedKeys().iterator();
while(iteratorSelectionKey.hasNext())
{
SelectionKey selectionKey = iteratorSelectionKey.next();
iteratorSelectionKey.remove();
SelectableChannel selectableChannel = selectionKey.channel();
//connection finish value is false
if(!selectionKey.isConnectable())
{
//기존 socket 중에서 연결되지 않은 socket이면 close
closeClientSocket(selectableChannel);
continue;
}
if(selectionKey.isAcceptable())
{
if(INT_MAX_CLIENT > vectorClientSockets.size())
registChannel(selectableChannel,SelectionKey.OP_READ | SelectionKey.OP_WRITE);
else
System.out.println(" client number is connect over ");
}
else if(selectionKey.isReadable())
{
readMassage(selectableChannel);
}
else if(selectionKey.isWritable())
{
}
}
}
//socket을 닫는 기증을 제공
private void closeClientSocket(SelectableChannel pSelectableChannel) throws IOException
{
SocketChannel socketChannel=((ServerSocketChannel)pSelectableChannel).accept();
//indexOf 값은 0 부터시작
int index = vectorClientSockets.indexOf(socketChannel);
if(index >= INT_ZERO)
{
socketChannel = vectorClientSockets.get(index);
vectorClientSockets.remove(index);
socketChannel.close();
}
}
//채널을 등록
//regist channel (income channel)
private SocketChannel registChannel(SelectableChannel pSelectableChannel,int ops) throws IOException
{
SocketChannel socketChannel = null;
if( pSelectableChannel!= null )
{
pSelectableChannel.configureBlocking(bBlocking);
socketChannel=((ServerSocketChannel)pSelectableChannel).accept();
socketChannel.register(selector, ops);
}
return socketChannel;
}
//메세지를 읽어오는 기능
private void readMassage(SelectableChannel pSelectableChannel) throws IOException
{
if(!pSelectableChannel.isOpen()) throw new IOException("read socket close");
SocketChannel readSocketChanel = (SocketChannel)pSelectableChannel;
try
{
readStringBuffer = new StringBuffer(INT_READ_CAPACITY);
readByteBuffer.clear();
String str = "";
//data in socket channel read
while( readSocketChanel.read(readByteBuffer) > INT_ZERO)
{
readByteBuffer.flip();
str = "";
str = byteBufferToString(readByteBuffer);
if(str==null || STR_EMPTY.equals(str)) continue;
readStringBuffer.append( str);
readByteBuffer.clear();
}
vectorStringBuffer.add(readStringBuffer);
vectorClientSockets.add(readSocketChanel);
}
finally
{
readByteBuffer.clear();
}
}
//ByteBuffer를 String으로 변환
private String byteBufferToString(ByteBuffer pByteBuffer)
{
byte[] byteArray;
if(pByteBuffer.hasArray()) {
byteArray = pByteBuffer.array();
}
else
{
byteArray = new byte[pByteBuffer.remaining()];
pByteBuffer.duplicate().get(byteArray); //신규 buffer를 만들어서 가지고오는 것으로 구현
}
String returnString = "";
try {
returnString = new String(byteArray,UTF_8);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("byteBufferToString UnsupportedEncodingException : ["+ e.toString() + "]");
}
return returnString;
}
//클라이언트에게 보낼 메세지를 확인
public void doBroadCast() throws IOException
{
Iterator<StringBuffer> messageStringBufferIterator = vectorStringBuffer.iterator();
while(messageStringBufferIterator.hasNext())
{
sendMessage( messageStringBufferIterator.next());
messageStringBufferIterator.remove();
}
}
//메세지를 클라이언트에게 발송
private void sendMessage(StringBuffer messageStringBuffer) throws IOException
{
Iterator<SocketChannel> clientSocketChannelIterator = vectorClientSockets.iterator();
SocketChannel clientSocketChannel;
ByteBuffer messageByteBuffer = stringToByteBuffer(messageStringBuffer.toString());
try
{
messageByteBuffer.flip();
while(clientSocketChannelIterator.hasNext())
{
clientSocketChannel = clientSocketChannelIterator.next();
//연결되어 있으면 보냄
if(clientSocketChannel.isConnected())
{
clientSocketChannel.write(messageByteBuffer);
}
else //연결이 되어 있지 않으면 socket을 닫음
{
clientSocketChannel.close();
clientSocketChannelIterator.remove();
}
}
}
finally
{
messageByteBuffer.clear();
}
}
//String을 ByteBuffer로 변환
private ByteBuffer stringToByteBuffer(String pString)
{
return Charset.forName(UTF_8).encode(pString);
}
//false down service
@Override
public void doRunValue(boolean pBoolean) throws IOException
{
bRunValue = pBoolean;
}
}
package or.kr.TServer;
import java.io.IOException;
public interface IServerListen {
public void doOpen() throws IOException ;
public void doBind() throws IOException;
public void doAccept() throws IOException;
public void doRunValue(boolean pBoolean) throws IOException;
public void doBroadCast() throws IOException;
}
댓글 영역