123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- using System;
- using System.Text;
- using System.Collections;
- using System.Net.Sockets;
- using System.Net;
- using System.Threading;
- using System.Windows.Forms;
- using System.Net.NetworkInformation;
- //using System.Management.Instrumentation;
- //using System.Management;
- namespace FPER
- {
- // TCP 공통 루틴은 여기에서 구현한다
- // 출입통제 전용 소프트웨어간 통신을 위해 구현된다 (1개의 PC에 포트별로 하나의 프로그램이 연결된다)
- // 소켓의 세션을 유지하여 운영된다 (클라이언트용)
- public class _TcpComClient // cyim 2015.8.4 수신반을 위한 static 클래스 정리
- {
- // cyim 2015.8.4 수신반을 위한 static 클래스 정리
- MDIParent mdi = null;
- // 접속 응답 대기 시간 MicroSecond
- public const int WaitPollTime = 10000; // 사용안함
- // 수신 버퍼 크기
- public const int RxBufSize = 64; // 소방은 작다 (1개당 4byte 가 필요하므로 16 * 4) // cyim 2014.8.11 : 소방 수신 버퍼 사이즈 변경
- // 소켓 접속 혹은 종료 알림 이벤트
- public delegate void SocketConnect_Inform_Message_Handler(object data, bool Connect);
- public event SocketConnect_Inform_Message_Handler SocketConnect_Inform_Event;
- // TxConnect 클라이언트 소켓을 관리하기 위한 해쉬테이블 (클라이언트용)
- public Hashtable TxConnect_Socket = new Hashtable();
- // TxConnect 클라이언트 소켓세션이 종료되었는지 관리 (클라이언트용 : TxConnect_Socket 동일하게 생성 및 삭제가 됨)
- public Hashtable TxConnect_KeepTimer = new Hashtable();
- // 생성자
- public _TcpComClient(MDIParent mdiparent)
- {
- mdi = mdiparent;
- }
- // 소멸자
- ~_TcpComClient()
- {
- }
- // 클라이언트로 동작시 Connect 소켓 생성
- public void Create_TxConnect_Socket(string Key, Socket RxSocket, System.Threading.Timer Timer)
- {
- if (TxConnect_Socket.Contains(Key) == true)
- {
- //_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + Key + " Socket " + "이미 키가 중복입니다");
- }
- else
- {
- // 해쉬에 추가
- //TxConnect_KeepTimer.Add(Key, Timer);
- TxConnect_Socket.Add(Key, RxSocket);
- }
- }
- // TxConnect_Socket 소켓 삭제
- public void Delete_TxConnect_Socket(string Key)
- {
- // 모두 삭제
- if (Key == null)
- {
- foreach (DictionaryEntry d in TxConnect_Socket)
- ((Socket)d.Value).Dispose();
- //foreach (DictionaryEntry d in TxConnect_KeepTimer)
- // ((System.Threading.Timer)d.Value).Dispose();
- //TxConnect_KeepTimer.Clear();
- TxConnect_Socket.Clear();
- }
- // 부분 삭제
- else
- {
- if (TxConnect_Socket.Contains(Key) == true)
- {
- // 소켓 삭제
- if (((Socket)TxConnect_Socket[Key]) != null)
- ((Socket)TxConnect_Socket[Key]).Close();
- // 타이머도 삭제
- //if (((System.Threading.Timer)TxConnect_KeepTimer[Key]) != null)
- // ((System.Threading.Timer)TxConnect_KeepTimer[Key]).Dispose();
- //// 해쉬에서 삭제
- //TxConnect_KeepTimer.Remove(Key);
- TxConnect_Socket.Remove(Key);
- }
- }
- }
- // 수신 큐
- public Queue RxQueue = new Queue();
- // 발신 큐
- public Queue TxQueue = new Queue();
- // Critical Section
- public Object CS_TxQueue = new Object();
- public Object CS_RxQueue = new Object();
- // 종단 포맷 (소켓처리용)
- public class EndPointFormat
- {
- public string IP = null;
- public string Port = null;
- public byte[] Msg = null;
- }
- // 전송 포맷 (메세지처리용)
- public class SendFormat
- {
- public string IP = null;
- public string Port = null;
- public string Msg = null;
- }
- // 카드홀더 전송 포맷
- public class SendFormat_CardHolder
- {
- public string IP = null;
- public string Port = null;
- public string Msg = null;
- public string Protocol = null;
- public string UID= null;
- }
-
- //
- // Queue
- //
- public void RxQueue_ADD(EndPointFormat Data)
- {
- lock (CS_RxQueue)
- {
- RxQueue.Enqueue(Data);
- }
- }
- public void TxQueue_ADD(SendFormat Data)
- {
- lock (CS_TxQueue)
- {
- TxQueue.Enqueue(Data);
- }
- }
- //
- // Socket Status 알아내기
- //
- //public bool Socket_Status(Socket socket)
- //{
- //try
- //{
- //if (socket == null) return false;
- //bool part1 = socket.Poll(1000, SelectMode.SelectRead);
- //bool part2 = socket.Available == 0;
- //if (part1 & part2)
- // return false;
- //else
- // return true;
- //}
- //catch (Exception ex)
- //{
- //Util.UErrorMessage(ex, 0, 0);
- //return false;
- //}
- //}
- //
- // RX
- //
- // 서버에 접속하여 메세지를 수신할수 있도록 대기
- public bool Socket_Connect(string DestIP, string DestPort)
- {
- // Connect 된 소켓은 아이피와 포트를 키로 두고 처리한다
- string TxConnect_Socket_Key = DestIP + ":" + DestPort;
- try
- {
- // 연결된 소켓이 없다면 생성
- if (TxConnect_Socket.ContainsKey(TxConnect_Socket_Key) == false)
- {
- // 타겟이 접속이 가능한지 체크해서 소켓을 생성, 안에서 이미 소켓을 해쉬에 생성해버린다
- Socket TxSocket = Send_Check(DestIP, DestPort, WaitPollTime);
- if (TxSocket != null)
- {
- // 포맷 생성
- EndPointFormat RxQueFormat = new EndPointFormat();
- // IP
- RxQueFormat.IP = DestIP;
- // PORT
- RxQueFormat.Port = DestPort;
- // 상대방의 메시지 수신을 기다리는 스레드를 생성한다
- mdi.Thread.Abort(TxConnect_Socket_Key);
- mdi.Thread.Create(TxConnect_Socket_Key, Socket_Receive_byThread, RxQueFormat);
- //_Data.Exception_Log_Display = true;
- return true;
- }
- else
- return false;
- }
- else
- {
- // 있더라도 접속이 가능한지 체크
- //if (Socket_Status(((Socket)TxConnect_Socket[TxConnect_Socket_Key])) == false)
- if (Ping_SyncCheck(DestIP) == false)
- {
- // 소켓 해제
- Socket_Close(TxConnect_Socket_Key);
- return false;
- }
- else
- {
- return true;
- }
- }
- }
- catch (Exception ex)
- {
- Util.UErrorMessage(ex, 0, 0);
- // 특별처리 : 접속이 된상태에서 오류가 발생하면 출력
- //if (_Data.Exception_Log_Display == true)
- //{
- // // 1회만 로그 출력
- // _Data.Exception_Log_Display = false;
-
- // LOG
- //_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + ex.ToString());
- //}
- return false;
- }
- }
- // RX : 메세지 수신 스레드 (접속 요청이 들어오면 메세지를 받는 스레드)
- public void Socket_Receive_byThread(object QueFormat)
- {
- EndPointFormat Dest = (EndPointFormat)QueFormat;
- string TxConnect_Socket_Key = Dest.IP + ":" + Dest.Port;
- //while (Socket_Status((Socket)TxConnect_Socket[TxConnect_Socket_Key]) == true)
- while (true)
- {
- try
- {
- // 버퍼 생성
- Byte[] ReceiveByte = new Byte[RxBufSize];
- // 블럭킹
- ((Socket)TxConnect_Socket[TxConnect_Socket_Key]).Blocking = true;
- // 이는 메세지를 받기 위한 전용 소켓이다..
- int RxDataCnt = ((Socket)TxConnect_Socket[TxConnect_Socket_Key]).Receive(ReceiveByte, ReceiveByte.Length, SocketFlags.None);
- // 수신 버퍼 갯수
- if (RxDataCnt > 0)
- {
- // 수신용 소켓 정보
- EndPointFormat RxQueFormat = new EndPointFormat();
- // IP
- RxQueFormat.IP = Dest.IP;
- // Port
- RxQueFormat.Port = Dest.Port;
- // Message
- RxQueFormat.Msg = new Byte[RxDataCnt];
- // 버퍼 개수만큼 저장
- for (int i = 0; i < RxDataCnt; i++)
- RxQueFormat.Msg[i] = ReceiveByte[i];
- // 메세지큐에 삽입한다
- //RxQueue_ADD(RxQueFormat);
- // 임시 작업 (추후 삭제)
- string Rx = _Convert.DecodingData(_Convert.Coding.UTF8, RxQueFormat.Msg);
- ////_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + TxConnect_Socket_Key + ":Rx:" + Rx);
- mdi.Event.ClientSocketReceive_SendMessage_Write(Rx);
- }
- // 특별처리 간혹 소켓을 종료하지 못하고 죽는 서버프로그램도 존재한다
- if (RxDataCnt == 0)
- {
- throw new Exception("server shutdown. not socket close");
- }
- }
- catch (Exception ex)
- {
- Util.UErrorMessage(ex, 0, 0);
- // 로그
- //mdi.Event.DebugView_SendMessage_Write("[C]" + ex.ToString());
- // 소켓 해제
- Socket_Close(TxConnect_Socket_Key);
- break;
- }
- }
- }
- // 1초 지연후 3초마다 실행되는 스레드 타이머
- //public void SocketCheckedTimer_Tick(object d)
- //{
- //try
- //{
- // 키값은 소켓의 키:포트
- //string TxConnect_Socket_Key = (string)d;
- //string[] IP_Port = TxConnect_Socket_Key.Split(':');
- // 체크해본다
- //if (Ping_SyncCheck(IP_Port[0]) == false || Socket_Status(((Socket)TxConnect_Socket[TxConnect_Socket_Key])) == false)
- //{
- // 스레드 타이머
- //System.Threading.Timer t = (System.Threading.Timer)TxConnect_KeepTimer[TxConnect_Socket_Key];
- // 클라이언트가 종료되면 메세지 출력
- //_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + "Disconnected : " + ((Socket)TxConnect_Socket[TxConnect_Socket_Key]).LocalEndPoint + "-" + TxConnect_Socket_Key);
- // 해쉬 삭제
- //TxConnect_Socket.Remove(TxConnect_Socket_Key);
- //TxConnect_KeepTimer.Remove(TxConnect_Socket_Key);
- // 소켓 상태 알림 이벤트
- //if (SocketConnect_Inform_Event != null) SocketConnect_Inform_Event(TxConnect_Socket_Key, false);
- // 스레드 종료
- //mdi.Thread.Abort(TxConnect_Socket_Key);
- // 타이머종료
- //if (t != null)
- // t.Dispose();
- //}
- // }
- //catch (Exception ex)
- //{
- //Util.UErrorMessage(ex, 0, 0);
- //}
- //}
- //
- // TX
- //
- // TX : 메세지 전송 스레드 생성 : 메세지를 생성할때마다 스레드를 발생시키는 방식
- public void Socket_Send(string IP, string Port, byte[] Message)
- {
- // 소켓 생성
- EndPointFormat TxQueFormat = new EndPointFormat();
- // IP
- TxQueFormat.IP = IP;
- // PORT
- TxQueFormat.Port = Port;
- // 버퍼 생성
- TxQueFormat.Msg = new Byte[Message.Length];
- // 버퍼 저장
- TxQueFormat.Msg = Message;
- // 스레드 생성
- Thread Socket_Send_Thread = new Thread(new ParameterizedThreadStart(Socket_Send_byThread));
- // 스레드시작
- Socket_Send_Thread.Start(TxQueFormat);
- }
- // TX : 메세지 전송 스레드 함수 (접속 시도 횟수 없음)
- public void Socket_Send_byThread(object QueFormat)
- {
- // 메세지 전송
- EndPointFormat TxQueFormat = (EndPointFormat)QueFormat;
- Socket TxSocket = null;
- try
- {
- // 타겟이 접속이 가능한지 체크
- TxSocket = Send_Check(TxQueFormat.IP, TxQueFormat.Port, WaitPollTime);
- // 가능하다
- if (TxSocket != null)
- {
- // Tx 전송
- Send_Data(TxSocket, TxQueFormat);
- }
- else
- {
- // LOG
- //_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + TxQueFormat.IP + ":" + TxQueFormat.Port + "전송이 실패합니다");
- }
- }
- catch // (Exception e)
- {
- // LOG
- //_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + e.ToString());
- }
- }
- // TX : 메세지 전송시 상대방에게 접속가능한지 체크
- public Socket Send_Check(string DestIP, string DestPort, int timeoutMs)
- {
- // 키값은 소켓의 키:포트
- string TxConnect_Socket_Key = DestIP + ":" + DestPort;
- try
- {
- // 기존에 사용중인 소켓이 있는 경우에는 그 소켓을 그대로 사용하고 만약 없다면 재생성하도록 한다
- if (TxConnect_Socket.ContainsKey(TxConnect_Socket_Key) == true)
- {
- Socket socket = (Socket)TxConnect_Socket[TxConnect_Socket_Key];
- // 기존의 소켓이라고 해도 접속이 끊어져있다면 삭제
- if (Ping_SyncCheck(DestIP) == false)
- {
- // 소켓 해제
- Socket_Close(TxConnect_Socket_Key);
- return null;
- }
- else
- return socket;
- }
- else
- {
- // 상대방에게 접속할수 있는지 알아보기 위한 임시 소켓
- Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- // 타켓 주소 설정
- IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(DestIP), Int32.Parse(DestPort));
- //if (Socket_Status(socket) == true)
- //{
- // 접속시도
- socket.Connect(endPoint);
- //_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + "Connected :" + TxConnect_Socket_Key);
- // 소켓 테스트 타이머
- //System.Threading.Timer SocketCheckedTimer = new System.Threading.Timer(SocketCheckedTimer_Tick, TxConnect_Socket_Key, 1000, 3000);
- // 소켓 해쉬 테이블에 추가 (키는 PortNum)
- Create_TxConnect_Socket(TxConnect_Socket_Key, socket, null);//SocketCheckedTimer);
- // 소켓 상태 알림 이벤트
- if (SocketConnect_Inform_Event != null) SocketConnect_Inform_Event(TxConnect_Socket_Key, true);
- return socket;
- //}
- //else
- //{
- //// 소켓을 삭제
- //Delete_TxConnect_Socket(TxConnect_Socket_Key);
- //// 로그
- ////_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + "Disconnected :" + TxConnect_Socket_Key);
- //// 스레드 종료
- //_Thread.Abort(TxConnect_Socket_Key);
- //// 소켓 상태 알림 이벤트
- //if (SocketConnect_Inform_Event != null) SocketConnect_Inform_Event(TxConnect_Socket_Key, true);
- //return null;
- //}
- }
- }
- catch
- {
- // 소켓 해제
- Socket_Close(TxConnect_Socket_Key);
- return null;
- }
- }
- // TX : 메세지 전송
- public void Send_Data(Socket TxSocket, EndPointFormat TxQueFormat)
- {
- // 키값은 소켓의 키:포트
- string TxConnect_Socket_Key = TxQueFormat.IP + ":" + TxQueFormat.Port;
- try
- {
- int TxDataCnt = 0;
- // 블럭킹모드 사용 (이전에 poll 함수를 통한 블럭킹을 해제시켰음)
- TxSocket.Blocking = true;
-
- // 전송
- TxDataCnt = TxSocket.Send(TxQueFormat.Msg, 0, TxQueFormat.Msg.Length, 0);
- // 전송후 메세지를 정확히 보냈다면!
- if (TxDataCnt == TxQueFormat.Msg.Length)
- {
- // 임시 작업 (추후 삭제)
- string Tx = _Convert.DecodingData(_Convert.Coding.UTF8, TxQueFormat.Msg);
- //_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + TxQueFormat.IP + ":" + TxQueFormat.Port + ":Tx:" + Tx);
- }
- else
- {
- // LOG
- //_Event.DebugView_SendMessage_Write(e.ToString());("[C]" + TxQueFormat.IP + ":" + TxQueFormat.Port + "전송중 실패합니다");
- }
- }
- catch //(Exception e)
- {
- // 소켓 해제
- Socket_Close(TxConnect_Socket_Key);
- }
- }
- //
- // Ping 체크 (동기식)
- //
- public PingOptions options = new PingOptions(); // cyim 2017.01.02 : Memory leak
- public bool Ping_SyncCheck(string DestIP)
- {
- try
- {
- if (DestIP == null || DestIP.Trim().Length == 0) return false;
- using (Ping pingSender = new Ping()) // cyim 2017.01.02 : Memory leak
- {
- options.DontFragment = true;
- string data = "0";
- byte[] buffer = Encoding.ASCII.GetBytes(data);
- // 2초
- PingReply reply = pingSender.Send(DestIP, 500, buffer, options);
- if (reply.Status == IPStatus.Success)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- }
- catch (Exception ex)
- {
- Util.UErrorMessage(ex, 0, 0);
- return false;
- }
- }
-
- // 소켓 해제시 동작
- public void Socket_Close(string TxConnect_Socket_Key)
- {
- try
- {
- // 소켓을 삭제
- Delete_TxConnect_Socket(TxConnect_Socket_Key);
- // 소켓 상태 알림 이벤트
- if (SocketConnect_Inform_Event != null) SocketConnect_Inform_Event(TxConnect_Socket_Key, false);
- // 로그
- //if (TxConnect_Socket_ErrLog.Contains(TxConnect_Socket_Key) == false)
- // {
- // TxConnect_Socket_ErrLog.Add(TxConnect_Socket_Key);
- // _Event.DebugView_SendMessage_Write("[C]" + "Disconnected :" + TxConnect_Socket_Key);
- //}
- // 스레드 종료
- mdi.Thread.Abort(TxConnect_Socket_Key);
- }
- catch (Exception ex)
- {
- Util.UErrorMessage(ex, 0, 0);
- }
- }
-
- }
- }
|