Server와 Client간의 TCP 패킷 통신
Server는 IP와 Port를 이용해 서버를 개설하고 클라이언트의 접속을 대기한다.
패킷은 Header,Body,Footer로 구성
클라이언트와 접속이 성공하면 클라이언트가 보낸 패킷의 header를 체크한다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace TCP_Transmission.Model
{
public class MessageModel
{
private byte[] packet;
private byte[] syncPatter;
private byte[] type;
private byte[] length;
private byte[] footer;
public byte[] Packet { get => packet; set => packet = value; }
//Header
public byte[] SyncPattern { get => syncPatter; set => syncPatter = value; }
public byte[] Type { get => type; set => type = value; }
public byte[] Length { get => length; set => length = value; }
//Body
public string Content { get; set; }
public string Time { get; set; }
public string IP { get; set; }
//Footer
public byte[] Footer { get => footer; set => footer = value; }
public MessageModel()
{
// Byte Header 생성
packet = new byte[8];
syncPatter = new byte[4];
type = new byte[2];
length = new byte[2];
}
/// <summary>
/// 패킷화
/// </summary>
/// <returns></returns>
public byte[] GeneratePacket()
{
byte[] header = syncPatter.Concat(Type).Concat(Length).ToArray();
byte[] body = Encoding.UTF8.GetBytes(Content);
footer = new byte[header.Length];
footer = Enumerable.Reverse(header).ToArray();
Packet = header.Concat(body).Concat(footer).ToArray();
return Packet;
}
public bool CompareHeader(NetworkStream network, byte[] header, uint syncNum)
{
uint sync;
byte[] syncArr = new byte[4];
syncArr = header.Take(4).ToArray();
sync = BitConverter.ToUInt32(syncArr, 0);
if (sync == syncNum)
{
//Type
if (header[5] == 0)
Content = "정상: ";
else
Content = "오류: ";
//Length
int length = header[7];
//Content
byte[] contentArr = new byte[length];
network.Read(contentArr, 0, length);
//Footer
byte[] footerArr = new byte[8];
network.Read(footerArr, 0, 8);
Content += Encoding.UTF8.GetString(contentArr);
return true;
}
else
{
MessageBox.Show("SyncPattern이 일치하지않음..");
return false;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using TCP_Transmission.Model;
using WPF_Default;
//추가
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Windows.Controls;
using System.ComponentModel;
using System.Windows.Forms;
//다른이름저장처럼 경로 지정 -> 완료
//인코딩 방식 바꾸기 -> 완료
//Config -> 완료
//ByteConverter
namespace TCP_Transmission.ViewModel
{
class ServerViewModel : Notifier
{
NetworkStream networkStream;
private string content;
private string ip;
private string port;
private string startText;
private MessageModel selectedItem;
private int exportFileNum = 0;
private uint syncNum = 1234567890;
private byte[] originSync;
private bool isCheck = false;
private ObservableCollection<MessageModel> messageCollection;
public ICommand startCommand { get; set; }
public ICommand sendCommand { get; set; }
public ICommand deleteContextCommand { get; set; }
public ICommand exportContextCommand { get; set; }
public ICommand systemExitCommand { get; set; }
public ICommand openMessageCommand { get; set; }
public ServerViewModel()
{
//Command
startCommand = new Command(StartMethod, canexecuteMethod);
sendCommand = new Command(SendMethod, canexecuteMethod);
deleteContextCommand = new Command(DeleteContextMenu, canexecuteMethod);
exportContextCommand = new Command(ExportContextMenu, canexecuteMethod);
openMessageCommand = new Command(OpenMessageCommand, canexecuteMethod);
systemExitCommand = new Command(SystemExit, canexecuteMethod);
messageCollection = new ObservableCollection<MessageModel>();
Port = "3000";
StartText = "Start";
//originSync = BitConverter.GetBytes(syncNum);
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress a in host.AddressList)
{
if (a.AddressFamily == AddressFamily.InterNetwork)
{
ip = a.ToString();
GenerateMessage("Local IP주소 획득..." + ip);
}
}
}
private void SystemExit(object obj)
{
Environment.Exit(0);
}
/// <summary>
/// 이전대화 불러오기 CSV
/// </summary>
/// <param name="obj"></param>
private void OpenMessageCommand(object obj)
{
string path;
string saveLine = "";
OpenFileDialog openBrowserDialog = new OpenFileDialog();
openBrowserDialog.Filter = "대화 (*.csv)|*.csv";
if (openBrowserDialog.ShowDialog() == DialogResult.OK)
{
path = openBrowserDialog.FileName;
StreamReader streamReader = new StreamReader(path, Encoding.GetEncoding("ks_c_5601-1987"));
while (saveLine != null)
{
saveLine = streamReader.ReadLine();
if (saveLine == null)
break;
GenerateMessage(saveLine, streamReader.ReadLine(), streamReader.ReadLine());
Console.WriteLine("데이터 읽어오는중...");
}
}
}
private bool canexecuteMethod(object arg)
{
return true;
}
#region Getter & Setter
public string Content
{
get { return content; }
set { content = value; OnPropertyChanged("Content"); }
}
public ObservableCollection<MessageModel> MessageCollection
{
get { return messageCollection; }
set { messageCollection = value; }
}
public string IP
{
get { return ip; }
set { ip = value; OnPropertyChanged("IP"); }
}
public string Port
{
get { return port; }
set { port = value; OnPropertyChanged("Port"); }
}
public string StartText
{
get { return startText; }
set { startText = value; OnPropertyChanged("StartText"); }
}
public MessageModel SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; OnPropertyChanged("SelectedItemNum"); Console.WriteLine(selectedItem); }
}
public bool IsCheck
{
get { return isCheck; }
set { isCheck = value; OnPropertyChanged("IsCheck"); }
}
#endregion
#region ButtonMethode
public void StartMethod(object obj)
{
Thread thread = new Thread(Connect);
thread.IsBackground = true;
thread.Start();
//MessageCollection.Add(new Message() { Content = "서버 실행...", IP = this.IP, Time = DateTime.Now.ToString("dddd hh:mm") });
GenerateMessage("서버 실행...");
if (StartText == "Start")
StartText = "Stop";
else
StartText = "Start";
}
public void SendMethod(object obj)
{
byte[] header = new byte[8];
byte[] sync = new byte[4];
byte[] type = new byte[2];
byte[] length = new byte[2];
sync = BitConverter.GetBytes((uint)syncNum);
//Type
if (IsCheck)
type[1] = 1;
else
type[1] = 0;
//Length
length[1] = (byte)Content.Length;
MessageModel messageModel = new MessageModel() { SyncPattern = sync, Type = type, Length = length, Content = this.Content, IP = this.IP, Time = DateTime.Now.ToString("F") };
MessageCollection.Add(messageModel);
messageModel.GeneratePacket();
//패킷 전송...
//if (BitConverter.IsLittleEndian)
// Array.Reverse(messageModel.Packet);
//binaryWriter.Write(messageModel.Packet);
networkStream.Write(messageModel.Packet, 0, messageModel.Packet.Length);
Console.WriteLine(messageModel.Packet);
}
public void DeleteContextMenu(object obj)
{
Console.WriteLine("DeleteContextMenu Clicked");
messageCollection.Remove(selectedItem);
}
/// <summary>
/// Message 내보내기 CSV
/// </summary>
/// <param name="obj"></param>
public void ExportContextMenu(object obj)
{
string path = "";
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
path = folderBrowserDialog.SelectedPath;
StreamWriter file = new StreamWriter(path + "/ExportData" + exportFileNum + ".csv", true, Encoding.GetEncoding("euc-kr"));
Console.WriteLine(path);
file.WriteLine(selectedItem.Time);
file.WriteLine(selectedItem.IP);
file.WriteLine(selectedItem.Content);
file.Flush();
file.Close();
}
}
#endregion
/// <summary>
/// TCP 연결
/// </summary>
private void Connect()
{
TcpListener listener = new TcpListener(IPAddress.Parse(ip), int.Parse(port));
listener.Start();
//요청 수락
TcpClient client = listener.AcceptTcpClient();
App.Current.Dispatcher.Invoke(() =>
{
GenerateMessage("연결성공");
});
networkStream = client.GetStream();
while (client.Connected)
{
Console.WriteLine("메시지");
byte[] headerArr = new byte[8];
networkStream.Read(headerArr, 0, 8);
MessageModel messageModel = new MessageModel();
//head
if (messageModel.CompareHeader(networkStream, headerArr, syncNum))
GenerateMessage(messageModel.Content);
}
client.Close();
networkStream.Close();
}
/// <summary>
/// 메시지 생성
/// </summary>
/// <param name="text"></param>
public void GenerateMessage(string text)
{
Console.WriteLine(text);
App.Current.Dispatcher.Invoke(() =>
{
MessageCollection.Add(new MessageModel() { Content = text, IP = this.IP, Time = DateTime.Now.ToString("F") });
});
}
public void GenerateMessage(string date, string ip, string text)
{
MessageCollection.Add(new MessageModel() { Content = text, IP = ip, Time = date });
}
}
}
<Window x:Class="TCP_Transmission.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TCP_Transmission.ViewModel"
WindowStyle="None"
MouseDown="Window_MouseDown"
mc:Ignorable="d"
Title="Server" Height="500" Width="550"
Background="#FF002970">
<Window.DataContext>
<local:ServerViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="42"/>
<RowDefinition Height="380*"/>
<RowDefinition Height="47*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!--ToolBar-->
<Grid Grid.Column="0" Background="#FF06003E">
<TextBlock Text="Server" FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center" FontFamily="Lucida Bright" FontWeight="Bold" Foreground="Gold" />
</Grid>
<Grid Grid.Column="1" Grid.ColumnSpan="4" Background="#FF06003E">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
<Button Content="-" Width="20" Height="20" Click="Button_Click" Background="Gold" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,10,0" />
<Button Content="X" Width="20" Height="20" Click="Button_Click" Background="Gold" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,10,0" Command="{Binding systemExitCommand}" />
</StackPanel>
</Grid>
<!--TOP-->
<Grid Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" >
<TextBlock Foreground="Gold" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" ><Run FontFamily="Lucida Bright" Text="IP"/></TextBlock>
</Grid>
<Grid Grid.Column="1" Grid.Row="1">
<TextBox Text="{Binding IP}" IsReadOnly="True" Height="20" VerticalAlignment="Center" Background="#FF4F6B9B"/>
</Grid>
<Grid Grid.Column="2" Grid.Row="1">
<TextBlock Foreground="Gold" FontSize="20" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" ><Run FontFamily="Lucida Bright" Text="Port"/></TextBlock>
</Grid>
<Grid Grid.Column="3" Grid.Row="1">
<TextBox Text="{Binding Port}" Height="20" Background="#FF4F6B9B"/>
</Grid>
<Grid Grid.Column="4" Grid.Row="1">
<Button Command="{Binding startCommand}" Background="AliceBlue" Content="{Binding StartText}" HorizontalAlignment="Center" Height="20" Width="40"/>
</Grid>
<!--Content-->
<Grid Grid.Row=" 2" Grid.ColumnSpan="5">
<DataGrid SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding MessageCollection}" AutoGenerateColumns="False" CanUserAddRows="False" Margin="10" Background="#FFA19FAE" >
<DataGrid.Columns>
<DataGridTextColumn Header="Time" Binding="{Binding Time}" IsReadOnly="True"/>
<DataGridTextColumn Header="Client" Binding="{Binding IP}" IsReadOnly="True"/>
<DataGridTextColumn Header="Message" Binding="{Binding Content}" IsReadOnly="True" Width="*" />
</DataGrid.Columns>
<DataGrid.ContextMenu >
<ContextMenu Name="GridContext" >
<MenuItem Header="Delete" Command="{Binding deleteContextCommand}"/>
<MenuItem Header="Export" Command="{Binding exportContextCommand}"/>
<MenuItem Header="Import" Command="{Binding openMessageCommand}"/>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
<!--<Grid Grid.Row="2" Grid.Column="5" VerticalAlignment="Bottom" >
<Button Command="{Binding openMessageCommand}" Content="이전 대화 불러오기" FontSize="10" Height="20" Margin="-22,0,0,0" />
</Grid>-->
<!--Bottom-->
<Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4">
<TextBox Text="{Binding Content}" HorizontalAlignment="Stretch" Height="30" Margin="10" Background="#FF4F6B9B"/>
</Grid>
<Grid Grid.Row="3" Grid.Column="4">
<StackPanel Orientation="Horizontal" Margin="-10,0,0,0">
<CheckBox Foreground="Red" IsChecked="{Binding IsCheck}" Content="Error" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Lucida Bright" Margin="0,0,5,0" />
<Button Command="{Binding sendCommand}" Content="Send" Height="30" />
</StackPanel>
</Grid>
</Grid>
</Window>