private static void RoomListRefresh(CustomProtocolManager.Header header, byte[] msg) { ClientInfo ci = ClientInfo.getInstance(); List <RoomCreateData> roomDatas = CustomProtocolManager.ParseRoomListPacket(msg, 0, msg.Length); ci.mainClientFrame.RoomListRefresh(roomDatas); }
private static void SendRoomList(CustomProtocolManager.Header header, byte[] msg, User target) { if (target.ChannelNumber == -1) { return; } List <RoomCreateData> list = new List <RoomCreateData>(); Channel c = ServerProperty.getInstance().channelsList[target.ChannelNumber]; RoomCreateData rcd; foreach (KeyValuePair <string, Room> data in c.rooms) { rcd = new RoomCreateData() { gameInfo = data.Value.gameInfo, gameParameters = data.Value.gameParameters, roomName = data.Value.roomName, HostNickName = data.Value.HostNickName, teamCount = data.Value.teamCount, maxCapacity = data.Value.maxCapacity, password = data.Value.password }; list.Add(rcd); } SendToUser(target, CustomProtocolManager.MakeRoomListPacket(list)); }
private static void ServerMessageHandleFunction(byte[] msg) { Thread msgThread = new Thread(() => new MessageForm(CustomProtocolManager.ParseServerMessagePacket(msg, 0, msg.Length)).ShowDialog()); msgThread.IsBackground = false; msgThread.Start(); }
private void RoomListRefresh() { if (!inChannel) { return; } MessageHandler.SendToServer(CustomProtocolManager.MakeRoomListRequestPacket()); }
public void RequestJoinRoom() { PackedData.RoomCreateData roomData; try { roomData = (PackedData.RoomCreateData)list_GameListView.Items[list_GameListView.SelectedIndex]; MessageHandler.SendToServer(CustomProtocolManager.MakeRoomEnterPacket(roomData.HostNickName)); } catch (Exception e) { } }
private static void UserListRefresh(CustomProtocolManager.Header header, byte[] msg) { ClientInfo ci = ClientInfo.getInstance(); List <string> list; bool isRoom; isRoom = CustomProtocolManager.ParseUserList(msg, 0, (int)header.size, out list); ci.mainClientFrame.UserListRefresh(isRoom, list); }
private void RequestCreateRoom() { RoomCreateForm rcf = new RoomCreateForm(); if (rcf.ShowDialog() == DialogResult.OK) { MessageHandler.SendToServer(CustomProtocolManager.MakeRoomCreatePacket(rcf.data)); } rcf.Dispose(); }
// 채널에 입장하겠다고 서버에 알리고 결과를 받아 처리한다. private void EnterChannel() { ChannelListItem info = (ChannelListItem)list_ChannelList.SelectedItem; if (info == null) { return; } MessageHandler.SendToServer(CustomProtocolManager.MakeChannelEnterRequestPacket(info.index)); }
private static void RoomChangeHandle(CustomProtocolManager.Header header, byte[] msg) { RoomCreateData roomData = CustomProtocolManager.ParseRoomResponsePacket(msg, 0, msg.Length); if (roomData == null) // 빠져나오는 반응이거나 실패의 의미 { ClientInfo.getInstance().mainClientFrame.ExitingRoomUIChanger(); return; } ClientInfo.getInstance().mainClientFrame.EnteringRoom(roomData); }
private static void RoomStateChangeHandle(CustomProtocolManager.Header header, byte[] msg, User target) { string arg; string[] tmp; ServerProperty sp = ServerProperty.getInstance(); Room targetRoom; User user; try { targetRoom = sp.channelsList[target.ChannelNumber].rooms[target.RoomID]; } catch { return; } switch (CustomProtocolManager.ParseRoomStateChangePacket(msg, 0, msg.Length, out arg)) { case CustomProtocolManager.RoomStateChangeOption.Start: try { if (!target.RoomID.Equals(target.Nickname)) { return; // 방장이 아니면 거부 } else if (targetRoom.members.Count < 2) { return; // 최소 실행 인원이 안되면 거부 } foreach (KeyValuePair <string, RoomMember> r in targetRoom.members) { user = sp.channelsList[target.ChannelNumber].users[r.Value.Nickname]; // 방장의 경우 if (r.Key.Equals(target.Nickname)) { arg = String.Format("NormalStart {0} {1} {2} {3}", "SERVER", targetRoom.members.Count - 1, r.Value.Nickname, r.Value.TeamNumber); } else // 그 외의 경우 { tmp = ("" + target.client.Client.RemoteEndPoint).Split(':'); arg = String.Format("NormalStart {0} {1} {2} {3}", "CLIENT", tmp[0], r.Value.Nickname, r.Value.TeamNumber); } SendToUser(user, CustomProtocolManager.MakeRoomStateChangePacket(CustomProtocolManager.RoomStateChangeOption.Start, arg)); } } catch { } break; default: break; } }
private static void ChannelInfoMsgHandleFunction(CustomProtocolManager.Header header, byte[] msg) { ClientInfo ci = ClientInfo.getInstance(); ChannelInfo info; if (ci.channelSelect == null) { return; // 채널 선택창이 없으면 메세지의 의미가 없으므로 중단 } int index = CustomProtocolManager.ParseChannelInfoPacket(msg, 0, (int)header.size, out info); ci.channelSelect.AddChannelList(info, index); }
// 채널 정보를 보냅니다. private static void SendChannelInfo(byte[] msg, ref User target) { ServerProperty ci = ServerProperty.getInstance(); byte[] packet; Channel c; for (int i = 0; i < ci.channelsList.Count; ++i) { c = ci.channelsList[i]; packet = CustomProtocolManager.MakeChannelInfoPacket(c.MakeInfo(), i); SendToUser(target, packet); } }
// 서버명 요청 및 클라이언트 프로그램 인증 패킷 처리. private static void MakeConnectionMsgHandleFunction(CustomProtocolManager.Header header, byte[] msg, ref User target) {// 서버로 들어오는 MakeConnection 계 메세지는 000밖에 없다. ServerProperty sp = ServerProperty.getInstance(); // 0번 옵션이 아니거나 올바른 서버명 요청패킷이 아니라면 메세지는 처분한다. if (header.optNumber != 0 || !CustomProtocolManager.ParseConnectRequestPacket(msg, 0)) { return; } target.Id = CustomProtocolManager.AUTHORIZED_CLIENT_TEMP_NAME; byte[] packet = CustomProtocolManager.MakeClientReqInfoPacket(sp.serverName, sp.RSAparams, sp.gameInfoList); SendToUser(target, packet); }
// 회원가입 처리 private static void AccountMakingHandler(byte[] msg, User client) { JoinData data = CustomProtocolManager.ParseJoinPacket(msg, 0, msg.Length); CustomProtocolManager.GenericResponseType response; if (ConDatabase.AccountMakeFunction(CustomProtocolManager.ParseJoinPacket(msg, 0, msg.Length))) { response = CustomProtocolManager.GenericResponseType.Acknowledge; } else { response = CustomProtocolManager.GenericResponseType.Denied; } SendToUser(client, CustomProtocolManager.MakeAuthTypeResponsePacket(response)); }
public void Dispose() { ServerProperty sp = ServerProperty.getInstance(); if (client != null) { client.Close(); } if (ChannelNumber != -1) { User temp; PackedData.RoomMember temp2; sp.channelsList[ChannelNumber].users.TryRemove(Nickname, out temp); try { if (RoomID == Nickname) { foreach (KeyValuePair <string, PackedData.RoomMember> u in sp.channelsList[ChannelNumber].rooms[RoomID].members) { MessageHandler.SendToUser( sp.channelsList[ChannelNumber].users[u.Value.Nickname], CustomProtocolManager.MakeRoomResponsePacket() ); } Room dummy; sp.channelsList[ChannelNumber].rooms.TryRemove(RoomID, out dummy); } else if (RoomID != null) { sp.channelsList[ChannelNumber].rooms[RoomID].members.TryRemove(Nickname, out temp2); } } catch (Exception e) { Console.WriteLine("user.Dispose: " + e.Message); } } Id = null; Nickname = null; }
/* * opt 0 - 내용; 채널에 채팅 * opt 1 - 내용; 게임방에 채팅 * TODO: opt 2 - 팀번호 + 내용; 팀 채팅 * TODO: opt 3 - 길이 + 유저명 + 길이 + 내용; 대상 유저에게 귓속말 */ private static void ChattingHandleFunction(CustomProtocolManager.Header header, byte[] msg, ref User target) { ServerProperty sp = ServerProperty.getInstance(); byte[] packet; string chat = target.Nickname + ": " + CustomProtocolManager.ParseGeneralChatting(msg, 0, msg.Length); switch ((CustomProtocolManager.ChattingOption)header.optNumber) { case CustomProtocolManager.ChattingOption.Channel: packet = CustomProtocolManager.MakeGeneralChatting(chat, CustomProtocolManager.NormalChatType.Channel); foreach (KeyValuePair <string, User> pair in sp.channelsList[target.ChannelNumber].users) { SendToUser(pair.Value, packet); } break; case CustomProtocolManager.ChattingOption.Room: if (target.RoomID == null) { return; } packet = CustomProtocolManager.MakeGeneralChatting(chat, CustomProtocolManager.NormalChatType.Room); foreach (KeyValuePair <string, RoomMember> pair in sp.channelsList[target.ChannelNumber].rooms[target.RoomID].members) { SendToUser(sp.channelsList[target.ChannelNumber].users[pair.Value.Nickname], packet); } break; /* 테스트버전 미사용 * case CustomProtocolManager.ChattingOption.Team: * * break; * * case CustomProtocolManager.ChattingOption.Whisper: * break; */ default: break; } }
private static void RoomCreateHandle(CustomProtocolManager.Header header, byte[] msg, User target) { if (target.RoomID != null) { return; } target.RoomID = target.Nickname; RoomCreateData data = CustomProtocolManager.ParseRoomCreatePacket(msg, 0, msg.Length); ServerProperty sp = ServerProperty.getInstance(); data.HostNickName = target.Nickname; Room r = new Room(data); r.members.TryAdd(target.Nickname, new RoomMember(target.Nickname)); sp.channelsList[target.ChannelNumber].rooms.TryAdd(target.Nickname, r); SendToUser(target, CustomProtocolManager.MakeRoomResponsePacket(true, data)); }
private static void RoomStateChangeHandle(CustomProtocolManager.Header header, byte[] msg) { string arg; ClientInfo ci = ClientInfo.getInstance(); CustomProtocolManager.ParseRoomStateChangePacket(msg, 0, msg.Length, out arg); foreach (GameRegistry gr in ci.gameRegistries) { if (GameInfo.IsSameInfo(gr.gameInfo, ci.roomInfo.gameInfo)) { try { ci.gameProcess = Process.Start(gr.folderPath + @"\Game.exe ", arg); MessageBox.Show(arg); } catch { throw new Exception("게임을 찾을수 없었습니다. 실행파일을 Game.exe로 이름을 변경해주십시오."); } } } }
private static void SendUserInfo(CustomProtocolManager.Header header, byte[] msg, User target) { List <string> userList = new List <string>(); if (target.ChannelNumber != -1) { Channel channel = ServerProperty.getInstance().channelsList[target.ChannelNumber]; foreach (string nickname in channel.users.Keys) { userList.Add(nickname); } SendToUser(target, CustomProtocolManager.MakeUserList(userList, false)); } if (target.RoomID != null) { Room room; userList.Clear(); try { room = ServerProperty.getInstance().channelsList[target.ChannelNumber].rooms[target.RoomID]; } catch { target.RoomID = null; return; } foreach (KeyValuePair <string, RoomMember> pair in room.members) { if (pair.Value.TeamNumber != 0) { userList.Add(pair.Key + "(" + pair.Value.TeamNumber + ")"); } else { userList.Add(pair.Key); } } SendToUser(target, CustomProtocolManager.MakeUserList(userList, true)); } }
// 회원가입정보 중복여부 확인 처리 private static void AccountDuplicateCheckHandler(byte[] msg, User client) { CustomProtocolManager.GenericResponseType response; string context; CustomProtocolManager.DuplicateCheckJoinType joinType; joinType = CustomProtocolManager.ParseDuplicateCheckForJoin(msg, 0, (uint)msg.Length, out context); if (ConDatabase.AccountDuplicateCheckFunction(joinType, context)) { response = CustomProtocolManager.GenericResponseType.Acknowledge; } else { response = CustomProtocolManager.GenericResponseType.Denied; } byte[] packet = CustomProtocolManager.MakeAuthTypeResponsePacket(response); SendToUser(client, packet); }
/// <summary> /// 로그인을 시도하고 결과를 반환합니다. /// </summary> /// <param name="token">로그인에 사용할 토큰을 의미합니다.</param> /// <returns>로그인 결과를 의미합니다.</returns> public static bool TryLogin(CustomProtocolManager.LoginToken token) { ConnectInfo ci = ConnectInfo.getInstance(); SendToServer(CustomProtocolManager.MakeLoginPacket(token, ci.securityParameter), true); int index = 0; int requiredLength = CustomProtocolManager.HEADER_SIZE + 1; // 헤더 수신 byte[] recvData = new byte[requiredLength]; if (!ReceiveFunction(ci.tcpClient.Client, ref recvData, ref index, requiredLength)) { return(false); } if (CustomProtocolManager.ParseResponsePacket(recvData, CustomProtocolManager.HEADER_SIZE) == CustomProtocolManager.GenericResponseType.Acknowledge) { return(true); } return(false); }
/// <summary> /// 계정을 만들어달라고 요청합니다. 성공 여부가 반환됩니다. /// </summary> /// <param name="data">계정 양식입니다.</param> /// <returns>성공 여부</returns> public static bool MakeAccount(JoinData data) { ConnectInfo ci = ConnectInfo.getInstance(); SendToServer(CustomProtocolManager.MakeJoinPacket(data), true); int index = 0; int requiredLength = CustomProtocolManager.HEADER_SIZE + 1; byte[] recvData = new byte[requiredLength]; if (!ReceiveFunction(ci.tcpClient.Client, ref recvData, ref index, requiredLength)) { return(false); } if (CustomProtocolManager.ParseResponsePacket(recvData, CustomProtocolManager.HEADER_SIZE) == CustomProtocolManager.GenericResponseType.Acknowledge) { return(true); } return(false); }
/// <summary> /// 회원가입시 데이터가 중복되었는지 개별적으로 확인할때 사용하는 함수입니다. /// 형식과 데이터를 받아서 중복 여부를 확인합니다. True일 경우 사용해도 좋다는 의미입니다. /// </summary> /// <param name="type">확인할 데이터의 타입입니다.</param> /// <param name="context">확인할 데이터의 내용입니다.</param> /// <returns>사용 가능 여부</returns> public static bool CheckDuplicateData(CustomProtocolManager.DuplicateCheckJoinType type, string context) { ConnectInfo ci = ConnectInfo.getInstance(); SendToServer(CustomProtocolManager.MakeDuplicateCheckForJoin(type, context), true); int index = 0; int requiredLength = CustomProtocolManager.HEADER_SIZE + 1; byte[] recvData = new byte[requiredLength]; if (!ReceiveFunction(ci.tcpClient.Client, ref recvData, ref index, requiredLength)) { return(false); } if (CustomProtocolManager.ParseResponsePacket(recvData, CustomProtocolManager.HEADER_SIZE) == CustomProtocolManager.GenericResponseType.Acknowledge) { return(true); } return(false); }
// 로그인 인증 진행 함수 private static void LoginHandleFunction(byte[] msg, User target) { CustomProtocolManager.LoginToken loginInfo; string nickname; // 패킷 해석 loginInfo = CustomProtocolManager.ParseLoginPacket(msg, 0, ServerProperty.getInstance().RSAparams); // 로그인 절차. 실패시 실패했다는 내용의 응답을 보내고 함수를 종료한다 bool flag = ConDatabase.LoginFunction(loginInfo.id, loginInfo.password, out nickname); if (!flag || ServerProperty.getInstance().users.Exists((User u) => { return(u.Id == loginInfo.id); })) { SendToUser(target, CustomProtocolManager.MakeAuthTypeResponsePacket(CustomProtocolManager.GenericResponseType.Denied)); return; } target.Id = loginInfo.id; target.Nickname = nickname; // TODO: 성공시에는 User의 정보값을 갱신하고 성공했다는 응답을 보낸후 환영메세지를 보낸다. SendToUser(target, CustomProtocolManager.MakeAuthTypeResponsePacket(CustomProtocolManager.GenericResponseType.Acknowledge)); SendToUser(target, CustomProtocolManager.MakeServerMessagePacket(ServerProperty.getInstance().welcomeMessage)); }
private static void RoomEnterHandle(CustomProtocolManager.Header header, byte[] msg, User target) { ServerProperty sp = ServerProperty.getInstance(); string targetRoomHost = CustomProtocolManager.ParseRoomEnterPacket(msg, 0, msg.Length); try { if (targetRoomHost.Equals("") || targetRoomHost == null) { if (target.RoomID.Equals(target.Nickname)) { string str = target.RoomID; foreach (KeyValuePair <string, RoomMember> u in sp.channelsList[target.ChannelNumber].rooms[str].members) { sp.channelsList[target.ChannelNumber].users[u.Key].RoomID = null; SendToUser(sp.channelsList[target.ChannelNumber].users[u.Key], CustomProtocolManager.MakeRoomResponsePacket()); } Room rDummy; sp.channelsList[target.ChannelNumber].rooms.TryRemove(str, out rDummy); return; } RoomMember dummy; sp.channelsList[target.ChannelNumber].rooms[target.RoomID].members.TryRemove(target.Nickname, out dummy); target.RoomID = null; SendToUser(target, CustomProtocolManager.MakeRoomResponsePacket()); return; } } catch { } if (target.RoomID != null) { return; } Room targetRoom; try { targetRoom = sp.channelsList[target.ChannelNumber].rooms[targetRoomHost]; } catch { return; } if (targetRoom.members.Count < targetRoom.maxCapacity) { targetRoom.members.TryAdd(target.Nickname, new RoomMember(target.Nickname)); target.RoomID = targetRoom.HostNickName; SendToUser(target, CustomProtocolManager.MakeRoomResponsePacket(true, new RoomCreateData() { gameInfo = targetRoom.gameInfo, gameParameters = targetRoom.gameParameters, roomName = targetRoom.roomName, HostNickName = targetRoom.HostNickName, teamCount = targetRoom.teamCount, maxCapacity = targetRoom.maxCapacity, password = targetRoom.password }) ); } }
/// <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); }
/* * 1. 현재 BeginReceive는 새로운 스레드를 만들어서 콜백을 처리하고있다. * */ /* * 현재 수신 알고리즘 요약: * 1. 헤더를 먼저 해석하기 위해 헤더 크기만큼 무조건 받는다. => 비동기로 이 크기만큼 받도록 하나, 그것은 최대값이다. * 2. 헤더 크기에서 데이터 크기를 뽑아 Task 클래스를 이용한 새 스레드에서 데이터 크기만큼 읽게 한다. * 3. 온전한 바이트로 만들고 헤더에서 얻은 형식 정보를 이용해 해당 문장을 해석한다. * 4. 원본 스레드에서 다시 비동기 수신을 진행하라고 명령한다. * */ #region 수신계 /// <summary> /// 메세지 수신이 일어나면 사용하는 콜백 함수입니다. /// </summary> /// <param name="ar">송신 정보를 담아 포장하는 인터페이스입니다.</param> public static void MessageReceiveCallback(IAsyncResult ar) { User user = (User)ar.AsyncState; CustomProtocolManager.Header header; byte[] receivedData; try { user.msgReadedLength += user.client.Client.EndReceive(ar); if (user.client.Connected == false) { return; // 접속상태가 아니면 메세지를 그냥 버림 } if (user.msgReadedLength < CustomProtocolManager.HEADER_SIZE) // 메세지가 헤더의 원본 크기만큼 안왔다면 { user.client.Client.BeginReceive(user.msgHeaderHolder, user.msgReadedLength, CustomProtocolManager.HEADER_SIZE - user.msgReadedLength, 0, MessageReceiveCallback, user); return; } // 헤더의 원본 크기만큼 읽었거나 그 이상 읽은경우는 이 구역을 실행한다. user.msgReadedLength = 0; // 읽어온 메세지 길이량을 다시 초기화하여 사용할 수 있게 한다 header = CustomProtocolManager.ParseHeader(ref user.msgHeaderHolder); int received = 0; receivedData = new byte[header.size]; while (received < header.size) // 이 문장은 동기적으로 데이터만큼 메세지를 읽어오도록 한다. 허나 데이터 길이가 0일수 있으므로 do while은 쓸 수 없다 { received += user.client.Client.Receive(receivedData, received, (int)header.size - received, 0); } } catch { return; } // 동작에 오류가 있었다면 통신에 문제가 있는 것이므로 해당 소켓으로부터 수신을 그만둔다. switch (header.typeNumber) { case CustomProtocolManager.TypeNumber.MakeConnection: // 인증계 메세지는 인증해야할 클라이언트가 아니면 연결을 끊어버린다. if (user.Id != null) { user.client.Client.Disconnect(false); } else { MakeConnectionMsgHandleFunction(header, receivedData, ref user); } break; case CustomProtocolManager.TypeNumber.Authorization: // 인증계 메세지는 인증된 클라이언트가 아니면 연결을 끊어버린다. if (user.Id != CustomProtocolManager.AUTHORIZED_CLIENT_TEMP_NAME) { user.client.Client.Disconnect(false); } else { AuthorizeMsgHandleFunction(header, receivedData, ref user); } break; case CustomProtocolManager.TypeNumber.Chatting: if (user.Id == null) { user.client.Client.Disconnect(false); } else { ChattingHandleFunction(header, receivedData, ref user); } break; case CustomProtocolManager.TypeNumber.InfoReq: if (user.Id == null) { user.client.Client.Disconnect(false); } else { InfoTypeMsgHandleFunction(header, receivedData, ref user); } break; case CustomProtocolManager.TypeNumber.InfoChange: if (user.Id == null) { user.client.Client.Disconnect(false); } else { InfoChangeMsgHandleFunction(header, receivedData, ref user); } break; default: break; // 어느쪽에도 속하지 않으면 처리하지 않음 } // MEMO: 성능의 문제로 한 소켓당 수신처리는 한번씩만 수행하도록 빼놓았다. try { user.client.Client.BeginReceive(user.msgHeaderHolder, 0, CustomProtocolManager.HEADER_SIZE, 0, MessageReceiveCallback, user); } catch (Exception e) { Console.WriteLine("ReceiveCallback: " + e.Message); } }
// 채널 변경 요청에 대한 처리 private static void ChannelChangeRequestHandler(CustomProtocolManager.Header header, byte[] msg, User target) { ServerProperty sp = ServerProperty.getInstance(); int index = CustomProtocolManager.ParseChannelEnterRequestPacket(msg, 0); byte[] packet; if (index < sp.channelsList.Count) { if (sp.channelsList[index].CanEnter) { if (sp.channelsList[index].users.TryAdd(target.Nickname, target)) { User user; if (target.RoomID != null) { if (target.RoomID.Equals(target.Nickname)) { foreach (KeyValuePair <string, RoomMember> u in sp.channelsList[target.ChannelNumber].rooms[target.RoomID].members) { SendToUser( sp.channelsList[target.ChannelNumber].users[u.Value.Nickname], CustomProtocolManager.MakeRoomResponsePacket() ); } Room dummy; sp.channelsList[target.ChannelNumber].rooms.TryRemove(target.RoomID, out dummy); } else { RoomMember dummy; sp.channelsList[target.ChannelNumber].rooms[target.RoomID].members.TryRemove(target.Nickname, out dummy); } } if (target.ChannelNumber != -1) { sp.channelsList[target.ChannelNumber].users.TryRemove(target.Nickname, out user); } target.RoomID = null; target.ChannelNumber = index; packet = CustomProtocolManager.MakeChannelEnterResultPacket(CustomProtocolManager.GenericResponseType.Acknowledge); } else { packet = CustomProtocolManager.MakeChannelEnterResultPacket(CustomProtocolManager.GenericResponseType.Denied); } } else { packet = CustomProtocolManager.MakeChannelEnterResultPacket(CustomProtocolManager.GenericResponseType.Denied); } } else { packet = CustomProtocolManager.MakeChannelEnterResultPacket(CustomProtocolManager.GenericResponseType.Denied); } SendToUser(target, packet); }
/// <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; } }
// 채팅 메세지 처리 private static void ChattingHandleFunction(CustomProtocolManager.Header header, byte[] msg) { string parsedMsg = CustomProtocolManager.ParseGeneralChatting(msg, 0, (int)header.size); ClientInfo.getInstance().mainClientFrame.ChatWriter(parsedMsg, (int)header.optNumber); }