프로그램 설명
이번 예제는 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;
}