2016년 10월 12일 수요일

리눅스 ffmpeg CUDA 비디오 코덱 연동 빌드 - build ffmpeg with cuda video codec on linux

1. nvidia 드라이버, cuda 툴킷 설치 - /usr/local/cuda
2. cuda video codec 설치 - /home/ubuntu/work/Video_Codec_SDK_7.0.1
3. x264 설치(비교 테스트용) - /home/ubuntu/work/x264/build
4. ffmpeg 빌드(64비트)

./configure --prefix=./build/ --enable-shared --disable-static --enable-cuda --enable-nvenc --enable-nonfree --extra-cflags=-I/usr/local/cuda-8.0/include \
--extra-cflags=-I/home/ubuntu/work/Video_Codec_SDK_7.0.1/Samples/common/inc --arch=x86_6
4 --enable-libx264 --enable-gpl --extra-cflags=-I/home/ubuntu/work/x264/build/include --extra-ldflags=-L/home/ubuntu/work/x264/build/lib

2016년 7월 13일 수요일

소켓 프로그래밍에 대한 조언

언어와 플랫폼을 불문하고 소켓 프로그래밍은 형태가 조금씩 다를뿐이지 내부구현은 완전히 동일하다.
기본적으로 POSIX 소켓 API 로 제공되는 low level API 들이 있고 각 플랫폼마다 이것들을 래핑한 상위 레이어들이 존재하는 형태이다.

응용 프로그램에서는 이러한 low level API를 가지고 개발할 것인지 좀더 편리한 상위 라이브러리를
이용할 것인지 결정해서 개발하면된다.

소켓 프로그래밍을 처음 입문하는 개발자들은 개발에 용이한 상위 라이브러리가 아무래도 많이 끌릴것이다. 심지어 경력 개발자들도 이러한 라이브러리를 이용해서 개발하는것을 선호하기도한다.

개인적으로 소켓 프로그래밍은 반드시 low level API로 개발해야한다고 생각한다. 사실 말이 low level이지 그냥 소켓 API 들이다. C#/Java 등의 언어에서 제공되는 상위 클래스들은 이러한 소켓 API 들을 래핑해서 사용하기 편리하게 만들어주는 역할밖에 하지않는다.

문제는 이러한 라이브러리를 사용해서 개발하다보면 내부구현을 볼 수가 없어서 다양한 상황들이 발생하는 네트워크 프로그래밍에서 올바른 대처를 하기가 어렵다는 점이다. 물론 잘 사용하면 대부분 문제는 없지만 이러한 방법에 길들여지면 소켓 프로그래밍이나 TCP/IP의 기본원리를 이해하기 힘들다는 점도있다.

사실 소켓 프로그래밍을 시작하기 전에 기본적인 TCP/IP 에 대한 이해가 필요하다. 개발을 해가면서 이해하든 미리 공부를 하든 기본적인 TCP/UDP의 동작정도는 알고 시작하는것이 좋다.

여기서 중요한 점은 응용 프로그램과 TCP/IP간의 관계에 대해서 잘 알아야한다는것이다. 
사실 실제적인 데이터 전송과 수신은 TCP/IP가 하는것이지 응용 프로그램이 하는것이 아니다.
응용 프로그램과 TCP/IP는 서로 다른 계층에 존재하고 소켓 API를 통해 서로 데이터를 주고받는데 응용 프로그램의 역할과 TCP/IP의 역할을 잘 알아야 특정한 상황이 발생했을때 이것을 분석해서 원인을 유추하는것이 가능하다.
또한 응용 프로그램간의 프로토콜을 정의하고 구현하는데도 이러한 지식이 필요하다.

마지막으로 한가지 더하자면 통신 프로그램은 아주 정교하게 작성해야한다. TCP 서버/클라이언트 개발자는 잘 알겠지만 통신 상황에서는 너무나도 다양한 케이스들이 발생한다. 대상이 일반 유저라면 생각할 수 있는 거의 모든 경우의 수가 다 발생한다고 보면된다. 
적당히 데이터 주고받는 테스트 정도만 수행하고 릴리즈를 했다간 큰 낭패를 볼 수 있다. 네트워크 상황은 시시각각 변하기때문에 특정 상황에 대한 스냅샷을 잡기가 굉장히 힘들고 이것을 사후에 분석하는것도 어렵기때문에 애초에 robust한 프로그래밍과 테스트가 필요하다.

2016년 7월 4일 월요일

C# 소켓 Select 함수 사용방법 - C# Socket Select

C# 소켓 프로그래밍에서도 기존 POSIX select 함수를 그대로 사용할 수 있다. 방식은 제공되는 Socket 클래스의 Select 정적 멤버함수를 사용하면된다.

< 일반적인 select 사용 >
         try  
         {  
           ArrayList selectList = new ArrayList();  
             
           selectList.Add(mySock);             
   
           if (selectList.Count == 0)  
           {  
             Thread.Sleep(10);  
             return;  
           }  
   
           Socket.Select(selectList, null, null, 1000000);  
   
           foreach (Socket sock in selectList)  
           {                    
             if (sock == mySock)
             {
                 // do something with mySock...
             }
           }            
         }  
         catch (Exception ex)  
         {  
           Trace.WriteLine(ex.ToString());  
         }  

< TCP Connection 타임아웃 >
      
Socket clientSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSock.Blocking = false;
...
public bool Connect(IPAddress serverIP, int serverPort, int timeout) 
{
       try  
       {  
         clientSock.Connect(serverIP, serverPort);        
         return true;  
       }  
       catch (SocketException ex)  
       {          
         if (ex.SocketErrorCode == SocketError.WouldBlock)  
         {  
           ArrayList selectArray = new ArrayList();  
           selectArray.Add(clientSock);  
   
           Socket.Select(null, selectArray, null, timeout * 1000000);  
   
           if (selectArray.Count == 0)  
           {
             Trace.WriteLine(ex.ToString());  
             return false;  
           }
   
           return true;  
         }    
       }  
       catch (Exception ex)  
       {          
         Trace.WriteLine(ex.ToString());  
         return false;  
       }  
}

2016년 6월 20일 월요일

C# 간단한 시리얼 통신 라이브러리 소스코드 - simple c# source code for serial communication

 using System;  
 using System.Collections.Generic;  
 using System.Diagnostics;  
 using System.IO.Ports;  
 using System.Linq;  
 using System.Text;  
 using System.Threading;  
 using System.Threading.Tasks;  
   
 namespace SerialCommLib  
 {        
   public class SerialComm
   {    
     public delegate void DataReceivedHandlerFunc(byte[] receiveData);  
     public DataReceivedHandlerFunc DataReceivedHandler;  
   
     public delegate void DisconnectedHandlerFunc();  
     public DisconnectedHandlerFunc DisconnectedHandler;
   
     private SerialPort serialPort;  
      
     public bool IsOpen   
     {   
       get   
       {   
         if (serialPort != null) return serialPort.IsOpen;  
         return false;  
       }   
     }  
      
     // serial port check
     private Thread threadCheckSerialOpen;  
     private bool isThreadCheckSerialOpen = false;  
   
     public SerialComm()  
     {  
     }  
   
     public bool OpenComm(string portName, int baudrate, int databits, StopBits stopbits, Parity parity, Handshake handshake)  
     {  
       try  
       {  
         serialPort = new SerialPort();  
   
         serialPort.PortName = portName;  
         serialPort.BaudRate = baudrate;  
         serialPort.DataBits = databits;  
         serialPort.StopBits = stopbits;  
         serialPort.Parity = parity;  
         serialPort.Handshake = handshake;  
   
         serialPort.Encoding = new System.Text.ASCIIEncoding();  
         serialPort.NewLine = "\r\n";  
         serialPort.ErrorReceived += serialPort_ErrorReceived;     
         serialPort.DataReceived += serialPort_DataReceived; 
   
         serialPort.Open();  
   
         StartCheckSerialOpenThread();  
         return true;  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
         return false;  
       }  
     }  
   
     public void CloseComm()  
     {  
       try  
       {  
         if (serialPort != null)  
         {  
           StopCheckSerialOpenThread();  
           serialPort.Close();  
           serialPort = null;  
         }  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
     }  
   
     public bool Send(string sendData)  
     {  
       try  
       {  
         if (serialPort != null && serialPort.IsOpen)  
         {  
           serialPort.Write(sendData);  
           return true;  
         }          
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
       return false;  
     }  
   
     public bool Send(byte[] sendData)  
     {  
       try  
       {  
         if (serialPort != null && serialPort.IsOpen)  
         {  
           serialPort.Write(sendData, 0, sendData.Length);  
           return true;  
         }  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
       return false;  
     }  
   
     public bool Send(byte[] sendData, int offset, int count)  
     {  
       try  
       {  
         if (serialPort != null && serialPort.IsOpen)  
         {  
           serialPort.Write(sendData, offset, count);  
           return true;  
         }  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
       return false;  
     }  
   
     private byte[] ReadSerialByteData()  
     {  
       serialPort.ReadTimeout = 100;  
       byte[] bytesBuffer = new byte[serialPort.BytesToRead];  
       int bufferOffset = 0;  
       int bytesToRead = serialPort.BytesToRead;  
   
       while (bytesToRead > 0)  
       {  
         try  
         {  
           int readBytes = serialPort.Read(bytesBuffer, bufferOffset, bytesToRead - bufferOffset);  
           bytesToRead -= readBytes;  
           bufferOffset += readBytes;  
         }  
         catch (TimeoutException ex)  
         {  
           Debug.WriteLine(ex.ToString());  
         }  
       }  
   
       return bytesBuffer;  
     }  
   
     private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)  
     {  
       try  
       {  
         byte[] bytesBuffer = ReadSerialByteData();  
         string strBuffer = Encoding.ASCII.GetString(bytesBuffer);  

         if (DataReceivedHandler != null)  
           DataReceivedHandler(bytesBuffer);  
   
         Debug.WriteLine("received(" + strBuffer.Length + ") : " + strBuffer);     
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
     }  
   
     private void serialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)  
     {  
       Debug.WriteLine(e.ToString());  
     }  
      
     private void StartCheckSerialOpenThread()  
     {   
       isThreadCheckSerialOpen = true;  
       threadCheckSerialOpen = new Thread(new ThreadStart(ThreadCheckSerialOpen));  
       threadCheckSerialOpen.Start();  
     }  
   
     private void StopCheckSerialOpenThread()  
     {  
       if (isThreadCheckSerialOpen)  
       {  
         isThreadCheckSerialOpen = false;  
         if (Thread.CurrentThread != threadCheckSerialOpen)  
           threadCheckSerialOpen.Join();
       }  
     }  
   
     private void ThreadCheckSerialOpen()  
     {  
       while (isThreadCheckSerialOpen)  
       {  
         Thread.Sleep(100);  
   
         try  
         {  
           if (serialPort == null || !serialPort.IsOpen)  
           {  
             Debug.WriteLine("seriaport disconnected");  
             if (DisconnectedHandler != null)  
               DisconnectedHandler();  
             break;  
           }  
         }  
         catch (Exception ex)  
         {  
           Debug.WriteLine(ex.ToString());  
         }  
       }  
     }  
   }  
 }  
   


< SerialComm 사용 >

using System.IO.Ports;

using SerialCommLib;
...
SerialComm serial = new SerialComm();
serial.DataReceivedHandler = DataReceivedHandler;
serial.DisconnectedHandler = DisconnectedHandler;
...
serial.OpenComm("COM3", 9600, 8, StopBits.One, Parity.None, Handshake.None);
...
serial.CloseComm();

private void DataReceivedHandler(byte[] receiveData)
{
    // do something with receiveData here
}

private void DisconnectedHandler()
{
    Console.WriteLine("serial disconnected");
}

* 라이브러리 사용은 시리얼 데이터 수신과 연결종료 이벤트 핸들러를 등록해서 콜백으로 받아서 처리하는 방식으로 간단하게 사용할 수 있다.

C# SerialPort 클래스는 이벤트 수신 방식과 쓰레드 방식 두 가지로 사용할 수 있는데 쓰레드 방식보다 이벤트 수신 방식이 더 간단하고 직관적이다.
SerialPort 클래스의 Read 계열 메소드는 블러킹 방식으로 동작하기때문에 데이터 수신 이벤트 발생시 반드시 BytesToRead 에 명시된 모든 바이트를 읽어서 수신버퍼를 비워줘야한다.


2016년 2월 4일 목요일

OpenCV sample build

cmake -G "Visual Studio 11 2012" -DBUILD_EXAMPLES=1

* samples 아래에 있는 소스들 솔루션/프로젝트 파일 만들 수 있다.

2016년 1월 28일 목요일

ffmpeg 비디오 필터 사용 소스코드 - ffmpeg video filter sample source code

뮤텍스 소스

< VideoFilter.h >
 #ifndef __VIDEO_FILTER_H__  
 #define __VIDEO_FILTER_H__  
   
 extern "C" {  
 #include "libavfilter/avfiltergraph.h"  
 #include "libavfilter/buffersink.h"  
 #include "libavfilter/buffersrc.h"  
 }  
   
 #include "Mutex.h"  
   
 struct VideoFilterParam {  
      int width;  
      int height;  
      AVPixelFormat pix_fmt;  
      AVRational time_base;  
      AVRational sample_aspect_ratio;  
 };  
   
 class VideoFilter   
 {  
 public:  
      VideoFilter();  
      virtual ~VideoFilter();  
   
      int initFilter(struct VideoFilterParam &param, int vflip, int hflip, int rotate);  
      void freeFilter();  
   
      int processFilter(AVFrame *newFrame, AVFrame *frame);  
   
      int checkFilter(struct VideoFilterParam &param);  
   
 protected:  
      static void initFilter();  
   
 protected:  
      AVFilterGraph*          m_pFilterGraph;  
      AVFilterContext*     m_pBufferSinkCtx;  
      AVFilterContext*     m_pBufferSrcCtx;       
      AVFrame*               m_pFrame;  
   
      int                    m_nWidth;  
      int                    m_nHeight;  
      AVPixelFormat     m_nPixelFormat;  
      AVRational          m_timeBase;  
      AVRational          m_sampleAspectRatio;  
   
      static MUTEX     m_hMutex;  
      static bool          m_bInit;  
 };  
   
 #endif  
   

< VideoFilter.cpp >
 #include "VideoFilter.h"  
 #include "GlobalEnv.h"  
 #include "CommonType.h"  
   
 bool VideoFilter::m_bInit = false;  
 MUTEX VideoFilter::m_hMutex = PTHREAD_MUTEX_INITIALIZER;  
   
 VideoFilter::VideoFilter() : m_pFilterGraph(NULL), m_pBufferSrcCtx(NULL), m_pBufferSinkCtx(NULL), m_pFrame(NULL)  
 {  
      initFilter();  
      m_nWidth = m_nHeight = 0;  
      m_nPixelFormat = AV_PIX_FMT_NONE;  
      m_timeBase.den = m_timeBase.num = 0;  
      m_sampleAspectRatio.den = m_sampleAspectRatio.num = 0;  
 }  
   
 VideoFilter::~VideoFilter()  
 {  
      freeFilter();  
 }  
   
 void VideoFilter::initFilter()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      if (!m_bInit) {  
           avfilter_register_all();  
           m_bInit = true;  
      }  
   
      MUTEX_UNLOCK(&m_hMutex);  
 }  
   
 int VideoFilter::initFilter(struct VideoFilterParam &param, int vflip, int hflip, int rotate)  
 {  
      freeFilter();  
   
      char args[128];  
   snprintf(args, sizeof(args),  
       "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",  
       param.width, param.height, param.pix_fmt,   
                param.time_base.num, param.time_base.den,   
       param.sample_aspect_ratio.num, param.sample_aspect_ratio.den);  
   
      m_pFrame = av_frame_alloc();  
      m_pFilterGraph = avfilter_graph_alloc();  
   
      char errbuf[128];  
      int err = avfilter_graph_create_filter(&m_pBufferSrcCtx, avfilter_get_by_name("buffer"), "buffer1", args, NULL, m_pFilterGraph);       
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      err = avfilter_graph_create_filter(&m_pBufferSinkCtx, avfilter_get_by_name("buffersink"), "buffersink1", NULL, NULL, m_pFilterGraph);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      AVFilterContext *vflipCtx = NULL;  
      AVFilterContext *hflipCtx = NULL;  
      AVFilterContext *rotateCtx = NULL;  
      AVFilterContext *prevCtx = m_pBufferSrcCtx;  
   
      if (vflip > 0) {  
           err = avfilter_graph_create_filter(&vflipCtx, avfilter_get_by_name("vflip"), "vflip1", NULL, NULL, m_pFilterGraph);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
                return -1;  
           }  
           err = avfilter_link(prevCtx, 0, vflipCtx, 0);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
                return -1;  
           }  
           prevCtx = vflipCtx;  
      }  
   
      if (hflip > 0) {  
           err = avfilter_graph_create_filter(&hflipCtx, avfilter_get_by_name("hflip"), "hflip1", NULL, NULL, m_pFilterGraph);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
                return -1;  
           }  
           err = avfilter_link(prevCtx, 0, hflipCtx, 0);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
                return -1;  
           }  
           prevCtx = hflipCtx;  
      }  
   
      if (rotate) {  
           snprintf(args, sizeof(args), "PI*2/360*%d", rotate);  
           err = avfilter_graph_create_filter(&rotateCtx, avfilter_get_by_name("rotate"), "rotate1", args, NULL, m_pFilterGraph);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
                return -1;  
           }  
           err = avfilter_link(prevCtx, 0, rotateCtx, 0);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
                return -1;  
           }  
           prevCtx = rotateCtx;  
      }  
   
      err = avfilter_link(prevCtx, 0, m_pBufferSinkCtx, 0);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      err = avfilter_graph_config(m_pFilterGraph, NULL);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_graph_config failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      m_nWidth = param.width;  
      m_nHeight = param.height;  
      m_nPixelFormat = param.pix_fmt;  
      m_timeBase.den = param.time_base.den;  
      m_timeBase.num = param.time_base.num;  
      m_sampleAspectRatio.den = param.sample_aspect_ratio.den;  
      m_sampleAspectRatio.num = param.sample_aspect_ratio.num;  
   
      return 0;  
 }  
   
 void VideoFilter::freeFilter()  
 {  
      avfilter_graph_free(&m_pFilterGraph);  
      av_frame_free(&m_pFrame);  
   
      m_nWidth = m_nHeight = 0;  
      m_nPixelFormat = AV_PIX_FMT_NONE;  
      m_timeBase.den = m_timeBase.num = 0;  
      m_sampleAspectRatio.den = m_sampleAspectRatio.num = 0;  
 }  
   
 AVFrame* copyFrame(AVFrame *frame)  
 {  
      AVFrame *new_frame = avcodec_alloc_frame();  
      if (new_frame) {  
           if (av_image_alloc(new_frame->data, new_frame->linesize,  
                frame->width, frame->height, (AVPixelFormat)frame->format, 1) > 0)   
           {  
                av_image_copy(new_frame->data, new_frame->linesize,  
                     (const uint8_t **)frame->data, frame->linesize, (AVPixelFormat)frame->format,  
                     frame->width, frame->height);  
   
                new_frame->width = frame->width;  
                new_frame->height = frame->height;  
                new_frame->pict_type = frame->pict_type;  
                new_frame->format = frame->format;  
   
                return new_frame;  
           }  
           else  
           {  
                av_free(new_frame->data[0]);  
                av_free(new_frame);  
           }  
      }  
   
      return NULL;  
 }  
   
 int VideoFilter::processFilter(AVFrame *newFrame, AVFrame *frame)  
 {  
      if (!m_pFilterGraph) return -1;  
   
      int ret = av_buffersrc_add_frame_flags(m_pBufferSrcCtx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);  
      if (ret >= 0) {  
           while (1) {  
                ret = av_buffersink_get_frame(m_pBufferSinkCtx, m_pFrame);  
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;  
                if (ret < 0) break;  
        
                newFrame = copyFrame(m_pFrame);  
                av_frame_unref(m_pFrame);  
           }  
      }  
      return 0;  
 }  
   
 int VideoFilter::checkFilter(struct VideoFilterParam &param)  
 {  
      if (m_nWidth != param.width || m_nHeight != param.height ||   
           m_nPixelFormat != param.pix_fmt ||  
           m_timeBase.den != param.time_base.den || m_timeBase.num != param.time_base.num ||  
           m_sampleAspectRatio.den != param.sample_aspect_ratio.den ||   
           m_sampleAspectRatio.num != param.sample_aspect_ratio.num)   
      {  
           return -1;  
      }  
   
      return 0;  
 }  
   

< VideoFilter 사용 >

AVCodecContext *pCodecCtx;
...
struct VideoFilterParam param;
param.width = pCodecCtx->width;
param.height = pCodecCtx->height;
param.pix_fmt = pCodecCtx->pix_fmt;
param.time_base.den = pCodecCtx->time_base.den;
param.time_base.num = pCodecCtx->time_base.num;
param.sample_aspect_ratio.den = pCodecCtx->sample_aspect_ratio.den;
param.sample_aspect_ratio.num = pCodecCtx->sample_aspect_ratio.num;
...
VideoFilter *filter = new VideoFilter();
...
filter->initFilter(param, 1, 1, 30);    // 상하반전, 좌우반전, 30도 회전
...
if (filter->checkFilter(param) < 0) {
    filter->freeFilter();
 
    param.width = pCodecCtx->width;
    param.height = pCodecCtx->height;
    param.pix_fmt = pCodecCtx->pix_fmt;
    param.time_base.den = pCodecCtx->time_base.den;
    param.time_base.num = pCodecCtx->time_base.num;
    param.sample_aspect_ratio.den = pCodecCtx->sample_aspect_ratio.den;
    param.sample_aspect_ratio.num = pCodecCtx->sample_aspect_ratio.num;
}  
filter->processFilter(newFrame, frame);   // newFrame은 처리결과, frame은 디코딩된 프레임
...
filter->freeFilter();

2015년 12월 10일 목요일

java/안드로이드 넌블러킹 소켓을 사용하는 TCP 클라이언트/서버 소스 - java/android nonblocking socket tcp server/client source code

자바/안드로이드 환경에서 TCP 서버는 ServerSocketChannel 클래스를 사용해서 넌블러킹 TCP 서버소켓을 생성후 Selector 를 통해 소켓 select 함수 기능을 수행한다.(소켓 다중화)
TCP 클라이언트는 SocketChannel 클래스를 사용해서 넌블러킹 소켓을 구현한다.

* 서버 종료시 현재 연결된 클라이언트들에 대해 3-way handshake 를 통해 자연스럽게 연결종료 시키는 것에 주목(stopServer 메소드)

< TCPServer.java >
 package com.kimdh.dxmediaplayer;  
   
 import java.net.InetSocketAddress;  
 import java.nio.ByteBuffer;  
 import java.nio.channels.SelectionKey;  
 import java.nio.channels.Selector;  
 import java.nio.channels.ServerSocketChannel;  
 import java.nio.channels.SocketChannel;  
 import java.util.ArrayList;  
 import java.util.Iterator;  
 import java.util.List;  
 import java.util.Set;  
   
 import android.os.StrictMode;  
   
 public class TcpServer {  
      protected ServerSocketChannel mChannel;  
      protected ServerThread mThread;  
        
      private static final int BUFFER_SIZE = 1024 * 1024;  
        
      public boolean isOpened() {  
           if (mChannel == null) return false;  
           return mChannel.isOpen();  
      }       
   
      protected void setNetworkThreadPolicy() {  
           StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();  
           StrictMode.setThreadPolicy(policy);            
      }  
        
      public boolean startServer(int port, ReceiveEventHandler handler) {  
           setNetworkThreadPolicy();  
             
           try {  
                if (mChannel != null) return false;  
                  
                mChannel = ServerSocketChannel.open();  
                mChannel.configureBlocking(false);  
                mChannel.socket().bind(new InetSocketAddress(port));  
                  
                Selector selector = Selector.open();  
                mChannel.register(selector, SelectionKey.OP_ACCEPT);  
                  
                mThread = new ServerThread(mChannel, selector, handler);                                
                mThread.start();                 
                  
                return true;  
           } catch (Exception ex) {  
                ex.printStackTrace();  
                return false;  
           }            
      }  
        
      public void stopServer() {  
           setNetworkThreadPolicy();  
           try {  
                if (mChannel != null) {       
                     mThread.closeAllClient();  
                       
                     while (mThread.getClientCount() > 0)  
                          Thread.sleep(100);  
                       
                     if (mThread != null) {  
                          mThread.mIsRunning = false;  
                          mThread.join();  
                     }            
                     mChannel.close();  
                     mChannel = null;  
                     System.out.println("tcp server channel closed");  
                }  
           } catch (Exception ex) {  
                ex.printStackTrace();  
           }            
      }  
        
      public void closeAllClient() {  
           setNetworkThreadPolicy();  
           if (mChannel != null)       
                mThread.closeAllClient();            
      }  
        
      public boolean send(SocketChannel client, ByteBuffer buffer) {  
           try {  
                if (client.write(buffer) == buffer.limit())   
                     return true;  
           } catch (Exception ex) {  
                ex.printStackTrace();  
           }  
           return false;  
      }       
        
      public interface ReceiveEventHandler {  
           public void onClientConnected(SocketChannel client);  
           public void onReceived(SocketChannel client, ByteBuffer buffer, int len);  
           public void onClientDisconnected(SocketChannel client);  
      }       
        
      protected class ServerThread extends Thread {  
           private ServerSocketChannel mChannel;  
           private Selector mSelector;  
           private List<SocketChannel> mClientList = new ArrayList<SocketChannel>();  
             
           public boolean mIsRunning = false;  
             
           private ReceiveEventHandler mHandler;  
             
           public ServerThread(ServerSocketChannel channel, Selector selector, ReceiveEventHandler handler) {  
                mChannel = channel;  
                mSelector = selector;  
                mIsRunning = true;  
                mHandler = handler;  
           }  
             
           public void closeAllClient() {  
                synchronized (mClientList) {  
                     for (int i=0; i<mClientList.size(); i++) {  
                          try {  
                               mClientList.get(i).socket().shutdownOutput();  
                          } catch (Exception ex) {  
                               ex.printStackTrace();  
                          }  
                     }                      
                }  
           }  
             
           public int getClientCount() {   
                synchronized (mClientList) {  
                     return mClientList.size();                      
                }                 
           }  
             
           @Override  
           public void run() {  
                System.out.println("server thread start");  
                  
                try {  
                     while (mIsRunning) {  
                          mSelector.select(2*1000);  
                            
                          Set keys = mSelector.selectedKeys();  
                          Iterator i = keys.iterator();  
                            
                          while (i.hasNext()) {  
                               SelectionKey key = (SelectionKey)i.next();  
                               i.remove();  
                                 
                               if (key.isAcceptable()) {  
                                    SocketChannel client = mChannel.accept();  
                                    client.configureBlocking(false);  
                                    client.register(mSelector, SelectionKey.OP_READ);  
                                    synchronized (mClientList) {  
                                         mClientList.add(client);                                          
                                    }                                     
                                    System.out.println("client connected : " + client.socket().getRemoteSocketAddress().toString());  
                                    if (mHandler != null) mHandler.onClientConnected(client);  
                                    continue;  
                               }  
                                 
                               if (key.isReadable()) {  
                                    SocketChannel client = (SocketChannel)key.channel();  
                                    ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);  
                                    buffer.clear();  
                                      
                                    int ret;  
                                    try {  
                                         ret = client.read(buffer);  
                                    } catch (Exception ex) {  
                                         ex.printStackTrace();  
                                         key.cancel();  
                                         synchronized (mClientList) {  
                                              mClientList.remove(client);                                               
                                         }                                          
                                         System.out.println("client disconnected : " + client.socket().getRemoteSocketAddress().toString());  
                                         if (mHandler != null) mHandler.onClientDisconnected(client);  
                                         continue;  
                                    }  
                                      
                                    if (ret <= 0) {  
                                         key.cancel();  
                                         synchronized (mClientList) {  
                                              mClientList.remove(client);                                               
                                         }                                                                                       
                                         System.out.println("socket read : " + ret + ", client disconnected : " + client.socket().getRemoteSocketAddress().toString());  
                                         if (mHandler != null) mHandler.onClientDisconnected(client);  
                                         continue;  
                                    }  
                                      
                                    buffer.rewind();  
                                    if (mHandler != null) mHandler.onReceived(client, buffer, ret);  
                               }  
                          }  
                     }  
                } catch (Exception ex) {  
                     ex.printStackTrace();  
                }  
                  
                System.out.println("server thread end");  
           }            
      }  
 }  
   

< TCPClient.java >
 package com.kimdh.dxmediaplayer;  
   
 import java.io.IOException;  
 import java.net.InetSocketAddress;  
 import java.nio.ByteBuffer;  
 import java.nio.channels.SelectionKey;  
 import java.nio.channels.Selector;  
 import java.nio.channels.SocketChannel;  
 import java.util.Iterator;  
 import java.util.Set;  
   
 import android.os.StrictMode;  
   
 public class TcpClient {  
      protected SocketChannel mChannel;  
      protected ReceiveThread mThread;  
        
      public int RECV_BUFFER_SIZE = 1024 * 1024;  
        
      public boolean isConnected() {  
           if (mChannel == null) return false;  
           return mChannel.isConnected();  
      }  
        
      protected void setNetworkThreadPolicy() {  
           StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();  
           StrictMode.setThreadPolicy(policy);            
      }  
             
      public boolean connect(String ipAddress, short port, int timeout, ReceiveEventHandler handler) {  
           setNetworkThreadPolicy();  
                                 
           try     {  
                if (mChannel != null && mChannel.isConnected() == true)  
                     return false;  
                  
                mChannel = SocketChannel.open();  
                mChannel.configureBlocking(false);  
                mChannel.socket().setReceiveBufferSize(RECV_BUFFER_SIZE);  
                  
                mChannel.connect(new InetSocketAddress(ipAddress, port));  
                  
                Selector selector = Selector.open();                 
                SelectionKey clientKey = mChannel.register(selector, SelectionKey.OP_CONNECT);  
                                 
                if (selector.select(timeout*1000) > 0) {  
                     if (clientKey.isConnectable()) {  
                          if (mChannel.finishConnect()) {  
                               mThread = new ReceiveThread(mChannel, handler);  
                               mThread.start();  
                               return true;  
                          }  
                     }                      
                     mChannel.close();  
                     mChannel = null;  
                     return false;                      
                } else {  
                     return false;  
                }  
           } catch (Exception ex) {  
                ex.printStackTrace();  
                return false;  
           }       
      }  
        
      public void close() {  
           setNetworkThreadPolicy();  
           try {  
                if (mChannel != null) {                      
                     if (mThread != null) {  
                          mThread.mIsRunning = false;  
                          mThread.join();  
                     }  
                     mChannel.close();  
                     mChannel = null;  
                     System.out.println("tcp client channel closed");  
                }  
           } catch (Exception ex) {  
                ex.printStackTrace();  
           }  
      }  
             
      public int send(ByteBuffer buffer) {  
           setNetworkThreadPolicy();  
           if (mChannel == null || !mChannel.isConnected()) return -1;            
           try {  
                return mChannel.write(buffer);  
           } catch (IOException ex) {  
                ex.printStackTrace();  
                return -1;  
           }            
      }  
                  
      public interface ReceiveEventHandler {  
           public void onReceived(ByteBuffer buffer, int len);  
           public void onClosed();  
           public void onThreadEvent();  
      }  
             
      protected class ReceiveThread extends Thread {  
           private SocketChannel mChannel;  
           public boolean mIsRunning = false;  
             
           private ReceiveEventHandler mHandler;  
             
           private static final int BUFFER_SIZE = 1024 * 4;  
             
           public ReceiveThread(SocketChannel channel, ReceiveEventHandler handler) {  
                mChannel = channel;                 
                mHandler = handler;  
                mIsRunning = true;  
           }  
             
           @Override  
           public void run() {  
                System.out.println("receive thread start");  
                  
                try {  
                     Selector selector = Selector.open();  
                     mChannel.register(selector, SelectionKey.OP_READ);  
                  
                     while (mIsRunning) {                 
                          if (selector.select(2*1000) > 0) {  
                               Set<SelectionKey> selectedKey = selector.selectedKeys();  
                               Iterator<SelectionKey> iterator = selectedKey.iterator();  
                                 
                               while (iterator.hasNext()) {  
                                    SelectionKey key = iterator.next();  
                                    iterator.remove();  
                                      
                                    if (key.isReadable()) {  
                                         SocketChannel channel = (SocketChannel)key.channel();  
                                         ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);  
                                         buffer.clear();  
                                           
                                         int ret;  
                                         try {  
                                              ret = channel.read(buffer);  
                                         } catch (IOException ex) {  
                                              ex.printStackTrace();  
                                              if (mHandler != null) mHandler.onClosed();  
                                              key.cancel();  
                                              continue;  
                                         }  
                                           
                                         if (ret <= 0) {  
                                              System.out.println("SocketChannel.read returned " + ret);  
                                              if (mHandler != null) mHandler.onClosed();  
                                              key.cancel();  
                                              continue;  
                                         }  
                                           
                                         buffer.rewind();                                                                             
                                                                                   
                                         if (mHandler != null) mHandler.onReceived(buffer, ret);  
                                    }                                     
                               }  
                          }  
                          if (mHandler != null) mHandler.onThreadEvent();  
                     }  
                } catch (Exception ex) {  
                     ex.printStackTrace();  
                }  
                  
                System.out.println("receive thread end");  
           }            
      }  
 }  
   


< TCP 서버 사용 >

public class MainActivity extends Activity implements TcpServer.ReceiveEventHandler {
private TcpServer mServer = new TcpServer();

private byte[] mReceiveBuffer = new byte[1024*4];
private int mReceiveBufferIndex = 0;
        ...
public void onClick(View v) {
        ...
mServer.startServer(8112, this);
    }

protected void onDestroy() {
        ...
mServer.stopServer();
    }

    @Override
public void onClientConnected(SocketChannel client) {
            // do something when client connected

    @Override
public void onReceived(SocketChannel client, ByteBuffer buffer, int len) {
System.out.println("onReceived : " + len);
buffer.get(mReceiveBuffer, mReceiveBufferIndex, len);
mReceiveBufferIndex += len;
// process receive buffer
         ...
         ByteBuffer response = ByteBuffer.allocate(len+4);
         buffer.putInt(len);
         buffer.put(4, payload);
         buffer.rewind();
         mServer.send(client, response);
}

    @Override
public void onClientDisconnected(SocketChannel client) {
// do something when client disconnected
}
}