/// <summary> /// 인스턴스를 만듭니다. /// </summary> /// <param name="serverMode">서버모드인지에 대한 여부</param> /// <param name="arg2">서버모드라면 자신을 제외한 클라이언트 개수, 클라이언트라면 서버의 주소</param> /// <returns>생성 여부</returns> /// <exception cref="SocketException">소켓을 연결하는 도중 오류가 발생하여 게임을 준비할 수 없습니다.</exception> internal static bool MakeConnections(bool serverMode, string arg2, User myInfo) { if (instance != null) { return(false); } UserInfos ui = UserInfos.getInstance(); ui.users.TryAdd(myInfo.Nickname, myInfo); ui.myNickname = myInfo.Nickname; #region 서버 작업 if (serverMode) { int userCount; TcpListener listener = new TcpListener(System.Net.IPAddress.Any, SERVER_PORT_NUMBER); Int32.TryParse(arg2, out userCount); Thread ConnectThread; instance = new Connections(serverMode, userCount); #region P2P 연결 영역 // P2P 연결을 담당하는 블럭이다. 이 블럭은 P2P 연결이 제한시간 내에 이루어지지 않으면 소켓류 예외를 반환한다. listener.Start(); ConnectThread = new Thread(MessageHandler.Server_SocketAcceptFunction); ConnectThread.Start(listener); Thread.Sleep(CONNECTION_TIME_LIMIT); listener.Stop(); if (ConnectThread.IsAlive) { ConnectThread.Abort(); instance.Dispose(); instance = null; throw new SocketException(10051); // WSAENETUNREACH } #endregion #region 클라이언트 정보 비교 영역 // 클라이언트가 실행시킨 게임에 대한 기본 정보를 비교한다. 이 정보는 서버에서만 비교한다. // 비밀글은 Minute_Server에서 비교해야 보안성이 보장되므로 여기에서 비교하는 실수를 하지 않도록 한다. WaitHandle[] whs = new WaitHandle[userCount]; InfoCheckAssistanceObject[] assistanceObj = new InfoCheckAssistanceObject[userCount]; for (int i = 0; i < userCount; ++i) { whs[i] = new AutoResetEvent(false); assistanceObj[i] = new InfoCheckAssistanceObject(instance.targets[i], (AutoResetEvent)whs[i]); } foreach (InfoCheckAssistanceObject o in assistanceObj) { ThreadPool.QueueUserWorkItem(new WaitCallback(MessageHandler.Server_ClientInfoCheckAndInitFunction), o); } while (!WaitHandle.WaitAll(whs)) { ; // 대조작업이 모두 끝날때까지 대기 } foreach (InfoCheckAssistanceObject o in assistanceObj) { // 각각 조사하여 같지 않음을 하나라도 검출한다면 더이상 진행해서는 안되므로 거짓을 반환하고 종료하도록 한다. if (!o.maybeSame) { instance.Dispose(); instance = null; return(false); } } // 만들어진 유저 리스트를 다시 보냄으로서 현재 어떤 유저들이 같은 게임방에 있는지에 대해 넘긴다. // MEMO: 순서는 중요하지 않으며, Dictionary이므로 결국 Key값만을 중요하게 생각하면 된다. foreach (KeyValuePair <string, User> u in UserInfos.getInstance().users) { instance.SendToAll(Protocol.MinuteFrameworkProtocol.MakeUserPacket(u.Value)); } #endregion #region 사용자 기초 정보 획득 과정 #endregion } #endregion #region 클라이언트 작업 else { try { // 연결 진행 instance = new Connections(serverMode, 1); instance.targets[0] = new CommunicateTarget(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)); for (int i = 0; i < 10; ++i) { try { instance.targets[0].socket.Connect(arg2, SERVER_PORT_NUMBER); break; } catch { Console.WriteLine("Client: 연결 재시도중..."); Thread.Sleep(WAIT_TIME / 10); } } if (!instance.targets[0].socket.Connected) { throw new Exception(); } // 자신의 게임 정보를 보내는 과정 byte[] packet = Protocol.MinuteFrameworkProtocol.MakeGameInfoPacket(SettingManager.gameInfo); int sendedLength = 0; while (sendedLength < packet.Length) { sendedLength += instance.targets[0].socket.Send(packet, sendedLength, packet.Length - sendedLength, 0); } // 이번게임에 사용한 계정의 기본 정보(닉네임과 팀 번호)를 전송하는 과정 packet = Protocol.MinuteFrameworkProtocol.MakeUserPacket(myInfo); sendedLength = 0; while (sendedLength < packet.Length) { sendedLength += instance.targets[0].socket.Send(packet, sendedLength, packet.Length - sendedLength, 0); } } catch (Exception e) { Console.WriteLine("MakeConnections: " + e.Message); return(false); } GC.Collect(); } #endregion foreach (CommunicateTarget ct in instance.targets) { ct.socket.BeginReceive(ct.msgHeaderHolder, 0, ct.msgReadedLength, 0, MessageHandler.ReceiveHandler, ct); } return(true); }