/// <summary> /// 송신을 시작하라고 명령하는 함수입니다. /// 현재 송신 기능은 보내야할 메세지를 작업형태로 만들고 '공존 큐'에 넣어서 관리하고 있기 때문에 사용합니다. /// 이 함수는 내부 로직에 의해 동기적으로 송신하므로 Task와 조합하여 사용해야합니다. /// </summary> private async static void StartSendingQueue() { byte[] currentMessage; ConnectInfo ci = ConnectInfo.getInstance(); try // 작업을 진행하는 서버가 접속종료를 해서 오류가 날 경우가 있다. // 그리고 다른 함수 절차에 따라 프로그램이 종료되려고 할때 이 정보가 말소 될 가능성이 있다. // 이때 말소된 정보에 접근하려하면 프로그램이 죽을 것이다. 이를 방지한다. { do { if (!ci.sendMsgQueue.TryDequeue(out currentMessage)) { // 꺼내오는데 실패하면 대기하고 재시도하게 한다. await Task.Delay(ClientInfo.getInstance().sleepTime); continue; } SendingFunction(ci.tcpClient.Client, currentMessage); } while (ci.sendMsgQueue.Count > 0); } catch { } }
/// <summary> /// 서버명을 요청한 후 수신받아 기록합니다. 절차는 동기식으로 작동합니다. /// 이 함수를 사용하면 인증 가능한 상태라고 초기화 되기 때문에, /// 처음 접속시가 아니면 사용하지 마십시오. /// </summary> /// <returns>성공 여부를 나타냅니다.</returns> public static bool ReceiveDefaultInfo() { string serverName; System.Security.Cryptography.RSAParameters parameters; int index = 0; byte[] data; List <GameInfo> reqInfos; ConnectInfo ci = ConnectInfo.getInstance(); ClientInfo cInfo = ClientInfo.getInstance(); data = CustomProtocolManager.MakeConnectRequestPacket(); if (!SendingFunction(ci.tcpClient.Client, data)) { return(false); } // 헤더 수신 index = 0; data = new byte[CustomProtocolManager.HEADER_SIZE]; if (!ReceiveFunction(ci.tcpClient.Client, ref data, ref index, CustomProtocolManager.HEADER_SIZE)) { return(false); } // 수신된 헤더 확인 CustomProtocolManager.Header header = CustomProtocolManager.ParseHeader(ref data); // 데이터 수신 index = 0; data = new byte[header.size]; if (!ReceiveFunction(ci.tcpClient.Client, ref data, ref index, (int)header.size)) { return(false); } CustomProtocolManager.ParseClientReqInfoPacket(data, 0, (UInt32)data.Length, out serverName, out parameters, out reqInfos); ci.ServerName = serverName; ci.securityParameter = parameters; // 필요 게임들이 존재하는지 확인 DirectoryInfo di = new DirectoryInfo(ClientInfo.GAMEDATA_PATH); FileStream fs; XmlSerializer xs = new XmlSerializer(typeof(GameInfo)); GameInfo registry; foreach (DirectoryInfo sdi in di.GetDirectories()) { foreach (FileInfo fi in sdi.GetFiles()) { if (!fi.Extension.Equals(".info")) { continue; } fs = fi.OpenRead(); registry = (GameInfo)xs.Deserialize(fs); for (int i = 0; i < reqInfos.Count; ++i) { if (GameInfo.IsSameInfo(reqInfos[i], registry)) { cInfo.gameRegistries.Add(new GameRegistry { gameInfo = registry, folderPath = fi.DirectoryName }); reqInfos.RemoveAt(i); } } fs.Close(); } } // 필요 정보가 남아있으면 필요한 게임이 없으므로 서버 이용 불가 if (reqInfos.Count != 0) { string msg; foreach (GameInfo gi in reqInfos) { msg = "이 서버에 접속하기 위해서는 다음의 게임이 필요합니다." + "\n게임명: " + gi.name + "\n버전: " + gi.gameVersion + "\n제작자: " + gi.producer; MessageBox.Show(msg, "파일 누락"); } return(false); } return(true); }
public ConnectForm() { InitializeComponent(); ci = ConnectInfo.getInstance(); }
/// <summary> /// 비동기 수신 메서드로부터 반응이오면 실행되는 콜백 함수입니다. /// 해당 콜백 함수는 MainFrame에서 사용되어야 합니다. /// </summary> /// <param name="ar">송신정보를 담아 생성되는 인터페이스 객체입니다.</param> public static void MainFrameMessageReceiveCallback(IAsyncResult ar) { #region 메세지 수신 ConnectInfo ci = (ConnectInfo)ar.AsyncState; CustomProtocolManager.Header header; byte[] receivedData; try { ci.recvHeaderReaded += ci.tcpClient.Client.EndReceive(ar); if (ci.tcpClient.Connected == false) { throw new Exception(); // 연결이 끊기면 메세지를 버리라고 알림 } if (ci.recvHeaderReaded < CustomProtocolManager.HEADER_SIZE) // 메세지가 헤더의 원본 크기만큼 안왔다면 비동기적으로 더 수신하라고 명령한다 { ci.tcpClient.Client.BeginReceive(ci.recvHeaderBuffer, ci.recvHeaderReaded, CustomProtocolManager.HEADER_SIZE - ci.recvHeaderReaded, 0, MainFrameMessageReceiveCallback, ci); return; } // 헤더의 원본 크기만큼 읽었거나 그 이상 읽은경우는 이 구역을 실행한다. header = CustomProtocolManager.ParseHeader(ref ci.recvHeaderBuffer); int received = 0; receivedData = new byte[header.size]; if (!ReceiveFunction(ci.tcpClient.Client, ref receivedData, ref received, (int)header.size)) { throw new Exception();// 동기 수신에 실패하면 연결에 문제가 있으므로 예외처리 } // 서버랑 다르게 처리하기 전에 비동기 수신 이벤트를 새로 등록한다. ci.recvHeaderReaded = 0; // 인덱스 초기화 } catch (Exception e) { // 동작에 오류가 있었다면 통신에 문제가 있는 것이므로 프로그램을 재시작한다. Console.WriteLine("MainFrameMessageReceiveCallback: " + e.Message); MessageBox.Show("서버와의 통신에 이상이 생겨 접속이 끊겼습니다." + "\n본 시스템의 네트워크에 이상이 없다면 " + "서버가 오프라인이 되었을 가능성이 높습니다.", "통신 오류", MessageBoxButtons.OK, MessageBoxIcon.Error); Application.Exit(); return; } #endregion #region 메세지 처리 switch (header.typeNumber) { case CustomProtocolManager.TypeNumber.Chatting: ChattingHandleFunction(header, receivedData); break; case CustomProtocolManager.TypeNumber.ServerMessage: ServerMessageHandleFunction(receivedData); break; case CustomProtocolManager.TypeNumber.InfoReq: InfoTypeMsgHandleFunction(header, receivedData); break; case CustomProtocolManager.TypeNumber.InfoChange: InfoChangeMsgHandleFunction(header, receivedData); break; // 미사용 메세지 처리 구간 case CustomProtocolManager.TypeNumber.MakeConnection: // 기초정보 제공은 앞선 폼에서 모두 사용한다 case CustomProtocolManager.TypeNumber.Authorization: // 인증방식은 로그인과 회원가입에서 모두 처리하는 방식이다 default: break; // 어느쪽에도 속하지 않으면 메세지를 버림 } #endregion try { ci.tcpClient.Client.BeginReceive(ci.recvHeaderBuffer, 0, CustomProtocolManager.HEADER_SIZE, 0, MainFrameMessageReceiveCallback, ci); } catch (Exception e) { Console.WriteLine("MainFrameMessageReceiveCallback: " + e.Message); MessageBox.Show("서버와의 통신에 이상이 생겨 접속이 끊겼습니다." + "\n본 시스템의 네트워크에 이상이 없다면 " + "서버가 오프라인이 되었을 가능성이 높습니다.", "통신 오류", MessageBoxButtons.OK, MessageBoxIcon.Error); Application.Exit(); return; } }
public LoginForm() { InitializeComponent(); ci = ConnectInfo.getInstance(); txt_ServerName.Text = ci.ServerName; }