Ejemplo n.º 1
0
        /// <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);
        }