diff --git a/Tutorials/EchoClient/App.config b/Tutorials/EchoClient/App.config new file mode 100644 index 0000000..d740e88 --- /dev/null +++ b/Tutorials/EchoClient/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Tutorials/EchoClient/ClientSimpleTcp.cs b/Tutorials/EchoClient/ClientSimpleTcp.cs new file mode 100644 index 0000000..62fa8fe --- /dev/null +++ b/Tutorials/EchoClient/ClientSimpleTcp.cs @@ -0,0 +1,93 @@ +using System; +using System.Net.Sockets; +using System.Net; + +namespace csharp_test_client +{ + public class ClientSimpleTcp + { + public Socket Sock = null; + public string LatestErrorMsg; + + + //소켓연결 + public bool Connect(string ip, int port) + { + try + { + IPAddress serverIP = IPAddress.Parse(ip); + int serverPort = port; + + Sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + Sock.Connect(new IPEndPoint(serverIP, serverPort)); + + if (Sock == null || Sock.Connected == false) + { + return false; + } + + return true; + } + catch (Exception ex) + { + LatestErrorMsg = ex.Message; + return false; + } + } + + public Tuple Receive() + { + + try + { + byte[] ReadBuffer = new byte[2048]; + var nRecv = Sock.Receive(ReadBuffer, 0, ReadBuffer.Length, SocketFlags.None); + + if (nRecv == 0) + { + return null; + } + + return Tuple.Create(nRecv,ReadBuffer); + } + catch (SocketException se) + { + LatestErrorMsg = se.Message; + } + + return null; + } + + //스트림에 쓰기 + public void Send(byte[] sendData) + { + try + { + if (Sock != null && Sock.Connected) //연결상태 유무 확인 + { + Sock.Send(sendData, 0, sendData.Length, SocketFlags.None); + } + else + { + LatestErrorMsg = "먼저 채팅서버에 접속하세요!"; + } + } + catch (SocketException se) + { + LatestErrorMsg = se.Message; + } + } + + //소켓과 스트림 닫기 + public void Close() + { + if (Sock != null && Sock.Connected) + { + //Sock.Shutdown(SocketShutdown.Both); + Sock.Close(); + } + } + + public bool IsConnected() { return (Sock != null && Sock.Connected) ? true : false; } + } +} diff --git a/Tutorials/EchoClient/DevLog.cs b/Tutorials/EchoClient/DevLog.cs new file mode 100644 index 0000000..4d45fbe --- /dev/null +++ b/Tutorials/EchoClient/DevLog.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using System.Runtime.CompilerServices; +using System.Threading; + +namespace csharp_test_client +{ + public class DevLog + { + static System.Collections.Concurrent.ConcurrentQueue logMsgQueue = new System.Collections.Concurrent.ConcurrentQueue(); + + static Int64 출력가능_로그레벨 = (Int64)LOG_LEVEL.TRACE; + + + + static public void Init(LOG_LEVEL logLevel) + { + ChangeLogLevel(logLevel); + } + + static public void ChangeLogLevel(LOG_LEVEL logLevel) + { + Interlocked.Exchange(ref 출력가능_로그레벨, (int)logLevel); + } + + public static LOG_LEVEL CurrentLogLevel() + { + var curLogLevel = (LOG_LEVEL)Interlocked.Read(ref 출력가능_로그레벨); + return curLogLevel; + } + + static public void Write(string msg, LOG_LEVEL logLevel = LOG_LEVEL.TRACE, + [CallerFilePath] string fileName = "", + [CallerMemberName] string methodName = "", + [CallerLineNumber] int lineNumber = 0) + { + if (CurrentLogLevel() <= logLevel) + { + logMsgQueue.Enqueue(string.Format("{0}:{1}| {2}", DateTime.Now, methodName, msg)); + } + } + + static public bool GetLog(out string msg) + { + if (logMsgQueue.TryDequeue(out msg)) + { + return true; + } + + return false; + } + + } + + + public enum LOG_LEVEL + { + TRACE, + DEBUG, + INFO, + WARN, + ERROR, + DISABLE + } +} diff --git a/Tutorials/EchoClient/Packet.cs b/Tutorials/EchoClient/Packet.cs new file mode 100644 index 0000000..72f42dc --- /dev/null +++ b/Tutorials/EchoClient/Packet.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace csharp_test_client +{ + struct PacketData + { + public Int16 DataSize; + public Int16 PacketID; + public SByte Type; + public byte[] BodyData; + } + + public class PacketDump + { + public static string Bytes(byte[] byteArr) + { + StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < byteArr.Length; ++i) + { + sb.Append(byteArr[i] + " "); + } + sb.Append("]"); + return sb.ToString(); + } + } + + + public class ErrorNtfPacket + { + public ERROR_CODE Error; + + public bool FromBytes(byte[] bodyData) + { + Error = (ERROR_CODE)BitConverter.ToInt16(bodyData, 0); + return true; + } + } + + + public class LoginReqPacket + { + byte[] UserID = new byte[PacketDef.MAX_USER_ID_BYTE_LENGTH]; + byte[] UserPW = new byte[PacketDef.MAX_USER_PW_BYTE_LENGTH]; + + public void SetValue(string userID, string userPW) + { + Encoding.UTF8.GetBytes(userID).CopyTo(UserID, 0); + Encoding.UTF8.GetBytes(userPW).CopyTo(UserPW, 0); + } + + public byte[] ToBytes() + { + List dataSource = new List(); + dataSource.AddRange(UserID); + dataSource.AddRange(UserPW); + return dataSource.ToArray(); + } + } + + public class LoginResPacket + { + public Int16 Result; + + public bool FromBytes(byte[] bodyData) + { + Result = BitConverter.ToInt16(bodyData, 0); + return true; + } + } + + + public class RoomEnterReqPacket + { + int RoomNumber; + public void SetValue(int roomNumber) + { + RoomNumber = roomNumber; + } + + public byte[] ToBytes() + { + List dataSource = new List(); + dataSource.AddRange(BitConverter.GetBytes(RoomNumber)); + return dataSource.ToArray(); + } + } + + public class RoomEnterResPacket + { + public Int16 Result; + public Int64 RoomUserUniqueId; + + public bool FromBytes(byte[] bodyData) + { + Result = BitConverter.ToInt16(bodyData, 0); + RoomUserUniqueId = BitConverter.ToInt64(bodyData, 2); + return true; + } + } + + public class RoomUserListNtfPacket + { + public int UserCount = 0; + public List UserUniqueIdList = new List(); + public List UserIDList = new List(); + + public bool FromBytes(byte[] bodyData) + { + var readPos = 0; + var userCount = (SByte)bodyData[readPos]; + ++readPos; + + for (int i = 0; i < userCount; ++i) + { + var uniqeudId = BitConverter.ToInt64(bodyData, readPos); + readPos += 8; + + var idlen = (SByte)bodyData[readPos]; + ++readPos; + + var id = Encoding.UTF8.GetString(bodyData, readPos, idlen); + readPos += idlen; + + UserUniqueIdList.Add(uniqeudId); + UserIDList.Add(id); + } + + UserCount = userCount; + return true; + } + } + + public class RoomNewUserNtfPacket + { + public Int64 UserUniqueId; + public string UserID; + + public bool FromBytes(byte[] bodyData) + { + var readPos = 0; + + UserUniqueId = BitConverter.ToInt64(bodyData, readPos); + readPos += 8; + + var idlen = (SByte)bodyData[readPos]; + ++readPos; + + UserID = Encoding.UTF8.GetString(bodyData, readPos, idlen); + readPos += idlen; + + return true; + } + } + + + public class RoomChatReqPacket + { + Int16 MsgLen; + byte[] Msg;//= new byte[PacketDef.MAX_USER_ID_BYTE_LENGTH]; + + public void SetValue(string message) + { + Msg = Encoding.UTF8.GetBytes(message); + MsgLen = (Int16)Msg.Length; + } + + public byte[] ToBytes() + { + List dataSource = new List(); + dataSource.AddRange(BitConverter.GetBytes(MsgLen)); + dataSource.AddRange(Msg); + return dataSource.ToArray(); + } + } + + public class RoomChatResPacket + { + public Int16 Result; + + public bool FromBytes(byte[] bodyData) + { + Result = BitConverter.ToInt16(bodyData, 0); + return true; + } + } + + public class RoomChatNtfPacket + { + public Int64 UserUniqueId; + public string Message; + + public bool FromBytes(byte[] bodyData) + { + UserUniqueId = BitConverter.ToInt64(bodyData, 0); + + var msgLen = BitConverter.ToInt16(bodyData, 8); + byte[] messageTemp = new byte[msgLen]; + Buffer.BlockCopy(bodyData, 8 + 2, messageTemp, 0, msgLen); + Message = Encoding.UTF8.GetString(messageTemp); + return true; + } + } + + + public class RoomLeaveResPacket + { + public Int16 Result; + + public bool FromBytes(byte[] bodyData) + { + Result = BitConverter.ToInt16(bodyData, 0); + return true; + } + } + + public class RoomLeaveUserNtfPacket + { + public Int64 UserUniqueId; + + public bool FromBytes(byte[] bodyData) + { + UserUniqueId = BitConverter.ToInt64(bodyData, 0); + return true; + } + } + + + + public class RoomRelayNtfPacket + { + public Int64 UserUniqueId; + public byte[] RelayData; + + public bool FromBytes(byte[] bodyData) + { + UserUniqueId = BitConverter.ToInt64(bodyData, 0); + + var relayDataLen = bodyData.Length - 8; + RelayData = new byte[relayDataLen]; + Buffer.BlockCopy(bodyData, 8, RelayData, 0, relayDataLen); + return true; + } + } + + + public class PingRequest + { + public Int16 PingNum; + + public byte[] ToBytes() + { + return BitConverter.GetBytes(PingNum); + } + + } +} diff --git a/Tutorials/EchoClient/PacketBufferManager.cs b/Tutorials/EchoClient/PacketBufferManager.cs new file mode 100644 index 0000000..590be3f --- /dev/null +++ b/Tutorials/EchoClient/PacketBufferManager.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace csharp_test_client +{ + class PacketBufferManager + { + int BufferSize = 0; + int ReadPos = 0; + int WritePos = 0; + + int HeaderSize = 0; + int MaxPacketSize = 0; + byte[] PacketData; + byte[] PacketDataTemp; + + public bool Init(int size, int headerSize, int maxPacketSize) + { + if (size < (maxPacketSize * 2) || size < 1 || headerSize < 1 || maxPacketSize < 1) + { + return false; + } + + BufferSize = size; + PacketData = new byte[size]; + PacketDataTemp = new byte[size]; + HeaderSize = headerSize; + MaxPacketSize = maxPacketSize; + + return true; + } + + public bool Write(byte[] data, int pos, int size) + { + if (data == null || (data.Length < (pos + size))) + { + return false; + } + + var remainBufferSize = BufferSize - WritePos; + + if (remainBufferSize < size) + { + return false; + } + + Buffer.BlockCopy(data, pos, PacketData, WritePos, size); + WritePos += size; + + if (NextFree() == false) + { + BufferRelocate(); + } + return true; + } + + public ArraySegment Read() + { + var enableReadSize = WritePos - ReadPos; + + if (enableReadSize < HeaderSize) + { + return new ArraySegment(); + } + + var packetDataSize = BitConverter.ToInt16(PacketData, ReadPos); + if (enableReadSize < packetDataSize) + { + return new ArraySegment(); + } + + var completePacketData = new ArraySegment(PacketData, ReadPos, packetDataSize); + ReadPos += packetDataSize; + return completePacketData; + } + + bool NextFree() + { + var enableWriteSize = BufferSize - WritePos; + + if (enableWriteSize < MaxPacketSize) + { + return false; + } + + return true; + } + + void BufferRelocate() + { + var enableReadSize = WritePos - ReadPos; + + Buffer.BlockCopy(PacketData, ReadPos, PacketDataTemp, 0, enableReadSize); + Buffer.BlockCopy(PacketDataTemp, 0, PacketData, 0, enableReadSize); + + ReadPos = 0; + WritePos = enableReadSize; + } + } +} diff --git a/Tutorials/EchoClient/PacketDefine.cs b/Tutorials/EchoClient/PacketDefine.cs new file mode 100644 index 0000000..396078c --- /dev/null +++ b/Tutorials/EchoClient/PacketDefine.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace csharp_test_client +{ + class PacketDef + { + public const Int16 PACKET_HEADER_SIZE = 5; + public const int MAX_USER_ID_BYTE_LENGTH = 16; + public const int MAX_USER_PW_BYTE_LENGTH = 16; + } + + public enum PACKET_ID : ushort + { + PACKET_ID_ECHO = 101, + + // Ping(Heart-beat) + PACKET_ID_PING_REQ = 201, + PACKET_ID_PING_RES = 202, + + PACKET_ID_ERROR_NTF = 203, + + + // 로그인 + PACKET_ID_LOGIN_REQ = 701, + PACKET_ID_LOGIN_RES = 702, + + + PACKET_ID_ROOM_ENTER_REQ = 721, + PACKET_ID_ROOM_ENTER_RES = 722, + PACKET_ID_ROOM_USER_LIST_NTF = 723, + PACKET_ID_ROOM_NEW_USER_NTF = 724, + + PACKET_ID_ROOM_LEAVE_REQ = 726, + PACKET_ID_ROOM_LEAVE_RES = 727, + PACKET_ID_ROOM_LEAVE_USER_NTF = 728, + + PACKET_ID_ROOM_CHAT_REQ = 731, + PACKET_ID_ROOM_CHAT_RES = 732, + PACKET_ID_ROOM_CHAT_NOTIFY = 733, + + PACKET_ID_ROOM_RELAY_REQ = 741, + PACKET_ID_ROOM_RELAY_NTF = 742, + } + + + public enum ERROR_CODE : Int16 + { + ERROR_NONE = 0, + + + + ERROR_CODE_USER_MGR_INVALID_USER_UNIQUEID = 112, + + ERROR_CODE_PUBLIC_CHANNEL_IN_USER = 114, + + ERROR_CODE_PUBLIC_CHANNEL_INVALIDE_NUMBER = 115, + } +} diff --git a/Tutorials/EchoClient/PacketProcessForm.cs b/Tutorials/EchoClient/PacketProcessForm.cs new file mode 100644 index 0000000..32bea27 --- /dev/null +++ b/Tutorials/EchoClient/PacketProcessForm.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace csharp_test_client +{ + public partial class mainForm + { + Dictionary> PacketFuncDic = new Dictionary>(); + + void SetPacketHandler() + { + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ECHO, PacketProcess_Echo); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ERROR_NTF, PacketProcess_ErrorNotify); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_LOGIN_RES, PacketProcess_LoginResponse); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ROOM_ENTER_RES, PacketProcess_RoomEnterResponse); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ROOM_USER_LIST_NTF, PacketProcess_RoomUserListNotify); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ROOM_NEW_USER_NTF, PacketProcess_RoomNewUserNotify); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ROOM_LEAVE_RES, PacketProcess_RoomLeaveResponse); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ROOM_LEAVE_USER_NTF, PacketProcess_RoomLeaveUserNotify); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ROOM_CHAT_RES, PacketProcess_RoomChatResponse); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ROOM_CHAT_NOTIFY, PacketProcess_RoomChatNotify); + PacketFuncDic.Add(PACKET_ID.PACKET_ID_ROOM_RELAY_NTF, PacketProcess_RoomRelayNotify); + } + + void PacketProcess(PacketData packet) + { + var packetType = (PACKET_ID)packet.PacketID; + //DevLog.Write("Packet Error: PacketID:{packet.PacketID.ToString()}, Error: {(ERROR_CODE)packet.Result}"); + //DevLog.Write("RawPacket: " + packet.PacketID.ToString() + ", " + PacketDump.Bytes(packet.BodyData)); + + if (PacketFuncDic.ContainsKey(packetType)) + { + PacketFuncDic[packetType](packet.BodyData); + } + else + { + DevLog.Write("Unknown Packet Id: " + packet.PacketID.ToString()); + } + } + + void PacketProcess_Echo(byte[] bodyData) + { + DevLog.Write($"Echo 받음: {bodyData.Length}"); + } + + void PacketProcess_ErrorNotify(byte[] bodyData) + { + var notifyPkt = new ErrorNtfPacket(); + notifyPkt.FromBytes(bodyData); + + DevLog.Write($"에러 통보 받음: {notifyPkt.Error}"); + } + + + void PacketProcess_LoginResponse(byte[] bodyData) + { + var responsePkt = new LoginResPacket(); + responsePkt.FromBytes(bodyData); + + DevLog.Write($"로그인 결과: {(ERROR_CODE)responsePkt.Result}"); + } + + + void PacketProcess_RoomEnterResponse(byte[] bodyData) + { + var responsePkt = new RoomEnterResPacket(); + responsePkt.FromBytes(bodyData); + + DevLog.Write($"방 입장 결과: {(ERROR_CODE)responsePkt.Result}"); + } + + void PacketProcess_RoomUserListNotify(byte[] bodyData) + { + var notifyPkt = new RoomUserListNtfPacket(); + notifyPkt.FromBytes(bodyData); + + for (int i = 0; i < notifyPkt.UserCount; ++i) + { + AddRoomUserList(notifyPkt.UserUniqueIdList[i], notifyPkt.UserIDList[i]); + } + + DevLog.Write($"방의 기존 유저 리스트 받음"); + } + + void PacketProcess_RoomNewUserNotify(byte[] bodyData) + { + var notifyPkt = new RoomNewUserNtfPacket(); + notifyPkt.FromBytes(bodyData); + + AddRoomUserList(notifyPkt.UserUniqueId, notifyPkt.UserID); + + DevLog.Write($"방에 새로 들어온 유저 받음"); + } + + + void PacketProcess_RoomLeaveResponse(byte[] bodyData) + { + var responsePkt = new RoomLeaveResPacket(); + responsePkt.FromBytes(bodyData); + + DevLog.Write($"방 나가기 결과: {(ERROR_CODE)responsePkt.Result}"); + } + + void PacketProcess_RoomLeaveUserNotify(byte[] bodyData) + { + var notifyPkt = new RoomLeaveUserNtfPacket(); + notifyPkt.FromBytes(bodyData); + + RemoveRoomUserList(notifyPkt.UserUniqueId); + + DevLog.Write($"방에서 나간 유저 받음"); + } + + + void PacketProcess_RoomChatResponse(byte[] bodyData) + { + var responsePkt = new RoomChatResPacket(); + responsePkt.FromBytes(bodyData); + + var errorCode = (ERROR_CODE)responsePkt.Result; + var msg = $"방 채팅 요청 결과: {(ERROR_CODE)responsePkt.Result}"; + if (errorCode == ERROR_CODE.ERROR_NONE) + { + DevLog.Write(msg, LOG_LEVEL.ERROR); + } + else + { + AddRoomChatMessageList(0, msg); + } + } + + + void PacketProcess_RoomChatNotify(byte[] bodyData) + { + var responsePkt = new RoomChatNtfPacket(); + responsePkt.FromBytes(bodyData); + + AddRoomChatMessageList(responsePkt.UserUniqueId, responsePkt.Message); + } + + void AddRoomChatMessageList(Int64 userUniqueId, string msgssage) + { + var msg = $"{userUniqueId}: {msgssage}"; + + if (listBoxRoomChatMsg.Items.Count > 512) + { + listBoxRoomChatMsg.Items.Clear(); + } + + listBoxRoomChatMsg.Items.Add(msg); + listBoxRoomChatMsg.SelectedIndex = listBoxRoomChatMsg.Items.Count - 1; + } + + + void PacketProcess_RoomRelayNotify(byte[] bodyData) + { + var notifyPkt = new RoomRelayNtfPacket(); + notifyPkt.FromBytes(bodyData); + + var stringData = Encoding.UTF8.GetString(notifyPkt.RelayData); + DevLog.Write($"방에서 릴레이 받음. {notifyPkt.UserUniqueId} - {stringData}"); + } + } +} diff --git a/Tutorials/EchoClient/Program.cs b/Tutorials/EchoClient/Program.cs new file mode 100644 index 0000000..7b9e1ff --- /dev/null +++ b/Tutorials/EchoClient/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Versioning; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace csharp_test_client +{ + [SupportedOSPlatform("windows10.0.177630")] + static class Program + { + /// + /// 해당 응용 프로그램의 주 진입점입니다. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new mainForm()); + } + } +} diff --git a/Tutorials/EchoClient/Properties/AssemblyInfo.cs b/Tutorials/EchoClient/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d3e425d --- /dev/null +++ b/Tutorials/EchoClient/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해 +// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면 +// 이러한 특성 값을 변경하세요. +[assembly: AssemblyTitle("csharp_test_client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("csharp_test_client")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에 +// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면 +// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요. +[assembly: ComVisible(false)] + +// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다. +[assembly: Guid("9e4b5e72-4e76-4e22-90b0-e53275a99018")] + +// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다. +// +// 주 버전 +// 부 버전 +// 빌드 번호 +// 수정 버전 +// +// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로 +// 지정되도록 할 수 있습니다. +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tutorials/EchoClient/Properties/Resources.Designer.cs b/Tutorials/EchoClient/Properties/Resources.Designer.cs new file mode 100644 index 0000000..fbcd69d --- /dev/null +++ b/Tutorials/EchoClient/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// 이 코드는 도구를 사용하여 생성되었습니다. +// 런타임 버전:4.0.30319.42000 +// +// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 +// 이러한 변경 내용이 손실됩니다. +// +//------------------------------------------------------------------------------ + +namespace csharp_test_client.Properties +{ + + + /// + /// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다. + /// + // 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder + // 클래스에서 자동으로 생성되었습니다. + // 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 + // ResGen을 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("csharp_test_client.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대해 + /// 현재 스레드의 CurrentUICulture 속성을 재정의합니다. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/Tutorials/EchoClient/Properties/Resources.resx b/Tutorials/EchoClient/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/Tutorials/EchoClient/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Tutorials/EchoClient/Properties/Settings.Designer.cs b/Tutorials/EchoClient/Properties/Settings.Designer.cs new file mode 100644 index 0000000..aca6dcd --- /dev/null +++ b/Tutorials/EchoClient/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace csharp_test_client.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Tutorials/EchoClient/Properties/Settings.settings b/Tutorials/EchoClient/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/Tutorials/EchoClient/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Tutorials/EchoClient/csharp_test_client.csproj b/Tutorials/EchoClient/csharp_test_client.csproj new file mode 100644 index 0000000..ba2a444 --- /dev/null +++ b/Tutorials/EchoClient/csharp_test_client.csproj @@ -0,0 +1,20 @@ + + + net8.0-windows10.0.17763.0 + WinExe + false + true + true + + + ..\bin\ + + + + + + all + + + + \ No newline at end of file diff --git a/Tutorials/EchoClient/csharp_test_client.sln b/Tutorials/EchoClient/csharp_test_client.sln new file mode 100644 index 0000000..8a34e3c --- /dev/null +++ b/Tutorials/EchoClient/csharp_test_client.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp_test_client", "csharp_test_client.csproj", "{9E4B5E72-4E76-4E22-90B0-E53275A99018}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tutorials/EchoClient/mainForm.Designer.cs b/Tutorials/EchoClient/mainForm.Designer.cs new file mode 100644 index 0000000..48e4419 --- /dev/null +++ b/Tutorials/EchoClient/mainForm.Designer.cs @@ -0,0 +1,454 @@ +namespace csharp_test_client +{ + partial class mainForm + { + /// + /// 필수 디자이너 변수입니다. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 사용 중인 모든 리소스를 정리합니다. + /// + /// 관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form 디자이너에서 생성한 코드 + + /// + /// 디자이너 지원에 필요한 메서드입니다. + /// 이 메서드의 내용을 코드 편집기로 수정하지 마세요. + /// + private void InitializeComponent() + { + btnDisconnect = new System.Windows.Forms.Button(); + btnConnect = new System.Windows.Forms.Button(); + groupBox5 = new System.Windows.Forms.GroupBox(); + textBoxPort = new System.Windows.Forms.TextBox(); + label10 = new System.Windows.Forms.Label(); + checkBoxLocalHostIP = new System.Windows.Forms.CheckBox(); + textBoxIP = new System.Windows.Forms.TextBox(); + label9 = new System.Windows.Forms.Label(); + button1 = new System.Windows.Forms.Button(); + textSendText = new System.Windows.Forms.TextBox(); + labelStatus = new System.Windows.Forms.Label(); + listBoxLog = new System.Windows.Forms.ListBox(); + label1 = new System.Windows.Forms.Label(); + textBoxUserID = new System.Windows.Forms.TextBox(); + textBoxUserPW = new System.Windows.Forms.TextBox(); + label2 = new System.Windows.Forms.Label(); + button2 = new System.Windows.Forms.Button(); + Room = new System.Windows.Forms.GroupBox(); + textBoxRelay = new System.Windows.Forms.TextBox(); + btnRoomRelay = new System.Windows.Forms.Button(); + btnRoomChat = new System.Windows.Forms.Button(); + textBoxRoomSendMsg = new System.Windows.Forms.TextBox(); + listBoxRoomChatMsg = new System.Windows.Forms.ListBox(); + label4 = new System.Windows.Forms.Label(); + listBoxRoomUserList = new System.Windows.Forms.ListBox(); + btn_RoomLeave = new System.Windows.Forms.Button(); + btn_RoomEnter = new System.Windows.Forms.Button(); + textBoxRoomNumber = new System.Windows.Forms.TextBox(); + label3 = new System.Windows.Forms.Label(); + groupBox5.SuspendLayout(); + Room.SuspendLayout(); + SuspendLayout(); + // + // btnDisconnect + // + btnDisconnect.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 129); + btnDisconnect.Location = new System.Drawing.Point(421, 55); + btnDisconnect.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + btnDisconnect.Name = "btnDisconnect"; + btnDisconnect.Size = new System.Drawing.Size(88, 32); + btnDisconnect.TabIndex = 29; + btnDisconnect.Text = "접속 끊기"; + btnDisconnect.UseVisualStyleBackColor = true; + btnDisconnect.Click += btnDisconnect_Click; + // + // btnConnect + // + btnConnect.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 129); + btnConnect.Location = new System.Drawing.Point(420, 20); + btnConnect.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + btnConnect.Name = "btnConnect"; + btnConnect.Size = new System.Drawing.Size(88, 32); + btnConnect.TabIndex = 28; + btnConnect.Text = "접속하기"; + btnConnect.UseVisualStyleBackColor = true; + btnConnect.Click += btnConnect_Click; + // + // groupBox5 + // + groupBox5.Controls.Add(textBoxPort); + groupBox5.Controls.Add(label10); + groupBox5.Controls.Add(checkBoxLocalHostIP); + groupBox5.Controls.Add(textBoxIP); + groupBox5.Controls.Add(label9); + groupBox5.Location = new System.Drawing.Point(12, 15); + groupBox5.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + groupBox5.Name = "groupBox5"; + groupBox5.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); + groupBox5.Size = new System.Drawing.Size(403, 65); + groupBox5.TabIndex = 27; + groupBox5.TabStop = false; + groupBox5.Text = "Socket 더미 클라이언트 설정"; + // + // textBoxPort + // + textBoxPort.Location = new System.Drawing.Point(225, 25); + textBoxPort.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + textBoxPort.MaxLength = 6; + textBoxPort.Name = "textBoxPort"; + textBoxPort.Size = new System.Drawing.Size(51, 23); + textBoxPort.TabIndex = 18; + textBoxPort.Text = "32452"; + textBoxPort.WordWrap = false; + // + // label10 + // + label10.AutoSize = true; + label10.Location = new System.Drawing.Point(163, 30); + label10.Name = "label10"; + label10.Size = new System.Drawing.Size(62, 15); + label10.TabIndex = 17; + label10.Text = "포트 번호:"; + // + // checkBoxLocalHostIP + // + checkBoxLocalHostIP.AutoSize = true; + checkBoxLocalHostIP.Checked = true; + checkBoxLocalHostIP.CheckState = System.Windows.Forms.CheckState.Checked; + checkBoxLocalHostIP.Location = new System.Drawing.Point(285, 30); + checkBoxLocalHostIP.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + checkBoxLocalHostIP.Name = "checkBoxLocalHostIP"; + checkBoxLocalHostIP.Size = new System.Drawing.Size(102, 19); + checkBoxLocalHostIP.TabIndex = 15; + checkBoxLocalHostIP.Text = "localhost 사용"; + checkBoxLocalHostIP.UseVisualStyleBackColor = true; + // + // textBoxIP + // + textBoxIP.Location = new System.Drawing.Point(68, 24); + textBoxIP.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + textBoxIP.MaxLength = 6; + textBoxIP.Name = "textBoxIP"; + textBoxIP.Size = new System.Drawing.Size(87, 23); + textBoxIP.TabIndex = 11; + textBoxIP.Text = "0.0.0.0"; + textBoxIP.WordWrap = false; + // + // label9 + // + label9.AutoSize = true; + label9.Location = new System.Drawing.Point(6, 29); + label9.Name = "label9"; + label9.Size = new System.Drawing.Size(62, 15); + label9.TabIndex = 10; + label9.Text = "서버 주소:"; + // + // button1 + // + button1.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 129); + button1.Location = new System.Drawing.Point(319, 88); + button1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + button1.Name = "button1"; + button1.Size = new System.Drawing.Size(100, 32); + button1.TabIndex = 39; + button1.Text = "echo 보내기"; + button1.UseVisualStyleBackColor = true; + button1.Click += button1_Click; + // + // textSendText + // + textSendText.Location = new System.Drawing.Point(12, 92); + textSendText.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + textSendText.MaxLength = 32; + textSendText.Name = "textSendText"; + textSendText.Size = new System.Drawing.Size(301, 23); + textSendText.TabIndex = 38; + textSendText.Text = "test1"; + textSendText.WordWrap = false; + // + // labelStatus + // + labelStatus.AutoSize = true; + labelStatus.Location = new System.Drawing.Point(10, 719); + labelStatus.Name = "labelStatus"; + labelStatus.Size = new System.Drawing.Size(112, 15); + labelStatus.TabIndex = 40; + labelStatus.Text = "서버 접속 상태: ???"; + // + // listBoxLog + // + listBoxLog.FormattingEnabled = true; + listBoxLog.HorizontalScrollbar = true; + listBoxLog.ItemHeight = 15; + listBoxLog.Location = new System.Drawing.Point(10, 532); + listBoxLog.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + listBoxLog.Name = "listBoxLog"; + listBoxLog.Size = new System.Drawing.Size(490, 169); + listBoxLog.TabIndex = 41; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(10, 153); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(45, 15); + label1.TabIndex = 42; + label1.Text = "UserID:"; + // + // textBoxUserID + // + textBoxUserID.Location = new System.Drawing.Point(62, 149); + textBoxUserID.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + textBoxUserID.MaxLength = 6; + textBoxUserID.Name = "textBoxUserID"; + textBoxUserID.Size = new System.Drawing.Size(87, 23); + textBoxUserID.TabIndex = 43; + textBoxUserID.Text = "jacking75"; + textBoxUserID.WordWrap = false; + // + // textBoxUserPW + // + textBoxUserPW.Location = new System.Drawing.Point(220, 150); + textBoxUserPW.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + textBoxUserPW.MaxLength = 6; + textBoxUserPW.Name = "textBoxUserPW"; + textBoxUserPW.Size = new System.Drawing.Size(87, 23); + textBoxUserPW.TabIndex = 45; + textBoxUserPW.Text = "jacking75"; + textBoxUserPW.WordWrap = false; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(158, 154); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(53, 15); + label2.TabIndex = 44; + label2.Text = "PassWD:"; + // + // button2 + // + button2.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 129); + button2.Location = new System.Drawing.Point(316, 144); + button2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + button2.Name = "button2"; + button2.Size = new System.Drawing.Size(100, 32); + button2.TabIndex = 46; + button2.Text = "Login"; + button2.UseVisualStyleBackColor = true; + button2.Click += button2_Click; + // + // Room + // + Room.Controls.Add(textBoxRelay); + Room.Controls.Add(btnRoomRelay); + Room.Controls.Add(btnRoomChat); + Room.Controls.Add(textBoxRoomSendMsg); + Room.Controls.Add(listBoxRoomChatMsg); + Room.Controls.Add(label4); + Room.Controls.Add(listBoxRoomUserList); + Room.Controls.Add(btn_RoomLeave); + Room.Controls.Add(btn_RoomEnter); + Room.Controls.Add(textBoxRoomNumber); + Room.Controls.Add(label3); + Room.Location = new System.Drawing.Point(13, 213); + Room.Name = "Room"; + Room.Size = new System.Drawing.Size(495, 312); + Room.TabIndex = 47; + Room.TabStop = false; + Room.Text = "Room"; + // + // textBoxRelay + // + textBoxRelay.Location = new System.Drawing.Point(306, 30); + textBoxRelay.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + textBoxRelay.MaxLength = 6; + textBoxRelay.Name = "textBoxRelay"; + textBoxRelay.Size = new System.Drawing.Size(109, 23); + textBoxRelay.TabIndex = 55; + textBoxRelay.Text = "test"; + textBoxRelay.WordWrap = false; + // + // btnRoomRelay + // + btnRoomRelay.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 129); + btnRoomRelay.Location = new System.Drawing.Point(420, 25); + btnRoomRelay.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + btnRoomRelay.Name = "btnRoomRelay"; + btnRoomRelay.Size = new System.Drawing.Size(66, 32); + btnRoomRelay.TabIndex = 54; + btnRoomRelay.Text = "Relay"; + btnRoomRelay.UseVisualStyleBackColor = true; + btnRoomRelay.Click += btnRoomRelay_Click; + // + // btnRoomChat + // + btnRoomChat.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 129); + btnRoomChat.Location = new System.Drawing.Point(437, 265); + btnRoomChat.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + btnRoomChat.Name = "btnRoomChat"; + btnRoomChat.Size = new System.Drawing.Size(50, 32); + btnRoomChat.TabIndex = 53; + btnRoomChat.Text = "chat"; + btnRoomChat.UseVisualStyleBackColor = true; + btnRoomChat.Click += btnRoomChat_Click; + // + // textBoxRoomSendMsg + // + textBoxRoomSendMsg.Location = new System.Drawing.Point(13, 269); + textBoxRoomSendMsg.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + textBoxRoomSendMsg.MaxLength = 32; + textBoxRoomSendMsg.Name = "textBoxRoomSendMsg"; + textBoxRoomSendMsg.Size = new System.Drawing.Size(419, 23); + textBoxRoomSendMsg.TabIndex = 52; + textBoxRoomSendMsg.Text = "test1"; + textBoxRoomSendMsg.WordWrap = false; + // + // listBoxRoomChatMsg + // + listBoxRoomChatMsg.FormattingEnabled = true; + listBoxRoomChatMsg.ItemHeight = 15; + listBoxRoomChatMsg.Location = new System.Drawing.Point(144, 81); + listBoxRoomChatMsg.Name = "listBoxRoomChatMsg"; + listBoxRoomChatMsg.Size = new System.Drawing.Size(343, 169); + listBoxRoomChatMsg.TabIndex = 51; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(10, 64); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(55, 15); + label4.TabIndex = 50; + label4.Text = "User List:"; + // + // listBoxRoomUserList + // + listBoxRoomUserList.FormattingEnabled = true; + listBoxRoomUserList.ItemHeight = 15; + listBoxRoomUserList.Location = new System.Drawing.Point(13, 82); + listBoxRoomUserList.Name = "listBoxRoomUserList"; + listBoxRoomUserList.Size = new System.Drawing.Size(123, 169); + listBoxRoomUserList.TabIndex = 49; + // + // btn_RoomLeave + // + btn_RoomLeave.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 129); + btn_RoomLeave.Location = new System.Drawing.Point(216, 24); + btn_RoomLeave.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + btn_RoomLeave.Name = "btn_RoomLeave"; + btn_RoomLeave.Size = new System.Drawing.Size(66, 32); + btn_RoomLeave.TabIndex = 48; + btn_RoomLeave.Text = "Leave"; + btn_RoomLeave.UseVisualStyleBackColor = true; + btn_RoomLeave.Click += btn_RoomLeave_Click; + // + // btn_RoomEnter + // + btn_RoomEnter.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 129); + btn_RoomEnter.Location = new System.Drawing.Point(144, 23); + btn_RoomEnter.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + btn_RoomEnter.Name = "btn_RoomEnter"; + btn_RoomEnter.Size = new System.Drawing.Size(66, 32); + btn_RoomEnter.TabIndex = 47; + btn_RoomEnter.Text = "Enter"; + btn_RoomEnter.UseVisualStyleBackColor = true; + btn_RoomEnter.Click += btn_RoomEnter_Click; + // + // textBoxRoomNumber + // + textBoxRoomNumber.Location = new System.Drawing.Point(98, 25); + textBoxRoomNumber.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + textBoxRoomNumber.MaxLength = 6; + textBoxRoomNumber.Name = "textBoxRoomNumber"; + textBoxRoomNumber.Size = new System.Drawing.Size(38, 23); + textBoxRoomNumber.TabIndex = 44; + textBoxRoomNumber.Text = "0"; + textBoxRoomNumber.WordWrap = false; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(5, 31); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(90, 15); + label3.TabIndex = 43; + label3.Text = "Room Number:"; + // + // mainForm + // + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(524, 751); + Controls.Add(Room); + Controls.Add(button2); + Controls.Add(textBoxUserPW); + Controls.Add(label2); + Controls.Add(textBoxUserID); + Controls.Add(label1); + Controls.Add(labelStatus); + Controls.Add(listBoxLog); + Controls.Add(button1); + Controls.Add(textSendText); + Controls.Add(btnDisconnect); + Controls.Add(btnConnect); + Controls.Add(groupBox5); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D; + Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + Name = "mainForm"; + Text = "네트워크 테스트 클라이언트"; + FormClosing += mainForm_FormClosing; + Load += mainForm_Load; + groupBox5.ResumeLayout(false); + groupBox5.PerformLayout(); + Room.ResumeLayout(false); + Room.PerformLayout(); + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private System.Windows.Forms.Button btnDisconnect; + private System.Windows.Forms.Button btnConnect; + private System.Windows.Forms.GroupBox groupBox5; + private System.Windows.Forms.TextBox textBoxPort; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.CheckBox checkBoxLocalHostIP; + private System.Windows.Forms.TextBox textBoxIP; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textSendText; + private System.Windows.Forms.Label labelStatus; + private System.Windows.Forms.ListBox listBoxLog; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBoxUserID; + private System.Windows.Forms.TextBox textBoxUserPW; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.GroupBox Room; + private System.Windows.Forms.Button btn_RoomLeave; + private System.Windows.Forms.Button btn_RoomEnter; + private System.Windows.Forms.TextBox textBoxRoomNumber; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button btnRoomChat; + private System.Windows.Forms.TextBox textBoxRoomSendMsg; + private System.Windows.Forms.ListBox listBoxRoomChatMsg; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ListBox listBoxRoomUserList; + private System.Windows.Forms.Button btnRoomRelay; + private System.Windows.Forms.TextBox textBoxRelay; + } +} + diff --git a/Tutorials/EchoClient/mainForm.cs b/Tutorials/EchoClient/mainForm.cs new file mode 100644 index 0000000..ca58439 --- /dev/null +++ b/Tutorials/EchoClient/mainForm.cs @@ -0,0 +1,376 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Runtime.Versioning; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace csharp_test_client +{ + [SupportedOSPlatform("windows10.0.177630")] + public partial class mainForm : Form + { + ClientSimpleTcp Network = new ClientSimpleTcp(); + + bool IsNetworkThreadRunning = false; + bool IsBackGroundProcessRunning = false; + + System.Threading.Thread NetworkReadThread = null; + System.Threading.Thread NetworkSendThread = null; + + PacketBufferManager PacketBuffer = new PacketBufferManager(); + Queue RecvPacketQueue = new Queue(); + Queue SendPacketQueue = new Queue(); + + System.Windows.Forms.Timer dispatcherUITimer = new(); + + + public mainForm() + { + InitializeComponent(); + } + + private void mainForm_Load(object sender, EventArgs e) + { + PacketBuffer.Init((8096 * 10), PacketDef.PACKET_HEADER_SIZE, 1024); + + IsNetworkThreadRunning = true; + NetworkReadThread = new System.Threading.Thread(this.NetworkReadProcess); + NetworkReadThread.Start(); + NetworkSendThread = new System.Threading.Thread(this.NetworkSendProcess); + NetworkSendThread.Start(); + + IsBackGroundProcessRunning = true; + /*dispatcherUITimer = new System.Windows.Threading.DispatcherTimer(); + dispatcherUITimer.Tick += new EventHandler(BackGroundProcess); + dispatcherUITimer.Interval = new TimeSpan(0, 0, 0, 0, 100); + dispatcherUITimer.Start();*/ + dispatcherUITimer.Tick += new EventHandler(BackGroundProcess); + dispatcherUITimer.Interval = 100; + dispatcherUITimer.Start(); + + btnDisconnect.Enabled = false; + + SetPacketHandler(); + DevLog.Write("프로그램 시작 !!!", LOG_LEVEL.INFO); + } + + private void mainForm_FormClosing(object sender, FormClosingEventArgs e) + { + IsNetworkThreadRunning = false; + IsBackGroundProcessRunning = false; + + Network.Close(); + } + + private void btnConnect_Click(object sender, EventArgs e) + { + string address = textBoxIP.Text; + + if (checkBoxLocalHostIP.Checked) + { + address = "127.0.0.1"; + } + + int port = Convert.ToInt32(textBoxPort.Text); + + if (Network.Connect(address, port)) + { + labelStatus.Text = string.Format("{0}. 서버에 접속 중", DateTime.Now); + btnConnect.Enabled = false; + btnDisconnect.Enabled = true; + + DevLog.Write($"서버에 접속 중", LOG_LEVEL.INFO); + } + else + { + labelStatus.Text = string.Format("{0}. 서버에 접속 실패", DateTime.Now); + } + } + + private void btnDisconnect_Click(object sender, EventArgs e) + { + SetDisconnectd(); + Network.Close(); + } + + private void button1_Click(object sender, EventArgs e) + { + if (string.IsNullOrEmpty(textSendText.Text)) + { + MessageBox.Show("보낼 텍스트를 입력하세요"); + return; + } + + var body = Encoding.UTF8.GetBytes(textSendText.Text); + + PostSendPacket(PACKET_ID.PACKET_ID_ECHO, body); + + DevLog.Write($"Echo 요청: {textSendText.Text}, {body.Length}"); + } + + + + void NetworkReadProcess() + { + const Int16 PacketHeaderSize = PacketDef.PACKET_HEADER_SIZE; + + while (IsNetworkThreadRunning) + { + if (Network.IsConnected() == false) + { + System.Threading.Thread.Sleep(1); + continue; + } + + var recvData = Network.Receive(); + + if (recvData != null) + { + PacketBuffer.Write(recvData.Item2, 0, recvData.Item1); + + while (true) + { + var data = PacketBuffer.Read(); + if (data.Count < 1) + { + break; + } + + var packet = new PacketData(); + packet.DataSize = (short)(data.Count - PacketHeaderSize); + packet.PacketID = BitConverter.ToInt16(data.Array, data.Offset + 2); + packet.Type = (SByte)data.Array[(data.Offset + 4)]; + packet.BodyData = new byte[packet.DataSize]; + Buffer.BlockCopy(data.Array, (data.Offset + PacketHeaderSize), packet.BodyData, 0, (data.Count - PacketHeaderSize)); + lock (((System.Collections.ICollection)RecvPacketQueue).SyncRoot) + { + RecvPacketQueue.Enqueue(packet); + } + } + //DevLog.Write($"받은 데이터: {recvData.Item2}", LOG_LEVEL.INFO); + } + else + { + Network.Close(); + SetDisconnectd(); + DevLog.Write("서버와 접속 종료 !!!", LOG_LEVEL.INFO); + } + } + } + + void NetworkSendProcess() + { + while (IsNetworkThreadRunning) + { + System.Threading.Thread.Sleep(1); + + if (Network.IsConnected() == false) + { + continue; + } + + lock (((System.Collections.ICollection)SendPacketQueue).SyncRoot) + { + if (SendPacketQueue.Count > 0) + { + var packet = SendPacketQueue.Dequeue(); + Network.Send(packet); + } + } + } + } + + + void BackGroundProcess(object sender, EventArgs e) + { + ProcessLog(); + + try + { + var packet = new PacketData(); + + lock (((System.Collections.ICollection)RecvPacketQueue).SyncRoot) + { + if (RecvPacketQueue.Count() > 0) + { + packet = RecvPacketQueue.Dequeue(); + } + } + + if (packet.PacketID != 0) + { + PacketProcess(packet); + } + } + catch (Exception ex) + { + MessageBox.Show(string.Format("ReadPacketQueueProcess. error:{0}", ex.Message)); + } + } + + private void ProcessLog() + { + // 너무 이 작업만 할 수 없으므로 일정 작업 이상을 하면 일단 패스한다. + int logWorkCount = 0; + + while (IsBackGroundProcessRunning) + { + System.Threading.Thread.Sleep(1); + + string msg; + + if (DevLog.GetLog(out msg)) + { + ++logWorkCount; + + if (listBoxLog.Items.Count > 512) + { + listBoxLog.Items.Clear(); + } + + listBoxLog.Items.Add(msg); + listBoxLog.SelectedIndex = listBoxLog.Items.Count - 1; + } + else + { + break; + } + + if (logWorkCount > 8) + { + break; + } + } + } + + + public void SetDisconnectd() + { + if (btnConnect.Enabled == false) + { + btnConnect.Enabled = true; + btnDisconnect.Enabled = false; + } + + SendPacketQueue.Clear(); + + listBoxRoomChatMsg.Items.Clear(); + listBoxRoomUserList.Items.Clear(); + + labelStatus.Text = "서버 접속이 끊어짐"; + } + + public void PostSendPacket(PACKET_ID packetID, byte[] bodyData) + { + if (Network.IsConnected() == false) + { + DevLog.Write("서버 연결이 되어 있지 않습니다", LOG_LEVEL.ERROR); + return; + } + + Int16 bodyDataSize = 0; + if (bodyData != null) + { + bodyDataSize = (Int16)bodyData.Length; + } + var packetSize = bodyDataSize + PacketDef.PACKET_HEADER_SIZE; + + List dataSource = new List(); + dataSource.AddRange(BitConverter.GetBytes((Int16)packetSize)); + dataSource.AddRange(BitConverter.GetBytes((Int16)packetID)); + dataSource.AddRange(new byte[] { (byte)0 }); + + if (bodyData != null) + { + dataSource.AddRange(bodyData); + } + + SendPacketQueue.Enqueue(dataSource.ToArray()); + } + + void AddRoomUserList(Int64 userUniqueId, string userID) + { + var msg = $"{userUniqueId}: {userID}"; + listBoxRoomUserList.Items.Add(msg); + } + + void RemoveRoomUserList(Int64 userUniqueId) + { + object removeItem = null; + + foreach( var user in listBoxRoomUserList.Items) + { + var items = user.ToString().Split(":"); + if( items[0].ToInt64() == userUniqueId) + { + removeItem = user; + return; + } + } + + if (removeItem != null) + { + listBoxRoomUserList.Items.Remove(removeItem); + } + } + + + // 로그인 요청 + private void button2_Click(object sender, EventArgs e) + { + var loginReq = new LoginReqPacket(); + loginReq.SetValue(textBoxUserID.Text, textBoxUserPW.Text); + + PostSendPacket(PACKET_ID.PACKET_ID_LOGIN_REQ, loginReq.ToBytes()); + DevLog.Write($"로그인 요청: {textBoxUserID.Text}, {textBoxUserPW.Text}"); + } + + private void btn_RoomEnter_Click(object sender, EventArgs e) + { + var requestPkt = new RoomEnterReqPacket(); + requestPkt.SetValue(textBoxRoomNumber.Text.ToInt32()); + + PostSendPacket(PACKET_ID.PACKET_ID_ROOM_ENTER_REQ, requestPkt.ToBytes()); + DevLog.Write($"방 입장 요청: {textBoxRoomNumber.Text} 번"); + } + + private void btn_RoomLeave_Click(object sender, EventArgs e) + { + PostSendPacket(PACKET_ID.PACKET_ID_ROOM_LEAVE_REQ, null); + DevLog.Write($"방 입장 요청: {textBoxRoomNumber.Text} 번"); + } + + private void btnRoomChat_Click(object sender, EventArgs e) + { + if(textBoxRoomSendMsg.Text.IsEmpty()) + { + MessageBox.Show("채팅 메시지를 입력하세요"); + return; + } + + var requestPkt = new RoomChatReqPacket(); + requestPkt.SetValue(textBoxRoomSendMsg.Text); + + PostSendPacket(PACKET_ID.PACKET_ID_ROOM_CHAT_REQ, requestPkt.ToBytes()); + DevLog.Write($"방 채팅 요청"); + } + + private void btnRoomRelay_Click(object sender, EventArgs e) + { + if( textBoxRelay.Text.IsEmpty()) + { + MessageBox.Show("릴레이 할 데이터가 없습니다"); + return; + } + + var bodyData = Encoding.UTF8.GetBytes(textBoxRelay.Text); + PostSendPacket(PACKET_ID.PACKET_ID_ROOM_RELAY_REQ, bodyData); + DevLog.Write($"방 릴레이 요청"); + } + } +} diff --git a/Tutorials/EchoClient/mainForm.resx b/Tutorials/EchoClient/mainForm.resx new file mode 100644 index 0000000..b92c163 --- /dev/null +++ b/Tutorials/EchoClient/mainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Tutorials/PvPGameServer_Client/Packet.cs b/Tutorials/PvPGameServer_Client/Packet.cs index 67ecc7c..1645f73 100644 --- a/Tutorials/PvPGameServer_Client/Packet.cs +++ b/Tutorials/PvPGameServer_Client/Packet.cs @@ -7,15 +7,7 @@ using System.Threading.Tasks; namespace csharp_test_client -{ - struct PacketData - { - public Int16 DataSize; - public Int16 PacketID; - public SByte Type; - public byte[] BodyData; - } - +{ public struct MsgPackPacketHeadInfo { const int PacketHeaderMsgPackStartPos = 3; diff --git a/Tutorials/PvPGameServer_Client/Program.cs b/Tutorials/PvPGameServer_Client/Program.cs index a98e647..7b9e1ff 100644 --- a/Tutorials/PvPGameServer_Client/Program.cs +++ b/Tutorials/PvPGameServer_Client/Program.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Versioning; using System.Threading.Tasks; using System.Windows.Forms; namespace csharp_test_client { + [SupportedOSPlatform("windows10.0.177630")] static class Program { /// diff --git a/Tutorials/PvPGameServer_Client/csharp_test_client.csproj b/Tutorials/PvPGameServer_Client/csharp_test_client.csproj index 7a62f55..88ff92e 100644 --- a/Tutorials/PvPGameServer_Client/csharp_test_client.csproj +++ b/Tutorials/PvPGameServer_Client/csharp_test_client.csproj @@ -1,6 +1,6 @@  - net8.0-windows + net8.0-windows10.0.17763.0 WinExe false true diff --git a/Tutorials/PvPGameServer_Client/mainForm.cs b/Tutorials/PvPGameServer_Client/mainForm.cs index 7ecdd32..d67e252 100644 --- a/Tutorials/PvPGameServer_Client/mainForm.cs +++ b/Tutorials/PvPGameServer_Client/mainForm.cs @@ -5,12 +5,15 @@ using System.Data; using System.Drawing; using System.Linq; +using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; using System.Windows.Forms; namespace csharp_test_client { + [SupportedOSPlatform("windows10.0.177630")] public partial class mainForm : Form { ClientSimpleTcp Network = new ClientSimpleTcp(); @@ -94,6 +97,7 @@ private void btnDisconnect_Click(object sender, EventArgs e) Network.Close(); } + // 에코 private void button1_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(textSendText.Text)) @@ -103,8 +107,10 @@ private void button1_Click(object sender, EventArgs e) } var body = Encoding.UTF8.GetBytes(textSendText.Text); - - PostSendPacket(PACKET_ID.PACKET_ID_ECHO, body); + var packetData = new byte[body.Length + MsgPackPacketHeadInfo.HeadSize]; + Buffer.BlockCopy(body, 0, packetData, MsgPackPacketHeadInfo.HeadSize, body.Length); + + PostSendPacket(PACKET_ID.PACKET_ID_ECHO, packetData); DevLog.Write($"Echo 요청: {textSendText.Text}, {body.Length}"); }