내가만든/라인들2016. 3. 2. 23:00
Posted by 비엔나햄
내가만든/라인들2016. 3. 2. 22:47

 

Sched.cpp

다운로드

Posted by 비엔나햄
내가만든/라인들2016. 2. 28. 17:19

C#

로그 파일이 일정크기(2GB)가 넘어가면 일반적인 텍스트 툴로 파일으 오픈할 수 없다..ㅠ

해서 텍스트 파일을 크기/라인 수로 분할하는 프로그램을 제작.

큰 파일은 시간이 오래 걸리기 떄문에 멀티스레드를 이용한 병렬처리를 적용해 보았다(Parallel.For)

프로세스 사용량이 전 코어 100% 사용하게 되어 일반적인 싱글 스레드 로직보다 초대 3배가량 나은 성능을 보여준다 :)

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

namespace DistributeTextFile.FileManager
{
    // Declare ENUM Values
    //*---------------------------------*
    enum E_VALUE
    {
        KBYTE = 1024,
        MBYTE = KBYTE * KBYTE,
        READ_MAX = 100000,
    };

    enum E_TYPE
    {
        LINE = 0,
        SIZE ,
        _MAX_,
        DEFAULT = LINE,
    };
    //*---------------------------------*

    struct StreamData
    {
        public StreamReader SReader;
        public string szFilPath;
        public int nLines;
    };

    class FileManager : Util.Sigleton.Singleton<FileManager>
    {           
        public FileManager()
        {           
            this.Init();
        }
        ~FileManager() { }

        private void Init()
        {
            this.eDistributeType = E_TYPE.DEFAULT;
            this.FileListContainer = new List<string>();

            this.szDistributedTypeName = new string[(int)E_TYPE._MAX_];
            this.szDistributedTypeName[(int)E_TYPE.LINE] = "라인 수";
            this.szDistributedTypeName[(int)E_TYPE.SIZE] = "파일 크기(MB)";

            this.nDistributedTypeValue = new int[(int)E_TYPE._MAX_];
            this.nDistributedTypeValue[(int)E_TYPE.LINE] = 1000000;
            this.nDistributedTypeValue[(int)E_TYPE.SIZE] = 100;
        }

        private bool ReadFile( string szFilePath )
        {
            if (!File.Exists( szFilePath ))
            {
                return false;
            }          

            return true;
        }
       
        public void DistributeFiles()
        {
            int szMainProccessID = Thread.CurrentThread.ManagedThreadId;
            string szOutFolder = "\\Result";
            string szReadFailList = "";

            UInt64 nTotalBuffSize = 0;
            UInt64 nCurReadSize = 0;

            StreamReader []_SR = new StreamReader[this.FileListContainer.Count];
           
            List< StreamData > FileStreamContainer = new List<StreamData>();

            object objMutex = new object();
            Parallel.For(0, this.FileListContainer.Count, (nCnt) =>
            {
                if (false == this.ReadFile(this.FileListContainer[nCnt]))
                {
                    szReadFailList += this.FileListContainer[nCnt] + "; ";
                    return;
                }

                _SR[nCnt] = new StreamReader(this.FileListContainer[nCnt], Encoding.Default);
                nTotalBuffSize += (UInt64)_SR[nCnt].BaseStream.Length;

                int nLineNum = 0;

                /* 단순 라인 수 읽기는 싱글 스레드 로직이 2~3배 가량 더 빠름 */
                nLineNum = File.ReadLines(this.FileListContainer[nCnt]).Count();
                _SR[nCnt].BaseStream.Position = 0;

                StreamData stStreamData = new StreamData();
                stStreamData.nLines = nLineNum;
                stStreamData.SReader = _SR[nCnt];
                stStreamData.szFilPath = this.FileListContainer[nCnt];

                lock (objMutex)
                {
                    FileStreamContainer.Add(stStreamData);
                }
            });

            object objProgressBarMutex = new object();
            Parallel.For(0, FileStreamContainer.Count, (idx) =>
            {
                /// ---폴더 생성-------------------------------------------------
                string szPath = FileStreamContainer[idx].szFilPath;   // 파일 경로
                string szFileName = Path.GetFileName(szPath);             // 파일 이름
                string szExtName = Path.GetExtension(szFileName);        // 확장자
                szFileName = szFileName.Split('.')[0];

                string szOutPutPath = Directory.GetCurrentDirectory() + szOutFolder + "\\" + szFileName + "\\";
                Directory.CreateDirectory(szOutPutPath);
                /// ------------------------------------------------------------

                /// ---스레드 당 생성 되는 지역 변수----------------------------
                Dictionary<int, string> ReadFileContainer = new Dictionary<int, string>(); // 스레드 별로 사용할 파일(컨테이너)

                int nRemainReadLine = FileStreamContainer[idx].nLines;  // 이 스레드에서 처리해야할 총 라인 수
                int nTotalWriteLine = 0; // 현재 총 쓰기 완료 라인 수
                int nCurWriteLine = 0; // 현재 사용중인 파일에서 쓰기 완료한 라인 수
                int nFileCounts = getFileCounts(FileStreamContainer[idx].nLines); // 분할될 파일 갯수
                bool bReadStream = true; // 원본 파일(스트림) 읽기 여부
                int nReadLineNum = 0;
                /// ------------------------------------------------------------
                for (int jdx = 0; jdx < nFileCounts; jdx++)
                {
                    /// ---파일 생성-------------------------------------------------
                    FileStream FS = File.Create(szOutPutPath + szFileName + "[" + jdx + "]" + szExtName);
                    /// -------------------------------------------------------------

                    object objReadMutex = new object();
                    bool bIsWriteLineRemained = true; // 써야할 내용이 남아 있을때
                    int nCurFileTotalLines = 0; // 현재 파일에 쓰여진 라인 수

                    do
                    {
                        if (true == bReadStream)
                        {
                            /// 원본 파일에서 읽어들이기 ( 최대 100000 라인 )
                            ReadFileContainer.Clear();
                            int nLoopCnt = ((int)E_VALUE.READ_MAX < nRemainReadLine) ? (int)E_VALUE.READ_MAX : nRemainReadLine;
                            nCurWriteLine = 0; // 현재 사용중인 파일에서 쓰기 완료한 라인 수 초기화
                            Parallel.For(0, nLoopCnt, (nCurLine) =>
                            {
                                lock (objReadMutex)
                                {
                                    try
                                    {
                                        string szTmp = FileStreamContainer[idx].SReader.ReadLine();
                                        ReadFileContainer.Add(nReadLineNum++, szTmp + "\n");
                                    }
                                    catch (Exception e) { }

                                }
                            });
                        }

                        int nWriteLine = 0; // 이 반복문 내에서 쓰기 작업 완료한 횟수
                        for (int kdx = nCurWriteLine; kdx < ReadFileContainer.Count; kdx++)
                        {
                            try
                            {
                                byte[] byByteBuff = Encoding.Default.GetBytes(ReadFileContainer[kdx + nTotalWriteLine]);
                                FS.Write(byByteBuff, 0, ReadFileContainer[kdx + nTotalWriteLine].Length);

                                lock (objProgressBarMutex)
                                {
                                    // ---- Progress Bar -----------------------
                                    nCurReadSize += (UInt64)byByteBuff.Length;
                                    int nProgress = (int)(((float)nCurReadSize / (float)nTotalBuffSize) * 100);
                                    if ( Thread.CurrentThread.ManagedThreadId == szMainProccessID)
                                    {
                                        MainForm.ProgressBar_Set(nProgress);
                                    }                                   
                                    // ------------------------------------------
                                }

                            }
                            catch (Exception e)
                            {
                                nWriteLine++;
                                nCurFileTotalLines++;
                                break;
                            }
                            nWriteLine++;
                            nCurFileTotalLines++;
                            //if (nWriteLine >= this.nDistributedTypeValue[(int)this.eDistributeType])
                            //if (nCurWriteLine >= this.nDistributedTypeValue[(int)this.eDistributeType])
                            if (nCurFileTotalLines >= this.nDistributedTypeValue[(int)this.eDistributeType])
                            {
                                break;
                            }
                        }
                        /* 1. 파일 분할 조건에 따라 꽉 찼을 경우(읽어 들이는 작업은 No, 새로 파일 여는 작업 Yes)
                         * 2. 시스템 제한(10만줄)에 해당 되어 읽어 들이는 작업 재수행(읽어들이는 작업 Yes, 새로 파일 여는 작업 No) */
                        nCurWriteLine += nWriteLine;
                        nTotalWriteLine += nWriteLine;
                        nRemainReadLine -= nWriteLine; // 이 스레드(파일)의 남은 작업량을 갱신 해준다
                        if (0 >= nRemainReadLine) // 더 이상 읽어야 할 라인이 존재하지 않는다면 종료
                        {
                            bIsWriteLineRemained = false;
                            FS.Close();
                        }
                        else // 아직 읽어야 할 라인이 존재한다
                        {
                            // 1. 파일 분할 조건에 해당하지 않으면( 컨테이너 내용은 다썼는데, 파일 제한에 해당 되지 않음) 컨테이너를 다시 채우고 쓰기작업
                            // do~while문 탈출 하지 않고 재수행
                            if (nCurWriteLine == ReadFileContainer.Count && nTotalWriteLine < this.nDistributedTypeValue[(int)this.eDistributeType])
                            {
                                bReadStream = true;
                                continue;
                            }
                            // 2. 파일 분할 조건에 해당하고, 컨테이너 내용도 다썼을 경우
                            // do~while문 탈출
                            else if (nCurWriteLine == ReadFileContainer.Count && nTotalWriteLine >= this.nDistributedTypeValue[(int)this.eDistributeType])
                            {
                                bReadStream = true;
                                bIsWriteLineRemained = false;
                                FS.Close();
                            }
                            // 3. 파일 분할 조건에 해당하면( 컨테이너 내용 다 못썼는데, 파일 제한에 해당됨) 파일을 새로 생성(컨테이너 비우기는 X)
                            // do~while문 탈출
                            else
                            {
                                bReadStream = false;
                                bIsWriteLineRemained = false;
                                FS.Close();
                            }
                        }

                    } while (bIsWriteLineRemained);
                }
            }
            );

            OpenFolder(Directory.GetCurrentDirectory() + szOutFolder);
            MainForm.ProgressBar_Init();

            // =======================================================================
            // =======================================================================
        }

        private int getFileCounts(int nLineNums)
        {
            int nFileNum = 0;

            nFileNum = (nLineNums / this.nDistributedTypeValue[(int)this.eDistributeType]);
            if (0 < nLineNums % this.nDistributedTypeValue[(int)this.eDistributeType]) { nFileNum++; }

            return nFileNum;
        }
        private bool checkConditions( UInt64 dwReadValue )
        {
            int nType = (int)this.eDistributeType;
            UInt64 dwCondition = (UInt64) this.nDistributedTypeValue[nType];
            bool retValue = true;
            switch (this.eDistributeType)
            {
                case E_TYPE.LINE:
                default:
                    {
                        if (dwCondition <= dwReadValue)
                        {
                            retValue = false;
                        }
                    }
                    break;
                case E_TYPE.SIZE:
                    {
                        if (dwCondition * (UInt64)E_VALUE.MBYTE <= dwReadValue)
                        {
                            retValue = false;
                        }
                    }
                    break;
            }
            return retValue;
        }
        public void OpenFolder( string szFolderPath )
        {
            System.Diagnostics.Process.Start("explorer.exe", szFolderPath);
        }

        public void SetFilePath(string szFilePath)
        {
            this.szFilePath = szFilePath;
        }
        public void SetFilePath(string []szFilePath)
        {
            this.FileListContainer.Clear();
            this.FileListContainer.AddRange(szFilePath);
        }
        public String GetFilePath()
        {
            return this.szFilePath;
        }

        public void SetType(int nType)
        {
            this.eDistributeType = (E_TYPE)nType;
        }
        public new int GetType()
        {
            E_TYPE eIdx = this.eDistributeType;
            return (int)eIdx;
        }
        public String[] GetTypes()
        {
            return this.szDistributedTypeName;
        }

        public void SetTypeValue(int nValue)
        {
            this.nDistributedTypeValue[(int)this.eDistributeType] = nValue;
        }
        public int GetTypeValue()
        {
            return this.nDistributedTypeValue[(int)this.eDistributeType];
        }

        public void SetMainForm(DistributeTextFile.Form1 stMainForm) { this.MainForm = stMainForm; }

        private string szFilePath;
        private List<string> FileListContainer;
        private E_TYPE eDistributeType;
        private String []szDistributedTypeName;
        private int []nDistributedTypeValue;
               
        ///  Main Form
        private DistributeTextFile.Form1 MainForm;
    }
}

※ 성능을 더 개선한 버전(컨테이너 사용하지 않음)도 있는데, 모든 소스는 SVN에 있다 :)

※ 사용 예시

 

 

 

 

'내가만든 > 라인들' 카테고리의 다른 글

파일분할_파일  (0) 2016.03.02
스케쥴링_파일  (0) 2016.03.02
[이벤트] 카카오톡 욕설 집계 스크립트  (0) 2016.02.28
[이벤트] 크리스마스 트리  (0) 2016.02.28
[메모리풀] 헤더 파일  (0) 2016.02.28
Posted by 비엔나햄
내가만든/라인들2016. 2. 28. 17:11

Python V3.5

카카오톡 대화 욕설 집계 V1.0.0

카카오톡 대화내용을 내보내기 한 뒤, 나온 파일을 통해 욕 횟수를 집계해 준다.

#read.py

import string

#def ExportText()
def ExportText(append):
 f = open("C:\\PythonStudy\\ChatData\\Parsing_Chat.csv", 'a') 
 f.write(append)
 f.close()
 
#def ReadText()
def ReadText( MalWord ):
 
 # 대화 목록 읽어 오기
 f = open("C:\\PythonStudy\\ChatData\\KakaoTalkChats.txt", 'r') 
 Curline = f.readline()
 
 #While Loop1========================================================
 while Curline:
  line = Curline
  NextLine = f.readline()
  
 #While Loop2--------------------------------------------------------
  while NextLine:
   if 1 >= len(NextLine) : # 줄바꿈만 있는 공백
    line = NextLine
    NextLine = f.readline()
   
   if '2' == NextLine[0] and '년' == NextLine[4] : # 기본 채팅 라인
    break
   
   line = line.split('\n')[0]
   line += NextLine   
   NextLine = f.readline()
 #-------------------------------------------------------- 
  
 #========================================================
  for FilterWord in MalWord: # 필터 단어 수 만큼 검사 하기 때문에, 중복 여부를 설정해야 한다.
   nRet = line.find(FilterWord)
   if -1 == nRet: 
    continue
    
   split_SemiColumn = line.split(' : ')
   if 2 > len(split_SemiColumn):
    continue
   
   try: # 콜론(:)으로 파싱 시도
    split_Comma = split_SemiColumn[0].split(',')
    
   except: # 예외 사항 발생시 넘어감
    continue
           
   if 10 < len(split_Comma[1]): #이름 파싱
    continue
       
   #시간 + 이름 + 내용
   strLine = split_Comma[0]
   strLine += "," +split_Comma[1]
   strLine += "," +FilterWord
   strLine += "," +split_SemiColumn[1]
   ExportText( strLine )
   break # break 가 있으면 중복 제거, 없으면 중복 허용
   
  Curline = NextLine
  
 f.close()
 
#def ReadMalWord()
def ReadMalWord():
 f = open("C:\\PythonStudy\\MalWord\\MalWord.txt", 'r')
 line = f.read().splitlines()
 Container = []
 
 for MalWord in line:
  Container.append(MalWord)
    
 f.close()
 
 return Container

#main

WordContainer = ReadMalWord() # 필터링 단어 리스트

ReadText(WordContainer) # 대화 목록 읽어오기

 

파이썬은 기본 코딩 문법과는 다른 양상이어서 이해가 조금 힘들었다.

탭한번만 잘못써도 Scope가 바뀌어 버리는 상황이 발생! 주의해야 한다(적절한 코드 편집기 사용이 필 수 인 것 같다. 난 NotePad++ 써서 어려웠음)

사용하려면 필터링 대상 단어를 입력 해야하는데, 저작권 무제로 배포 못합니다(__)

프로그램 사용해보고 싶으시면, 실행파일로 만들어 드립니다 :)

(사용예시)

 

 

스크립트를 통해 집계된 욕 획수를 엑셀로 정리하여 표나 차트로 제공하면 더욱 좋다 ㅋㅋ :)

 

 

'내가만든 > 라인들' 카테고리의 다른 글

스케쥴링_파일  (0) 2016.03.02
[로그분할기] 텍스트 용량별 분할기  (0) 2016.02.28
[이벤트] 크리스마스 트리  (0) 2016.02.28
[메모리풀] 헤더 파일  (0) 2016.02.28
[채팅] 클라이언트 코드  (0) 2016.02.28
Posted by 비엔나햄
내가만든/라인들2016. 2. 28. 16:54

크리스마스에 여자친구에게 주고자 만들어봄.

이 간단한 녀석이 이틀이나 걸렸다..ㅠㅠ

그래도 벡터와 알림 시스템, 그리고 멀티스레드를 이용하여 나름 구조적으로 설계한,

애정 가득한 프로그램이다(소리도 난다) :)

매년 써먹어야지ㅋㅋ

 

Tree.7z

 

 

Posted by 비엔나햄
내가만든/라인들2016. 2. 28. 16:47

C/C++

메모리풀 포인터 구현 버전

메모리풀을 포인터와 벡터로 구현해 보았는데,

벡터는 너무 간단하여 포인터로 직접 구현한 뒤, 범용적으로 사용하기 위해 템플릿 적용.

메모리풀을 사용하면 할당하는데에 작은 시간비용이 추가 되긴 하지만,

leak방지와 메모리 관리에 효율 적이다.

자세한 내용은 SVN에 :)

template< class Type, int nSize >
class Pool
{
private:
 struct Node
 {
  Type clsItem;

  Node * pNext;
 };

public: 
 Pool();
 ~Pool(){}

 int GetPoolSize()
 { return this->m_nPoolSize; }

 int GetMaxCount()
 { return this->m_nMaxCount; }

 int GetUsedCount()
 { return this->m_nUsedCount; }
public:
 Type*    Capture();
 void    Release(); // Test
 void    Release( Type * pItem );

private:
 int m_nPoolSize; // 메모리 풀 사이즈
 int m_nUsedCount; // 사용 중인 객체 갯수
 int m_nMaxCount; // 메모리 풀에 보유중인 객체 전체 갯수

 Node * m_pVacant; // 사용 가능 객체 모음 포인터
 Node * m_pOccupied; // 사용 중인 객체 모음 포인터
};

template< class Type, int nSize >
Pool<Type, nSize>::Pool()
{
 m_nPoolSize  = sizeof(*this);
 m_nUsedCount = 0;
 m_nMaxCount  = 0;

 m_pVacant  = nullptr;
 m_pOccupied  = nullptr;
 
 for( int i = 0; i < nSize; i++ )
 {
  Node * pNewNode = new Node();

  m_nPoolSize += sizeof(Type); // 생성한 노드 만큼 풀 사이즈 증가
  m_nMaxCount += 1;    // 전체 객체 갯수 증가

  if( nullptr == m_pVacant )
  {   
   pNewNode->pNext = nullptr;
   m_pVacant = pNewNode;
   continue;
  }
  
  pNewNode->pNext = m_pVacant->pNext;
  m_pVacant->pNext = pNewNode;
 }
}

template< class Type, int nSize >
Type*
Pool<Type, nSize>::Capture()
{
 Node * pNode = m_pVacant;
 if( nullptr != pNode ) // 풀에 비어있는 노드가 있다면
 {
  m_pVacant = pNode->pNext;
 }
 else // 비어있는 노드가 없다면
 {
  pNode = new Node();

  m_nPoolSize += sizeof(Type);
  m_nMaxCount += 1;
 }
 
 // 사용중 객체 리스트에 넣는다
 pNode->pNext = m_pOccupied;
 m_pOccupied  = pNode;
  
 m_nUsedCount += 1;
 
 return &(pNode->clsItem);
}

template< class Type, int nSize >
void
Pool<Type, nSize>::Release() /* TEST (맨 앞 노드 해제) */
{
 if( nullptr == m_pOccupied )
 {
  return;
 }

 Node * pReleaseNode = m_pOccupied;
 m_pOccupied = pReleaseNode->pNext;

 // 미사용 객체 리스트에 넣는다
 pReleaseNode->pNext = m_pVacant;
 m_pVacant = pReleaseNode;
 
 m_nUsedCount -= 1;
}

template< class Type, int nSize >
void
Pool<Type, nSize>::Release( Type * pItem )
{
 if( nullptr == pItem )
 {
  return;
 }

 bool bIsExist = false;
 Node * pPrevNode = nullptr;
 Node * pCurNode = m_pOccupied;
 for( pCurNode; /* 사용중인 객체를 순회 한다 */
   nullptr != pCurNode;
   pCurNode = pCurNode->pNext )
 {
  if( pItem == &(pCurNode->clsItem) )
  {
   bIsExist = true;
  }
  pPrevNode = pCurNode;
 }

 if( false == bIsExist ) // 사용중인 객체가 없다
 {
  return;
 }

 // 노드가 존재하는데 이전의 노드가 null이라면, 첫번째 노드이다
 if( nullptr == pPrevNode )
 {
  m_pOccupied = pCurNode->pNext;
 }
 else
 {
  pPrevNode->pNext = pCurNode->pNext;
 }

 m_nUsedCount -= 1; // 사용중인 객체의 수를 감소 시킨다
 
 // 미사용 객체 리스트에 넣는다
 pCurNode->pNext = m_pVacant;
 m_pVacant = pCurNode;

 m_nUsedCount -= 1;
}

Posted by 비엔나햄
내가만든/라인들2016. 2. 28. 16:25

C++ -> C#

처음에는 C++로 구현했다가 콘솔의 한계.. 로 인해 UI를 위해 C#으로 변경

마샬링이 어려워서 그냥 JSON형식으로 전달. 서버와 마찬가지로 전체 소스는 SVN에!

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using PacketDLL;
using ChatClient_CSharp.Library;
using System.Net.Json;
using System.Windows.Forms;

namespace ChatClient_CSharp
{
    delegate void DText(string strMessage);

    public class ClientInfo
    {
        private IPAddress m_IpAddress;
        private int m_nPort;
        private TcpClient m_ClientSocket;
       
        /* 스트림 전송 관련 */
        private NetworkStream m_NetWorkStream;
        private StreamReader m_strReader;
        private StreamWriter m_strWriter;

        private MemoryStream m_MemStream;
        private BinaryFormatter m_BinaryFomatter;

        private string m_strNickName;

        public ClientInfo()
        {
            m_IpAddress = IPAddress.Parse("127.0.0.1");
            m_nPort = 50001;
            m_strNickName = "";
        }

        public IPAddress IpAddress
        {
            get { return this.m_IpAddress; }
            set { this.m_IpAddress = value; }
        }
        public int Port       
        {
            get { return this.m_nPort; }
            set { this.m_nPort = value; }
        }

        public TcpClient MyClientSocket
        {
            get { return this.m_ClientSocket; }
            set { this.m_ClientSocket = value; }
        }

        public NetworkStream MyNetWorkStream
        {
            get { return this.m_NetWorkStream; }
            set { this.m_NetWorkStream = value; }
        }
        public StreamReader MyStreamReader
        {
            get { return this.m_strReader; }
            set { this.m_strReader = value; }
        }
        public StreamWriter MyStreamWriter
        {
            get { return this.m_strWriter; }
            set { this.m_strWriter = value; }
        }
        public string NickName
        {
            get { return this.m_strNickName; }
            set { this.m_strNickName = value; }
        }
        public MemoryStream MyMemoryStream
        {
            get { return this.m_MemStream; }
            set { this.m_MemStream = value; }
        }
        public BinaryFormatter MyBinaryFomatter
        {
            get { return this.m_BinaryFomatter; }
            set { this.m_BinaryFomatter = value; }
        }
    }
    public class Client : Library.Singleton.MySingleton<Client>
    {
        public const int BUFFER_SIZE = 4096;

        private AsyncCallback asReceiveThread;
        private AsyncCallback asSendThread;
        private System.Windows.Forms.TextBox textBox_Chat;
        private ClientInfo ClientInfo;
        private DText call;

        public ClientInfo CInfo
        {
            get { return this.ClientInfo; }
        }

        public Client()
        {
            Init();
            //ConnectToServer();
        }

        private void Init()
        {
            ClientInfo = new ClientInfo();
            call       = new DText(Print_Text);
        }

        public bool ConnectToServer()
        {
            ClientInfo.MyClientSocket = new TcpClient();
            IPEndPoint EP = new IPEndPoint(ClientInfo.IpAddress, ClientInfo.Port);

            int nTryCnt = 0;
            while (true)
            {
                try
                {
                    ClientInfo.MyClientSocket.Connect(EP);
                    break;
                }
                catch
                {
                    if (10 < nTryCnt)
                    {
                        return false;
                    }
                    nTryCnt++;
                }
            }

            ClientInfo.MyNetWorkStream = ClientInfo.MyClientSocket.GetStream();
            ClientInfo.MyClientSocket.ReceiveBufferSize = BUFFER_SIZE;
            ClientInfo.MyClientSocket.SendBufferSize = BUFFER_SIZE;

            this.CreateReceiveThread();

            return true;
        }

        public void CreateReceiveThread()
        {
            Thread ThreadHandle = new Thread(new ThreadStart(Receive_Thread));
            ThreadHandle.Start();

            ClientInfo.MyStreamReader = new StreamReader(ClientInfo.MyNetWorkStream);
            ClientInfo.MyStreamWriter = new StreamWriter(ClientInfo.MyNetWorkStream);
        }
       
        public void SendMessage(string strMessage)
        {
            string strName = "["+ MyForm.MainForm.GetNameBox().Text +"] ";

            JsonObjectCollection JsonCollection = new JsonObjectCollection();
            JsonCollection.Add(new JsonStringValue("Nick", strName));
            JsonCollection.Add(new JsonStringValue("Chat", strMessage));
            string strSendMessage = JsonCollection.ToString();

            byte[] ByteMessage = System.Text.Encoding.Default.GetBytes(strSendMessage.ToCharArray());
            ClientInfo.MyNetWorkStream.Write(ByteMessage, 0, ByteMessage.Length);
            ClientInfo.MyNetWorkStream.Flush();
        }

        private void Receive_Thread()
        {
            byte[] byteMessage = new byte[BUFFER_SIZE];
            BinaryFormatter BF = new BinaryFormatter();
            NetworkStream stream = ClientInfo.MyNetWorkStream;
           
            while (true)
            {
                try
                {
                    ClientInfo.MyNetWorkStream.Read(byteMessage, 0, byteMessage.Length);
                    MemoryStream MemStream = new MemoryStream(byteMessage);
                   
                    JsonTextParser JsonParser = new JsonTextParser();                   
                    JsonObject     JsonObj    = JsonParser.Parse(Encoding.Default.GetString(byteMessage));
                    JsonObjectCollection JsonCol = (JsonObjectCollection)JsonObj;

                    string Nick = (string)JsonCol["Nick"].GetValue();
                    string Chat = (string)JsonCol["Chat"].GetValue();
                    string StrChat = Nick + Chat;
                    Print_Text( StrChat );
                }
                catch
                {                  
                    break;
                }
            }
        }

        public void SetNickName( string strName )
        {
            if( null == strName || "" == strName )
            {
                return;
            }
            this.ClientInfo.NickName = strName;
        }
        public void Print_Text(string strMessage)
        {
            if (MyForm.MainForm.GetTextBox_Chat().InvokeRequired)
            {
                DText call = new DText(Print_Text);
                MyForm.MainForm.GetTextBox_Chat().Invoke(call, strMessage);
            }
            else
            {
                MyForm.MainForm.GetTextBox_Chat().AppendText(strMessage + "\r\n");
                MyForm.MainForm.FocusToType();
            }           
        }

        public bool IsClientConnected()
        {
            return ClientInfo.MyClientSocket.Connected;
        }

        public void DisconnectFromServer()
        {
            ClientInfo.MyStreamReader.Close();
            ClientInfo.MyStreamWriter.Close();
            ClientInfo.MyNetWorkStream.Close();
            ClientInfo.MyClientSocket.Close();
        }       
    }   
}

Posted by 비엔나햄
내가만든/라인들2016. 2. 28. 16:23

C++/IOCP

메인 코드만 저장. 나머지 내용은 SVN에 있음.

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <windows.h>
#include <process.h>

#define MESSAGE_SIZE 100
#define MAX_CLIENT 3

using namespace std;
struct E_DEFINE
{
 enum ENUM
 {
  MAX_USER = 3,
 };
};
// 전역 변수
SOCKET stClientSocket[MAX_CLIENT]; // 접속중인 클라이언트 소켓
int g_nClientCnt = 0;    // 접속중인 클라이언트의 수
HANDLE g_hIocp = 0;     // IOCP 핸들 값

// 소켓 정보 구조체 OVERLAPPED로 캐스팅 되어서 넘어간다
typedef struct SocketInfo
 : public OVERLAPPED
{
 WSAOVERLAPPED m_OverLapped;
 SOCKET m_sSocket;
 WSABUF m_WsaDataBuf;
 int m_nReadLen;
 int m_nWriteLen; 
 char m_cBuff[MESSAGE_SIZE];
 SOCKADDR_IN m_siSocketAddr;
}SOCKETINFO, *PSOCKETINFO;

void SendMSG(char * message, int len, SOCKETINFO * pSocketInfo ); // 접속중인 유저들에게 패킷 broadcast 함수
DWORD WINAPI Thread_Func( LPVOID parm );       // IOCP 입력 결과에 의해서 사용될 함수
void ClientDisconnect( SOCKETINFO * pSocketInfo );     // 클라이언트 접속 종료 처리 함수
void ConsoleLog( char * strLOG );         // 콘솔 로그 출력 함수

void main()
{
 ConsoleLog("Server Start");

 WSADATA wsaData;
 SOCKET hServSock;
 SOCKADDR_IN servAddr, clntAddr;
 SOCKETINFO * pSocketInfo;

 char message[] = "Hello World";
 char RecvBuff[100];
 char szPort[] = "50001";

 HANDLE hThread;
 DWORD hThreadID;
 
 if( 0 != WSAStartup(MAKEWORD( 2, 2 ), &wsaData ) )
 {
  ConsoleLog( "StartUp ERROR!" );
 }

 hServSock = socket( PF_INET, SOCK_STREAM, 0 );
 if( INVALID_SOCKET == hServSock )
 {
  ConsoleLog( "Socket Open ERROR!" );
 }

 ZeroMemory( &servAddr, sizeof(servAddr) );
 servAddr.sin_family = AF_INET;
 servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
 servAddr.sin_port = htons(atoi(szPort));

 if( SOCKET_ERROR == bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)))
 {
  ConsoleLog( "Bind ERROR!" );
 }

 if( SOCKET_ERROR == listen( hServSock, 5 ))
 {
  ConsoleLog( "Listen ERROR!" );
 }

 g_hIocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );

 SYSTEM_INFO stSysInfo;
 GetSystemInfo( &stSysInfo );
 const int dwProcessors = stSysInfo.dwNumberOfProcessors * 2 + 2;
 for( int i = 0; i < dwProcessors; i++ )
 {
  hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)Thread_Func, 0, 0, &hThreadID );
 }

 while(TRUE)
 { 
  SOCKET hClientSock;
  SOCKADDR_IN hSockAddr;

  int nRead = MESSAGE_SIZE;
  int nFlags = 0;
  int nSockAddrLen = sizeof( hSockAddr );

  hClientSock = accept(hServSock, (SOCKADDR*)&hSockAddr, &nSockAddrLen );
  if( INVALID_SOCKET == hClientSock )
  {
   ConsoleLog( "Accept ERROR!" );
  }

  pSocketInfo = new SOCKETINFO;
  pSocketInfo->m_nReadLen  = 0;
  pSocketInfo->m_nWriteLen = 0;
  pSocketInfo->m_sSocket  = hClientSock;
  pSocketInfo->m_cBuff[0]  = '\0';
  memcpy( &(pSocketInfo->m_siSocketAddr), &hSockAddr, nSockAddrLen );
  
  ZeroMemory( &(pSocketInfo->m_OverLapped), sizeof(pSocketInfo->m_OverLapped) );
  pSocketInfo->m_WsaDataBuf.len = MESSAGE_SIZE;
  pSocketInfo->m_WsaDataBuf.buf = pSocketInfo->m_cBuff;

  g_hIocp = CreateIoCompletionPort( (HANDLE)hClientSock , g_hIocp, (unsigned long)pSocketInfo, 0 );
  if( nullptr == g_hIocp )
  {
   ConsoleLog( "CreateIoCompletion ERROR!" );
   delete pSocketInfo;
   continue;
  }

  if( SOCKET_ERROR == WSARecv( pSocketInfo->m_sSocket, &(pSocketInfo->m_WsaDataBuf), 1, (LPDWORD)(&pSocketInfo->m_nReadLen), (LPDWORD)(&nFlags), &(pSocketInfo->m_OverLapped), NULL ) );
  {
   int nRet = WSAGetLastError();
   if( WSAGetLastError() != WSA_IO_PENDING )
   {
    ConsoleLog( "WSARecv ERROR!" );
    delete pSocketInfo;
    continue;
   }
  }

  OVERLAPPED stOverLapped;
  WSABUF dataBuf;
  dataBuf.buf = message;
  dataBuf.len = strlen(message);
  ZeroMemory( &stOverLapped, sizeof(OVERLAPPED) );
  DWORD dwBufferProgress;
  if( WSASend( hClientSock, &dataBuf, 1, (LPDWORD)&dwBufferProgress, 0, NULL, NULL )  == SOCKET_ERROR )
  {
   if( WSAGetLastError() != WSA_IO_PENDING )
   {
    ConsoleLog( "WSASend ERROR" );
   }
  }  

  // 클라이언트 빈자리 검사 및 인원 제한
  int nEmptyClientSlot = 0;
  for( int nEmptyClientSlot = 0; nEmptyClientSlot<MAX_CLIENT; nEmptyClientSlot++ )
  {
   if( 0 == stClientSocket[nEmptyClientSlot] )
   {
    stClientSocket[g_nClientCnt] = hClientSock;
    char strClinetInfo[100]  = "";  
    sprintf( strClinetInfo, "Client %d accept || IP : %s", g_nClientCnt, inet_ntoa(hSockAddr.sin_addr) );
    ConsoleLog( strClinetInfo );
    g_nClientCnt++;
    break;
   }
  }  
 }

 // Socket Close
 closesocket(hServSock);
 WSACleanup();

 return;
}

// 접속중인 전체 유저에게 Send하는 함수
void SendMSG(char * message, int len, SOCKETINFO * pSocketInfo )
{
 DWORD dwBufferProgress;
 int nRetVal ;
 int nError;
 for( int i = 0; i < g_nClientCnt; i++ )
 { 
  if( 0 == stClientSocket[i] )
  {
   continue;
  }
  nRetVal = WSASend( stClientSocket[i], &pSocketInfo->m_WsaDataBuf, 1, &dwBufferProgress, 0, NULL, NULL );
  nError = WSAGetLastError();
  //send( stClientSocket[i], pSocketInfo->m_WsaDataBuf.buf, len, 0);
 }
}

DWORD WINAPI Thread_Func(LPVOID parm)
{
 HANDLE hCompletionPort = (HANDLE)parm;

 DWORD dwTransFerred = 0;
 int nFlag = 0;
 int nKey = 0;

 int nRet = 0;
 int nErrorNum = 0;

 SOCKETINFO * pSocketInfo;
 OVERLAPPED *pOverLapped;

 while(TRUE)
 {
  GetQueuedCompletionStatus( g_hIocp, (LPDWORD)&dwTransFerred, (LPDWORD)&nKey, (LPOVERLAPPED*)(&pOverLapped), INFINITE );
  pSocketInfo = (SOCKETINFO*)nKey;

  if( 0 == dwTransFerred )
  {
   ClientDisconnect( pSocketInfo );
   closesocket( pSocketInfo->m_sSocket );
   delete pSocketInfo;
   continue;
  }

  if( nullptr == pSocketInfo )
  {
   continue;
  }

  if( 0 != dwTransFerred )
  {
   SendMSG(pSocketInfo->m_cBuff, dwTransFerred, pSocketInfo );
  }

  ZeroMemory( &(pSocketInfo->m_OverLapped), sizeof( OVERLAPPED ));
  pSocketInfo->m_WsaDataBuf.len = MESSAGE_SIZE;
  pSocketInfo->m_WsaDataBuf.buf = pSocketInfo->m_cBuff;
  nFlag = 0;


  nRet = WSARecv( pSocketInfo->m_sSocket, &(pSocketInfo->m_WsaDataBuf), 1, (LPDWORD)(&dwTransFerred), (LPDWORD)(&nFlag), &(pSocketInfo->m_OverLapped), NULL ); 
  nErrorNum = WSAGetLastError();
 }
 return 0;
}

void ClientDisconnect( SOCKETINFO * pSocketInfo )
{
 if( nullptr == pSocketInfo )
 {
  return;
 }

 char strClinetInfo[100]  = "";
 for( int i = 0; i < MAX_CLIENT; i++ )
 {
  if( pSocketInfo->m_sSocket == stClientSocket[i] )
  {
   sprintf( strClinetInfo, "Client %d Disconnect || IP : %s", i, inet_ntoa(pSocketInfo->m_siSocketAddr.sin_addr) );

   stClientSocket[i] = 0;
   g_nClientCnt--;
   if( g_nClientCnt < 0 )
   {
    g_nClientCnt = 0;
   }
   break;
  }
 } 
 
 ConsoleLog( strClinetInfo );
}
void ConsoleLog( char * strLOG = nullptr )
{  
 SYSTEMTIME stSystemTime;
 GetLocalTime( &stSystemTime );
 char strTime[200] = "";

 if( nullptr == strLOG )
 {
  strLOG = "";
 }

 sprintf_s( strTime, "[%d%02d%02d] %s",
  stSystemTime.wYear,
  stSystemTime.wMonth,
  stSystemTime.wDay,
  strLOG);

 cout<< strTime << endl;
}

Posted by 비엔나햄