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); } } } }