2015. 1. 26. 17:50
C# with TCP/IP
닷넷 환경에서 구조체를 이용한 소켓 통신을 지원하기 위한 방법은 크게 두 가지로 나뉩니다.
★ 마샬링(Marshaling)을 이용한 구조체 사용.
★ 바이너리 포매터(Binary Formatter)의 사용.
이번에는 바이너리 포매터 기법을 이용한 구조체를 통신하기 위한 방법을 소개합니다.
프로그램 설명
클라이언트가 학생의 4가지 데이타(이름, 과목, 점수, 메모)를 서버로 전송한다고 가정하고 필요한 클래스는 아래와 같습니다.
[Serializable] struct DataPacket { public string Name; public string Subject; public int Grade; public string Memo; }
실행 후
메시지 전송 후
프로그램 작성 순서
1. 소켓/쓰레드과 관련한 네임스페이스를 서버/클라이언트 모두에 포함 시킵니다.
using System.Net; using System.Net.Sockets; using System.Threading; using System.Diagnostics;
2. 서버 프로그램
static void Main(string[] args) { TcpListener server = null; try { server = new TcpListener(IPAddress.Parse("192.168.0.12"), 13000); server.Start(); byte[] buffer = new byte[256]; while(true) { Console.WriteLine("Waiting for a connection....."); TcpClient client = server.AcceptTcpClient(); Console.WriteLine("\nConnected!!"); NetworkStream stream = client.GetStream(); while(stream.Read(buffer, 0, buffer.Length) != 0) { DataPacket packet = GetBindAck(buffer); string Name = packet.Name; string Subject = packet.Subject; Int32 Grade = packet.Grade; string Memo = packet.Memo; Console.WriteLine("이 름 : {0}", Name); Console.WriteLine("과 목 : {0}", Subject); Console.WriteLine("점 수 : {0}", Grade); Console.WriteLine("메 모 : {0}", Memo); Console.WriteLine(""); Console.WriteLine("==========================================="); Console.WriteLine(""); } stream.Close(); client.Close(); } } catch(SocketException se) { Console.WriteLine("SocketException : {0}", se.Message); } catch(Exception ex) { Console.WriteLine("Exception : {0}", ex.Message); } } private static DataPacket GetBindAck(byte[] btfuffer) { DataPacket packet = new DataPacket(); MemoryStream ms = new MemoryStream(btfuffer, false); BinaryReader br = new BinaryReader(ms); packet.Name = ExtendedTrim(Encoding.UTF8.GetString(br.ReadBytes(20))); packet.Subject = ExtendedTrim(Encoding.UTF8.GetString(br.ReadBytes(20))); packet.Grade = IPAddress.NetworkToHostOrder(br.ReadInt32()); packet.Memo = ExtendedTrim(Encoding.UTF8.GetString(br.ReadBytes(100))); br.Close(); ms.Close(); return packet; } // 문자열 뒤쪽에 위치한 null 을 제거한 후에 공백문자를 제거한다. private static string ExtendedTrim(string source) { string dest = source; int index = dest.IndexOf('\0'); if (index > -1) { dest = source.Substring(0, index + 1); } return dest.TrimEnd('\0').Trim(); }
3. 클라이언트 프로그램
static void Main(string[] args) { try { string Name = string.Empty; string Subject = string.Empty; Int32 Grade = 0; string Memo = string.Empty; do { // 1. 데이타 입력 Console.Write("이름 : "); Name = Console.ReadLine(); Console.Write("과목 : "); Subject = Console.ReadLine(); Console.Write("점수 : "); string tmpGrage = Console.ReadLine(); if (tmpGrage != "") { int outGrade = 0; if (Int32.TryParse(tmpGrage, out outGrade)) Grade = Convert.ToInt32(tmpGrage); else Grade = 0; } else Grade = 0; Console.Write("메모 : "); Memo = Console.ReadLine(); if (string.IsNullOrEmpty(Name) || string.IsNullOrEmpty(Subject)) break; // 2. 구조체 데이타를 바이트 배열로 변환 DataPacket packet = new DataPacket(); packet.Name = Name; packet.Subject = Subject; packet.Grade = Grade; packet.Memo = Memo; byte[] buffer = GetBytes_Bind(packet); // 3. 데이타 전송 TcpClient client = new TcpClient(); client.Connect("192.168.0.12", 13000); Console.WriteLine("Connected..."); NetworkStream stream = client.GetStream(); stream.Write(buffer, 0, buffer.Length); Console.WriteLine("{0} data sent\n", buffer.Length); stream.Close(); client.Close(); } while (Name != "" && Subject != ""); } catch (SocketException se) { Console.WriteLine("SocketException : {0} ", se.Message.ToString()); } catch (Exception ex) { Console.WriteLine("Exception : {0} ", ex.Message.ToString()); } Console.WriteLine("press the ENTER to continue..."); Console.ReadLine(); } // 패킷 사이즈 public const int BODY_BIND_SIZE = 20 + 20 + 4 + 100; // 인증 패킷 구조체를 바이트 배열로 변환하는 함수 public static byte[] GetBytes_Bind(DataPacket packet) { byte[] btBuffer = new byte[BODY_BIND_SIZE]; MemoryStream ms = new MemoryStream(btBuffer, true); BinaryWriter bw = new BinaryWriter(ms); // Name - string try { byte[] btName = new byte[20]; Encoding.UTF8.GetBytes(packet.Name, 0, packet.Name.Length, btName, 0); bw.Write(btName); } catch(Exception ex) { Console.WriteLine("Error : {0}", ex.Message.ToString()); } // Subject - string try { byte[] btName = new byte[20]; Encoding.UTF8.GetBytes(packet.Subject, 0, packet.Subject.Length, btName, 0); bw.Write(btName); } catch (Exception ex) { Console.WriteLine("Error : {0}", ex.Message.ToString()); } // Grade - long bw.Write(IPAddress.HostToNetworkOrder(packet.Grade)); // Memo - string try { byte[] btName = new byte[100]; Encoding.UTF8.GetBytes(packet.Memo, 0, packet.Memo.Length, btName, 0); bw.Write(btName); } catch (Exception ex) { Console.WriteLine("Error : {0}", ex.Message.ToString()); } bw.Close(); ms.Close(); return btBuffer; }
'C# with TCP/IP' 카테고리의 다른 글
[Program C#]Binary Serialization 을 이용한 소켓통신 - 콘솔버전 (0) | 2015.01.28 |
---|---|
[Program C#]구조체를 이용한 소켓통신3 - 바이너리 포매터 방법 - 윈도우 버전 (0) | 2015.01.27 |
[Program C#]구조체를 이용한 소켓통신2 - 마샬링 방법 - 윈도우 버전 (0) | 2015.01.22 |
[Program C#]구조체를 이용한 소켓통신2 - 마샬링 방법 - 콘솔 버전 (0) | 2015.01.21 |
[Program C#]구조체를 이용한 소켓통신1 - 마샬링 방법 - 윈도우 버전 (0) | 2015.01.20 |