프로그램 설명
이번 예제는 Binary Serialization / Deserialization 을 이용한 통신 방법을 설명합니다.
클래스에 property 로 5개 필드(Name, Subject, Grade, Memo, SendTime)를 정의 합니다. SendTime 필드는 전송하는 클라이언트의 전송 시각을 기입하고, 서버에서 메시지를 받아서 네트워크 지연 시간을 확인 해 보기 위한 용도로 추가 했습니다. 또한 탭컨트롤을 추가해서 [Log], [Message] 화면을 구분했습니다.
3개의 프로젝트가 필요합니다.
1. 공통 라이브러리
==> 클라이언트와 서버에서 사용할 클래스
==> 클래스 이름 위에 [Serializable] 특성을 기입하는 게 중요함.
==> DataPacket.cs
2. 클라이언트 프로그램
3. 서버 프로그램
실행 후
메시지 전송 후
프로그램 작성 순서
1. 공통 라이브러리 (Common Libray)
클라이언트와 서버에서 사용할 어셈블리 코드
[Serializable] public class DataPacket { public string Name { get; set; } public string Subject { get; set; } public Int32 Grade { get; set; } public string Memo { get; set; } public DateTime SendTime { get; set; } }
2. 서버 프로그램
public partial class MainForm : Form { TcpListener server = null; public MainForm() { InitializeComponent(); FormClosing += new FormClosingEventHandler(WindowsFormClosing); InitStart(); // 탭 페이지 [message] 탭 활성화 tabControl1.SelectTab("tabPage2"); } private void WindowsFormClosing(object sender, FormClosingEventArgs s) { if (server != null) server = null; Application.Exit(); } private void InitStart() { Thread socketworker = new Thread(new ThreadStart(socketThread)); socketworker.IsBackground = true; socketworker.Start(); } private void socketThread() { try { server = new TcpListener(IPAddress.Parse("192.168.0.12"), 13000); server.Start(); updateStatusInfo(string.Format("[{0}] server start", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"))); while (true) { TcpClient client = server.AcceptTcpClient(); updateStatusInfo(string.Format("[{0}] new connected", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"))); Thread clientworker = new Thread(new ParameterizedThreadStart(clientThread)); clientworker.IsBackground = true; clientworker.Start(client); } } catch (SocketException se) { Debug.WriteLine("SocketException : {0}", se.Message); } catch (Exception ex) { Debug.WriteLine("Exception : {0}", ex.Message); } } private void clientThread(object sender) { string Name = string.Empty; string Subject = string.Empty; Int32 Grade = 0; string Memo = string.Empty; DateTime SendTime; TimeSpan time = TimeSpan.Zero; // 1. 데이타 받기 TcpClient client = sender as TcpClient; NetworkStream stream = client.GetStream(); byte[] buffer = new byte[8092]; if (client.GetStream().CanRead) { IFormatter formatter = new BinaryFormatter(); DataPacket packet = new DataPacket(); packet = (DataPacket)formatter.Deserialize(stream); Name = packet.Name; Subject = packet.Subject; Grade = packet.Grade; Memo = packet.Memo; SendTime = packet.SendTime; time = DateTime.Now - SendTime; updateStatusInfo(string.Format("[{0}] data received", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"))); } stream.Close(); client.Close(); updateStatusInfo(string.Format("[{0}] disconnted", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"))); // 2. 데이타 표시하기 Invoke((MethodInvoker)delegate { int count = listView1.Items.Count; count++; ListViewItem i = new ListViewItem(); i.Text = count.ToString(); i.SubItems.Add(Name); i.SubItems.Add(Subject); i.SubItems.Add(Grade.ToString()); i.SubItems.Add(Memo); i.SubItems.Add(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); i.SubItems.Add(string.Format("{0}.{1} seconds", time.Seconds.ToString(), time.Milliseconds.ToString())); listView1.Items.Add(i); listView1.Items[this.listView1.Items.Count - 1].EnsureVisible(); }); Debug.WriteLine("{0} : {1} : {2} : {3} : {4} : {5}", Name, Subject, Grade, Memo, time.Seconds.ToString(), time.Milliseconds.ToString()); } private void updateStatusInfo(string content) { Action del = delegate() { listBox1.Items.Add(content); }; Invoke(del); } private void btnDataClear_Click(object sender, EventArgs e) { if (MessageBox.Show("데이타를 모두 삭제 하시겠습니까?", "질문", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.OK) { listBox1.Items.Clear(); listView1.Items.Clear(); } } private void btnClose_Click(object sender, EventArgs e) { updateStatusInfo(string.Format("[{0}] server stop", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"))); Application.Exit(); } }
2. 클라이언트 프로그램
public MainForm() { InitializeComponent(); // 마우스 in / out 이벤트 추가 btnSend.MouseEnter += new EventHandler(btnSend_MouseEnter); btnSend.MouseLeave += new EventHandler(btnSend_MouseLeave); // 탭 페이지 [message] 탭 활성화 tabControl1.SelectTab("tabPage2"); } void btnSend_MouseEnter(object sender, EventArgs e) { btnSend.UseVisualStyleBackColor = false; btnSend.BackColor = Color.FromArgb(255, 255, 165, 00); // 배경색을 오렌지 색으로 변경함... btnSend.ForeColor = Color.White; // 글자색을 흰색으로 변경함 } void btnSend_MouseLeave(object sender, EventArgs e) { btnSend.UseVisualStyleBackColor = true; btnSend.BackColor = SystemColors.Control; // 배경색을 시스템 기본색으로 변경함... btnSend.ForeColor = SystemColors.ControlText; // 글자색을 시스템 기본색으로 변경함... } private void btnSend_Click(object sender, EventArgs e) { Thread socketworker = new Thread(new ThreadStart(socketThread)); socketworker.IsBackground = true; socketworker.Start(); } private void socketThread() { // 1. 데이타패킷 조합 Debug.WriteLine("{0} : {1} : {2} : {3}", txtName.Text, txtSubject.Text, txtGrade.Text, txtMemo.Text); DataPacket packet = new DataPacket(); packet.Name = txtName.Text; packet.Subject = txtSubject.Text; Int32 outNum; if (Int32.TryParse(txtGrade.Text, out outNum)) { packet.Grade = Convert.ToInt32(txtGrade.Text); } else { packet.Grade = 0; } packet.Memo = txtMemo.Text; if (string.IsNullOrEmpty(packet.Name)) { MessageBox.Show("[이 름]을 입력하시기 바랍니다", "경고", MessageBoxButtons.OK, MessageBoxIcon.Error); Invoke((MethodInvoker)delegate { txtName.Focus(); }); return; } if (string.IsNullOrEmpty(packet.Subject)) { MessageBox.Show("[과 목]을 입력하시기 바랍니다", "경고", MessageBoxButtons.OK, MessageBoxIcon.Error); Invoke((MethodInvoker)delegate { txtSubject.Focus(); }); return; } if (packet.Grade == 0) { MessageBox.Show("[점 수]을 입력하시기 바랍니다", "경고", MessageBoxButtons.OK, MessageBoxIcon.Error); Invoke((MethodInvoker)delegate { txtGrade.Focus(); }); return; } if (string.IsNullOrEmpty(packet.Memo)) { packet.Memo = " "; } packet.SendTime = DateTime.Now; Debug.WriteLine("{0} : {1} : {2} : {3} : {4}", packet.Name, packet.Subject, packet.Grade, packet.Memo, packet.SendTime.ToString("yyyy-MM-dd HH:mm:ss.fff")); // 2. TcpClient 생성 및 설정 TcpClient client = new TcpClient(txtServerIP.Text, Convert.ToInt32(txtServerPort.Text)); NetworkStream stream = client.GetStream(); updateStatusInfo(string.Format("[{0}] Connected", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"))); // 3. 전송하기 IFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, packet); updateStatusInfo(string.Format("[{0}] {1} bytes data sent", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), SizeOf(packet))); // 4. 스트림과 소켓 닫기 stream.Close(); client.Close(); updateStatusInfo(string.Format("[{0}] Disconnected", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"))); // 5. listview 에 추가하기 Invoke((MethodInvoker)delegate { int count = listView1.Items.Count; count++; ListViewItem i = new ListViewItem(); i.Text = count.ToString(); i.SubItems.Add(txtName.Text); i.SubItems.Add(txtSubject.Text); i.SubItems.Add(txtGrade.Text); i.SubItems.Add(txtMemo.Text); i.SubItems.Add(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); listView1.Items.Add(i); listView1.Items[this.listView1.Items.Count - 1].EnsureVisible(); }); } private void updateStatusInfo(string content) { Action del = delegate() { listBox1.Items.Add(content); }; Invoke(del); } public static long SizeOf(object obj) { long size = 0; try { System.IO.MemoryStream stream = new System.IO.MemoryStream(); BinaryFormatter objFormatter = new BinaryFormatter(); objFormatter.Serialize(stream, obj); size = stream.Length; } catch (Exception ex) { Console.WriteLine("Exception : {0}", ex.Message); } return size; }
'C# with TCP/IP' 카테고리의 다른 글
[Program C#] 라이브러리를 이용한 클라이언트-서버 소켓 (0) | 2015.02.25 |
---|---|
[Program C#]Client-Server Broadcast 예제 - 콘솔버전 (0) | 2015.02.09 |
[Program C#]Binary Serialization 을 이용한 소켓통신 - 콘솔버전 (0) | 2015.01.28 |
[Program C#]구조체를 이용한 소켓통신3 - 바이너리 포매터 방법 - 윈도우 버전 (0) | 2015.01.27 |
[Program C#]구조체를 이용한 소켓통신3 - 바이너리 포매터 방법 - 콘솔 버전 (0) | 2015.01.26 |