상세 컨텐츠

본문 제목

nio server socket

보안/java

by MustThanks 2020. 9. 25. 22:07

본문

반응형
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;
}

관련글 더보기

댓글 영역